Search code examples
ruby-on-railsrubythor

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


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?


Solution

  • 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.