drnic/rubigen

Generator framework for your own Ruby framework

Ruby

74

109 commits

updated Dec 21, 2015

See the code

README

= RubiGen - Ruby Generator Framework

* http://drnic.github.com/rubigen

== DESCRIPTION:

A framework to allow Ruby applications to generate file/folder stubs 
(like the `rails` command does for Ruby on Rails, and the 'script/generate'
command within a Rails application during development).

== Background

RubiGen is originally extracted from Ruby on Rails (specifically the rails_generator
from its railties gem).

The rails_generator was hardcoded with Rails-specific dependencies (`RAILS_ROOT`), 
Rails generators ('app' = Rails application; 'model' = Rails model+tests+migration),
and generally assumed it was the only generator framework within the Ruby world (it was).
So, any RubyGem whose name ended with '_generator' was assumed to be a generator for
a Rails application.

But if you are developing an Adhearsion application, then you may want a different set of generators.
If you are developing a RubyGem, then you will want a different set of generators.

RubiGen exists to give different development environments their own generator framework.

=== Thanks go to...

Jeremy Kemper wrote the original Rails Generator, which is 95% of the basis of RubiGen. He's awesome.

== Installation

RubiGen is only required at development time, and normally isn't required at deployment time
(unless your application uses it to generate files at runtime).

On your development machine:

    sudo gem install rubigen
    
== Usage

RubiGen comes with a stand-alone executable to allow you to invoke generators:

For example, to run the rails' `model` generator:

    rubigen rails model Person name:string

would replace the normal

    script/generate model Person name:string

RubiGen has been traditionally integrated into another project, such as `rails`, `newgem` or `camping`,
rather than be used on its own.

These frameworks might use RubiGen for two reasons:
  
1. To generate an initial stub for developers, e.g. `rails` generated a stub to write a Rails application.
   `newgem` generates a stub to write a RubyGem.
   BTW - RubiGen has a builtin application `ruby_app` which generates a bare-bones Ruby application
   stub (lib, test, and script folders, plus a Rakefile, and a script/generate script)
2. To generate components within their development areas, e.g. Rails had its `script/generate`
   script within each Rails application, which hooked back into the rails_generator to lookup
   and execute generators.

So, there are two steps to integrating RubiGen into your framework:

1. Use it to generate an initial stub for the developers of your framework. This would create the folders 
   (`lib/app`, `test`, `script`, `doc`, `log`, etc) and starting files (`Rakefile`, 
   `README.txt`, `test/test_helper.rb` etc). Importantly, it would generate a `script/generate` file.
   The `script/generate` file (example below) will allow developers of your framework to 
   generate components/extensions within the framework.
   RubiGen allows you to restrict which generators are available. For example, within
   RubyGem development environment (as generated by `newgem`), the `script/generator`
   only shows `rubygem`-related generators. Rails could restrict `script/generator`
   to only show Rails related generators
2. Your framework RubyGem (e.g. `newgem` or `rails`) needs to add `rubigen` as a 
   dependency, so that users of your RubyGem can access the generator framework.
   
= Creating generators

There are two types of generators:

1. Application Generators - used by developers of your framework to get started. 
   Generally, you will create one Application Generator for your framework.
   It generates a base stub (such as the `rails` stub for new Rails applications)
   for your framework users.
2. Component Generators - used by developers to extend their application.
   You may include 1+ built-in generators with your framework.
   Developers can also write generators for your framework, and like Rails' generator
   install them in various places and have access to their via RubiGen.
   
== Creating an Application Generator for your Framework

Without RubiGen, to give your users a head start and create a stub for them, you will 
copiously use `mkdir_p` and `File.open`. Your script will either be primitive (only
create the bare folders and very few files) or it will be very long and unreadable
(ok, perhaps I'm just talking about the `newgem` script, which I am dubiously responsible
for... :P).

With RubiGen, you can create stubs using powerful, yet simple, syntax. Templates are
extracted into a `templates` folder, and activating the generator from a script requires
only a few lines of code.

These are the `newgem` files related to its Application Generator.

    bin/
      bin/newgem        # Application Generator script; Usage: newgem gemname [options]
    app_generators/
      app_generators/newgem/
        app_generators/newgem/newgem_generator.rb
        app_generators/newgem/USAGE
        app_generators/newgem/templates/
          app_generators/newgem/templates/app.rb
          app_generators/newgem/templates/History.txt
          app_generators/newgem/templates/... lots and lots of templates

The `bin/newgem` script is very simple, and looks like:

    require 'rubygems'
    require 'rubigen'

    if %w(-v --version).include? ARGV.first
      require 'newgem/version'
      puts "#{File.basename($0)} #{Newgem::VERSION}"
      exit(0)
    end

    require 'rubigen/scripts/generate'
    RubiGen::Base.use_application_sources!
    RubiGen::Scripts::Generate.new.run(ARGV, :generator => 'newgem')

You can copy and paste this for your own generator script, and place it in your RubyGem's `bin` folder.
Change `newgem` to your RubyGem's name in the script above (and in all the folders listed above too)

NOTE: If you leave `newgem` there, then it will execute the `newgem_generator.rb` generator; 
as the generators are loaded from all RubyGem's having `/app_generators` folders.

