How to Get Generators Call Other Generators in Rails 3

How to get generators call other generators in rails 3

In your generator, you can just call

generate "some:generator" # can be anything listed by 'rails g'

for example:

module MyGem
class InstallGenerator < Rails::Generators::Base

def run_other_generators
generate "jquery:install" # or whatever you want here
end

end
end

By the way, if you are working on Rails 3 gems, this question can also help out:

Rails 3 generators in gem

Running other generator within a generator

You must define a hook_for the other generator in your generator class. The ScaffoldGenerator is probably the best example of this, as it has a hook for the scaffold_controller generator.

If you want to pass in arguments to your generator, it's probably better to use something like the hook_for examples further down in this file for assets and stylesheet engine.

Rails 3 generators in gem

This took a little bit for me to figure out, but I've run into the same problem. Here is how I fixed it.

Tree structure looks like this:

lib
- generators
- gemname
install_generator.rb
- templates
(template files)

Here's the code for install_generator.rb

#lib/generators/gemname/install_generator.rb
require 'rails/generators'
module Gemname
class InstallGenerator < Rails::Generators::Base
desc "Some description of my generator here"

# Commandline options can be defined here using Thor-like options:
class_option :my_opt, :type => :boolean, :default => false, :desc => "My Option"

# I can later access that option using:
# options[:my_opt]

def self.source_root
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
end

# Generator Code. Remember this is just suped-up Thor so methods are executed in order

end
end

When I run
rails g

I see:

Gemname
gemname:install

Some other things you may need to setup:

#lib/gemname.rb
module Gemname
require 'gemname/engine' if defined?(Rails)
# any additional requires
end

and

#/lib/gemname/engine.rb
require 'rails'
module Gemname
class Engine < Rails::Engine
end
end

Some good references I've found on this are:

  • http://textmate.rubyforge.org/thor/Thor.html (take a look at the modules, especially Thor::Actions)
  • http://api.rubyonrails.org/classes/Rails/Generators/Base.html
  • http://api.rubyonrails.org/classes/Rails/Generators/Actions.html
  • https://github.com/wycats/thor/blob/master/README.md
  • http://www.themodestrubyist.com/2010/03/16/rails-3-plugins---part-3---rake-tasks-generators-initializers-oh-my/

Ruby on Rails Custom Generators with multiple sub generators

What rmlockerd explained only half answered my question, Here's what worked:

I created the custom generators using rails g generator g1 and rails g generator g2 and organized them into the following directory structure:

# directory: /lib/generators
λ tree
.
└── gorking_generators
├── g1
│   ├── g1_generator.rb
│   ├── templates
│   └── USAGE
└── g2
├── g2_generator.rb
├── templates
└── USAGE

The content of the files are as follows:

# file: g1_generator.rb
module GorkingGenerators
module Generators
class G1Generator < Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
end
end
end
# file: g2_generator.rb
module GorkingGenerators
module Generators
class G2Generator < Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
end
end
end

After this, I am able to see the generators in place when using rails g:

GorkingGenerators:
gorking_generators:g1
gorking_generators:g2

I can then use them using:

rails g gorking_generators:g1

Fixing Rails generator issues: calling same generator twice no-ops and exits 0 without running 2nd generator

Turns out the problem lies in Thor - it will only read the first invoke statement in any generator. Found the solution in this answer to a question about rails 3 generators - turns out it hasn't changed since.

Quoting the original answer:

"There's a catch however: Probably due to Thors dependency management, this only works once per generator you want to call, meaning that a second invoke of the same generator will do nothing. This can be circumvented by using a statement like

Rails::Generators.invoke 'active_record:model', '...', behavior: behavior"

So replacing invoke with Rails::Generators.invoke totally did the trick.

How to invoke generators from code?

Solution appears to be pretty simple:

This code in controller

Rails::Generators.invoke("ead_document", [@document_type.table_name.classify, "--document_type_id=#{@document_type.id}"])

is the same as this in console

rails generate ead_document TechnicalOpinion --document_type_id=1

In case you want to use it outside a controller, you may also want to require it explicitely:

require 'rails/generators'

Rails Gem: Running All Generators for given Namespace

Keep an "AllGenerator" class under Admin module. The generator will have to do the following :

  1. For each class under the namespace that is a generator class,
  2. get the namespace from classname.
  3. Call the invoke method with the namespace.

Something like this :

module Admin
module Generators
class AllGenerator < Rails::Generators::Base
def generator
Rails::Generators.lookup!
Admin::Generators.constants.each do |const|
generator_class = Admin::Generators.const_get(const)
next if self.class == generator_class
if generator_class < Rails::Generators::Base
namespace = generator_klass_to_namespace(generator_class)
invoke(namespace)
end
end
end
private
def generator_klass_to_namespace(klass)
namespace = Thor::Util.namespace_from_thor_class(klass)
return namespace.sub(/_generator$/, '').sub(/:generators:/, ':')
end
end

end
end

Here's the link to the gist with complete tested code

This way, running rails g admin:all would run every other generator directly under Admin::Generators .



Related Topics



Leave a reply



Submit