4

We are on Rails 5 and have a flow we are trying to automate which involves the creation of several models. We made a custom generator that handles this, among other things.

The first step creates some files that we need, and works splendidly, however we began to chain things and noticed something quite odd:

If we run invoke model some_args more than once, the second run does nothing, and exits with a 0 status code. We are somewhere in the Rack layer when this happens, however it seems that if we change invoke to be another generator it will run fine.

If we wrap rails g model in back-ticks it will run and execute the generator:

`rails g model`

Here is the code:

require "rails/generators"
require_relative "helpers/scaffold_generator_helper"
module ActiveCsv
  class ModelScaffoldGenerator < Rails::Generators::NamedBase
    desc <<~DESCRIPTION
    This Generator takes a file containing a class inheriting from ActiveCsv 
    and produces 3 models and migrations (SourceModel, DestinationModel, FailedImportModel) 
    based on the configurations within the object
    DESCRIPTION
    def load_object_params
      @helper = ActiveCSVScaffoldGeneratorHelper.new(name)
      @params = @helper.extract_parameters
      puts @params
    end

    def handle_source_model
      case self.behavior
      when :invoke
        args = @helper.generate_source_model_options
        created_files = invoke("model", args)
        created_class_name = created_files[0][0][0]
        created_model_path = created_files[0][2]
        inject_into_class(created_model_path, created_class_name) do
          "  acts_as_copy_target\n"
        end
      when :revoke
        invoke "model", [@helper.source_model_name], behavior: :revoke
      end
    end

    def handle_destination_model
      case self.behavior
      when :invoke
        args = @helper.generate_destination_model_options
        created_files = invoke("model", args)
      when :revoke
        invoke "model", [@helper.destination_model_name], behavior: :revoke
      end
    end
  end
end

Is it not possible to run the same generator multiple times inside of another generator?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Adam May
  • 153
  • 8
  • Small update - If we wrap a `rails g model` command in back ticks it **will** run and execute the generator. – Adam May Jan 24 '20 at 23:17
  • 1
    Please don't put "updates" in comments because you force people trying to help you to look through every comment in the page to try to figure out what's happening and the order they happened in. Instead, insert it into the question where it'd have been if you put it there initially. Don't mark the changes with "updated" or "edited" as we can see when and what changed if we need to. – the Tin Man Jan 24 '20 at 23:40
  • Are you seeing any errors in either the server or database logs? – the Tin Man Jan 24 '20 at 23:41
  • We're not seeing anything in the server or database logs, which we expected - so no leads there – Adam May Jan 27 '20 at 15:36
  • 1
    Because it runs with `\`rails g model\`` it sounds like it might be an environment problem because at that point it'd be in a sub-shell. You might want to run `gem env` and copy/paste that into your question, formatting it appropriately. You don't say whether you're running Ruby under rbenv or RVM, but those can cause "interesting" behavior if not installed correctly or when the environment isn't set right. – the Tin Man Jan 27 '20 at 20:25

1 Answers1

2

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.

Adam May
  • 153
  • 8