So, for your RubyGem, you need to keep the `/app_generators` folder (as you are creating an 
Application Generator, not a Component Generator), but change `newgem` to `your gem name` in
all the subfolders and files. ESPECIALLY `newgem_generator.rb` -> `yourgem_generator.rb`,
as this is how the generator is discovered (via `RubiGen::Base.use_application_sources!`).

All the generator work is performed within `yourgem_generator.rb`. A stub for it will be:

    require 'rbconfig'

    class YourgemGenerator < RubiGen::Base
      DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
                                  Config::CONFIG['ruby_install_name'])

      default_options   :shebang => DEFAULT_SHEBANG,
                        :an_option => 'some_default'
  
      attr_reader :app_name, :module_name
  
      def initialize(runtime_args, runtime_options = {})
        super
        usage if args.empty?
        @destination_root = args.shift
        @app_name     = File.basename(File.expand_path(@destination_root))
        @module_name  = app_name.camelize
        extract_options
      end

      def manifest
        # Use /usr/bin/env if no special shebang was specified
        script_options     = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] }
        windows            = (RUBY_PLATFORM =~ /dos|win32|cygwin/i) || (RUBY_PLATFORM =~ /(:?mswin|mingw)/)

        record do |m|
          # Root directory and all subdirectories.
          m.directory ''
          BASEDIRS.each { |path| m.directory path }
      
          # Root
          m.template_copy_each %w( Rakefile )
          m.file_copy_each     %w( README.txt )

          # Test helper
          m.template   "test_helper.rb",        "test/test_helper.rb"

          # Scripts
          %w( generate ).each do |file|
            m.template "script/#{file}",        "script/#{file}", script_options
            m.template "script/win_script.cmd", "script/#{file}.cmd", 
              :assigns => { :filename => file } if windows
          end
       
        end
      end

      protected
        def banner
          <<-EOS
    Create a stub for #{File.basename $0} to get started.

    Usage: #{File.basename $0} /path/to/your/app [options]"
    EOS
        end

        def add_options!(opts)
          opts.separator ''
          opts.separator "#{File.basename $0} options:"
          opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
        end
    
      # Installation skeleton.  Intermediate directories are automatically
      # created so don't sweat their absence here.
      BASEDIRS = %w(
        doc
        lib
        log
        script
        test
        tmp
      )
    end

Easy peasy.

== Creating a Component Generator for your Framework

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

drnic

90 commits

bklang

11 commits

benlangfeld

2 commits

jarib

2 commits

drnic/rubigen

Generator framework for your own Ruby framework

Ruby

74

109 commits

updated Dec 21, 2015

See the code

README

= RubiGen - Ruby Generator Framework

* http://drnic.github.com/rubigen

== DESCRIPTION:

A framework to allow Ruby applications to generate file/folder stubs 
(like the `rails` command does for Ruby on Rails, and the 'script/generate'
command within a Rails application during development).

== Background

RubiGen is originally extracted from Ruby on Rails (specifically the rails_generator
from its railties gem).

The rails_generator was hardcoded with Rails-specific dependencies (`RAILS_ROOT`), 
Rails generators ('app' = Rails application; 'model' = Rails model+tests+migration),
and generally assumed it was the only generator framework within the Ruby world (it was).
So, any RubyGem whose name ended with '_generator' was assumed to be a generator for
a Rails application.

But if you are developing an Adhearsion application, then you may want a different set of generators.
If you are developing a RubyGem, then you will want a different set of generators.

RubiGen exists to give different development environments their own generator framework.

=== Thanks go to...

Jeremy Kemper wrote the original Rails Generator, which is 95% of the basis of RubiGen. He's awesome.

== Installation

RubiGen is only required at development time, and normally isn't required at deployment time
(unless your application uses it to generate files at runtime).

On your development machine:

    sudo gem install rubigen
    
== Usage

RubiGen comes with a stand-alone executable to allow you to invoke generators:

For example, to run the rails' `model` generator:

    rubigen rails model Person name:string

would replace the normal

    script/generate model Person name:string

RubiGen has been traditionally integrated into another project, such as `rails`, `newgem` or `camping`,
rather than be used on its own.

These frameworks might use RubiGen for two reasons:
  
1. To generate an initial stub for developers, e.g. `rails` generated a stub to write a Rails application.
   `newgem` generates a stub to write a RubyGem.
   BTW - RubiGen has a builtin application `ruby_app` which generates a bare-bones Ruby application
   stub (lib, test, and script folders, plus a Rakefile, and a script/generate script)
2. To generate components within their development areas, e.g. Rails had its `script/generate`
   script within each Rails application, which hooked back into the rails_generator to lookup
   and execute generators.

So, there are two steps to integrating RubiGen into your framework:

1. Use it to generate an initial stub for the developers of your framework. This would create the folders 
   (`lib/app`, `test`, `script`, `doc`, `log`, etc) and starting files (`Rakefile`, 
   `README.txt`, `test/test_helper.rb` etc). Importantly, it would generate a `script/generate` file.
   The `script/generate` file (example below) will allow developers of your framework to 
   generate components/extensions within the framework.
   RubiGen allows you to restrict which generators are available. For example, within
   RubyGem development environment (as generated by `newgem`), the `script/generator`
   only shows `rubygem`-related generators. Rails could restrict `script/generator`
   to only show Rails related generators
