rails / thor

Thor is a toolkit for building powerful command-line interfaces.
http://whatisthor.com/
MIT License
5.12k stars 552 forks source link

Issue running Thor with Rails 7 #793

Closed jakepino closed 2 years ago

jakepino commented 2 years ago

When trying to run our ThorFile, we get this error:

WARNING: unable to load thorfile: uninitialized constant Rails::Command::Base

    class ConsoleCommand < Base # :nodoc:
                           ^^^^
Did you mean?  Base64

It works in Rails 6.1.6.1, but it does not work in Rails 7.0.3.1

our ThorFile contains:

# frozen_string_literal: true

Dir['./lib/thor/**/*.rb'].each { |f| load f }

The command we are running that brings up this error is bundle exec thor roles:create (string)

and the content of lib/thor/bootstap/roles.rb file is:

# frozen_string_literal: true

class Thor
  module Bootstrap
    class Roles < Thor
      include Thor::Actions

      namespace 'roles'

      desc 'create NAME', 'Create a new role NAME'
      def create(name)
        puts "Creating role #{name}"
        return if Role.find_by(name: name)

        Role.create!(
          name: name
        )
      end

      desc 'create_all_roles_with_permissions', 'Creates all roles with permissions via rake task'
      def create_all_roles_with_permissions
        Rails.application.load_tasks
        Rake::Task['role:create_all_roles_with_permissions'].invoke
      end
    end
  end
end

Still have not found a solution. If anyone has any ideas or fixes, please let me know - or even if anyone has been running into the same issues.

dorner commented 2 years ago

Have you tried replacing class ConsoleCommand < Base with class ConsoleCommand < Thor::Base ?

ConsoleCommand is not part of Thor so I assume it's your own code?

jakepino commented 2 years ago

Unfortunately, class ConsoleCommand < Base is part of Rails 7.

ruby/gems/3.1.0/gems/railties-7.0.3.1/lib/rails/commands/console/console_command.rb:75:in `<module:Command>'

dorner commented 2 years ago

I'm not sure this has anything to do with Thor. This seems to be part of Railties code - it looks like it's autoloading things in the wrong order or incorrectly.

Maybe open an issue here? https://github.com/rails/rails

dorner commented 2 years ago

Actually, maybe try require rails/command/base - play around with that. It seems like it can't find that class for some reason.

jakepino commented 2 years ago

We tried adding ‘require’ but couldn’t find anywhere that worked.

Has anyone been able to get Thor to work with this version of Rails?

dorner commented 2 years ago

Rails is built around Thor - if it stopped working, wouldn't all of the Rails generators stop working? I'm wondering if there's a particular code path that you are hitting that isn't doing what it should be. Or possibly it has something to do with Zeitwerk and how things are being loaded in your app? Is this a brand new Rails 7 app or was it upgraded from Rails 6?

A simple Thorfile in an app running Rails 7 works fine:

# frozen_string_literal: true

class Test < Thor
  include ::Thor::Actions
  namespace "test"

  desc "foobar", "whatever"
  def foobar(name)
    puts "This is a test #{name}"
  end

end
> thor test:foobar a_name
This is a test a_name

I'm wondering if the issue is that your Thor classes are being put within the parent Thor class instead of on their own? Wondering if that's messing up the resolution.

jakepino commented 2 years ago

Thor is working for us, until we try to include the Rails environment. Below is a simple reproducible case that we tested inside our Thorfile.


class Test < Thor
    include ::Thor::Actions

      namespace 'roles'

      desc 'create NAME', 'Create a new role NAME'
      def create(name)
        require File.expand_path('config/environment.rb')
        puts "Creating role #{name}"
        return if Role.find_by(name: name)

        Role.create!(
          name: name
        )
      end
end

It fails with:

  /Users/jake.pino/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/railties-7.0.3.1/lib/rails/commands/console/console_command.rb:75:in `<module:Command>': uninitialized constant Rails::Command::Base (NameError)

    class ConsoleCommand < Base # :nodoc:
                           ^^^^
Did you mean?  Base64
        from /Users/jake.pino/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/railties-7.0.3.1/lib/rails/commands/console/console_command.rb:74:in `<module:Rails>'
        from /Users/jake.pino/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/railties-7.0.3.1/lib/rails/commands/console/console_command.rb:8:in `<main>'
dorner commented 2 years ago

This works fine for me as well.

I removed the model creation (since my app doesn't have that model) and added some debugging:

class Test < Thor
    include ::Thor::Actions

      namespace 'roles'

      desc 'create NAME', 'Create a new role NAME'
      def create(name)
        require File.expand_path('config/environment.rb')
        puts Rails.version
        puts "Creating role #{name}"
      end
end
> thor roles:create A_NAME
7.0.3
Creating role A_NAME

This is in the context of an existing Rails 7 application. My hunch is still that your application has zeitwerk configured incorrectly somehow.

rafaelfranca commented 2 years ago

We have many Rails 7 application that uses thor every day. Even Rails itself is tested against thor. So there isn't any incompatibilities between Rails 7 and Thor. This must be something else, probably a bundler issue.

Can you please provide the full stacktrace?

jakepino commented 2 years ago

Thank you for the assistance, we got it to work by adding require 'rails/command' and require 'rails/command/base' to our config/environment.rb file, as well as updating this.