Call task more than once in Rails 3 generator

2020-02-25 23:12发布

问题:

I'm writing a Rails 3 generator that creates two different models. Here's a very simplified example of what I'm trying to do:

def my_generator_task
  invoke "model", ["foo"]
  invoke "model", ["bar"]
end

The problem is that the Thor invoke method only invokes a task once, so the second call to the "model" task never happens and the "bar" model is never created. Does anyone know an elegant way to accomplish this, preferably in a way that doesn't break the ability to run "rails destroy" with the generator?

回答1:

One more thought, this way it is also possible to run multiple model generator without migration

Rails::Generators.invoke("active_record:model", ["foo", "--no-migration" ])
Rails::Generators.invoke("active_record:model", ["bar", "--no-migration" ])


回答2:

With Thor, if you want to invoke a task withOUT dependency management, you just call it directly:

model(foo)
model(bar


回答3:

In case you want to run a generator that subclasses from Thor::Group, i.e. not just a single Thor task, you can invoke an entire generator from any different file.

Rails::Generators.invoke("my_generator", my_generator_args)

The generators module generators.rb in railties/rails seems to create a new instance, so it doesn't think that the task has already been called. This means you can repeat the above line as many times as you want and it will run each time.



回答4:

I don't know of an elegant way to do that. In this talk, I gave an example of a custom generator that invokes the controller generator twice - check out slide 43.

The inelegant way is to go into Thor's @_invocations array and delete the first run's tasks before running it again.



回答5:

there is a macro method called "behavior" can help(using bonyiii's example):

def generate_model
  if behavior == :invoke
    Rails::Generators.invoke("active_record:model", ["foo", "--no-migration"], behavior: behavior)
    Rails::Generators.invoke("active_record:model", ["bar", "--no-migration"], behavior: behavior)
  else # behavior == :revoke
    Rails::Generators.invoke("active_record:model", ["foo", "--no-migration"], behavior: :revoke)
    Rails::Generators.invoke("active_record:model", ["bar", "--no-migration"], behavior: :revoke)
  end
end

or just:

def generate_model
  Rails::Generators.invoke("active_record:model", ["foo", "--no-migration"], behavior: behavior)
  Rails::Generators.invoke("active_record:model", ["bar", "--no-migration"], behavior: behavior)
end