2. Your framework RubyGem (e.g. `newgem` or `rails`) needs to add `rubigen` as a 
   dependency, so that users of your RubyGem can access the generator framework.
   
= Creating generators

There are two types of generators:

1. Application Generators - used by developers of your framework to get started. 
   Generally, you will create one Application Generator for your framework.
   It generates a base stub (such as the `rails` stub for new Rails applications)
   for your framework users.
2. Component Generators - used by developers to extend their application.
   You may include 1+ built-in generators with your framework.
   Developers can also write generators for your framework, and like Rails' generator
   install them in various places and have access to their via RubiGen.
   
== Creating an Application Generator for your Framework

Without RubiGen, to give your users a head start and create a stub for them, you will 
copiously use `mkdir_p` and `File.open`. Your script will either be primitive (only
create the bare folders and very few files) or it will be very long and unreadable
(ok, perhaps I'm just talking about the `newgem` script, which I am dubiously responsible
for... :P).

With RubiGen, you can create stubs using powerful, yet simple, syntax. Templates are
extracted into a `templates` folder, and activating the generator from a script requires
only a few lines of code.

These are the `newgem` files related to its Application Generator.

    bin/
      bin/newgem        # Application Generator script; Usage: newgem gemname [options]
    app_generators/
      app_generators/newgem/
        app_generators/newgem/newgem_generator.rb
        app_generators/newgem/USAGE
        app_generators/newgem/templates/
          app_generators/newgem/templates/app.rb
          app_generators/newgem/templates/History.txt
          app_generators/newgem/templates/... lots and lots of templates

The `bin/newgem` script is very simple, and looks like:

    require 'rubygems'
    require 'rubigen'

    if %w(-v --version).include? ARGV.first
      require 'newgem/version'
      puts "#{File.basename($0)} #{Newgem::VERSION}"
      exit(0)
    end

    require 'rubigen/scripts/generate'
    RubiGen::Base.use_application_sources!
    RubiGen::Scripts::Generate.new.run(ARGV, :generator => 'newgem')

You can copy and paste this for your own generator script, and place it in your RubyGem's `bin` folder.
Change `newgem` to your RubyGem's name in the script above (and in all the folders listed above too)

NOTE: If you leave `newgem` there, then it will execute the `newgem_generator.rb` generator; 
as the generators are loaded from all RubyGem's having `/app_generators` folders.

So, for your RubyGem, you need to keep the `/app_generators` folder (as you are creating an 
Application Generator, not a Component Generator), but change `newgem` to `your gem name` in
all the subfolders and files. ESPECIALLY `newgem_generator.rb` -> `yourgem_generator.rb`,
as this is how the generator is discovered (via `RubiGen::Base.use_application_sources!`).

All the generator work is performed within `yourgem_generator.rb`. A stub for it will be:

    require 'rbconfig'

    class YourgemGenerator < RubiGen::Base
      DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
                                  Config::CONFIG['ruby_install_name'])

      default_options   :shebang => DEFAULT_SHEBANG,
                        :an_option => 'some_default'
  
      attr_reader :app_name, :module_name
  
      def initialize(runtime_args, runtime_options = {})
        super
        usage if args.empty?
        @destination_root = args.shift
        @app_name     = File.basename(File.expand_path(@destination_root))
        @module_name  = app_name.camelize
        extract_options
      end

      def manifest
        # Use /usr/bin/env if no special shebang was specified
        script_options     = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] }
        windows            = (RUBY_PLATFORM =~ /dos|win32|cygwin/i) || (RUBY_PLATFORM =~ /(:?mswin|mingw)/)

        record do |m|
          # Root directory and all subdirectories.
          m.directory ''
          BASEDIRS.each { |path| m.directory path }
      
          # Root
          m.template_copy_each %w( Rakefile )
          m.file_copy_each     %w( README.txt )

          # Test helper
          m.template   "test_helper.rb",        "test/test_helper.rb"

          # Scripts
          %w( generate ).each do |file|
            m.template "script/#{file}",        "script/#{file}", script_options
            m.template "script/win_script.cmd", "script/#{file}.cmd", 
              :assigns => { :filename => file } if windows
          end
       
        end
      end

      protected
        def banner
          <<-EOS
    Create a stub for #{File.basename $0} to get started.

    Usage: #{File.basename $0} /path/to/your/app [options]"
    EOS
        end

        def add_options!(opts)
          opts.separator ''
          opts.separator "#{File.basename $0} options:"
          opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
        end
    
      # Installation skeleton.  Intermediate directories are automatically
      # created so don't sweat their absence here.
      BASEDIRS = %w(
        doc
        lib
        log
        script
        test
        tmp
      )
    end

Easy peasy.

== Creating a Component Generator for your Framework

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

drnic

90 commits

bklang

11 commits

benlangfeld

2 commits

jarib

2 commits

Languages

Ruby

100.0%