SpinaCMS / Spina

Spina CMS
http://www.spinacms.com
Other
2.19k stars 405 forks source link

uninitialized constant Spina::Part (NameError) in Rails 7 #981

Closed tobiasbhn closed 2 years ago

tobiasbhn commented 2 years ago

I recently updated my application to Rails 7 (v7.0.2.2) using Ruby 3 (v3.0.0p0 revision 95aff21468) and got an NameError on Spina::Part, whilst registering Custom Parts. It also happens in a new project. The Code which throws the Error worked fine before updating and was not changed during update.

Environment

OS: Linux Ruby: 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux] Rails: 7.0.2.2 Bundler: version 2.2.3 Spina: 2.8.1

Steps to Reproduce

Expected Behaviour

The Spina installation is completed and any rails Command executes without any error.

Maybe Related SO

Found this SO which might be related, but no progress there. https://stackoverflow.com/questions/70655590/rails-7-uninitialized-constant-impressionistengineimpressionistcontroller

Complete Stacktrace

** Invoke db:migrate (first_time)
** Invoke db:load_config (first_time)
** Invoke environment (first_time)
** Execute environment
rails aborted!
NameError: uninitialized constant Spina::Part
Did you mean?  Spina::PARTS
/home/tobias/projects/spina-name-error/config/initializers/spina.rb:2:in `block in <main>'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/activesupport-7.0.2.2/lib/active_support/configurable.rb:38:in `configure'
/home/tobias/projects/spina-name-error/config/initializers/spina.rb:1:in `<main>'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:48:in `load'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:48:in `load'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/engine.rb:667:in `block in load_config_initializer'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/activesupport-7.0.2.2/lib/active_support/notifications.rb:208:in `instrument'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/engine.rb:666:in `load_config_initializer'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/engine.rb:620:in `block (2 levels) in <class:Engine>'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/engine.rb:619:in `each'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/engine.rb:619:in `block in <class:Engine>'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/initializable.rb:32:in `instance_exec'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/initializable.rb:32:in `run'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/initializable.rb:61:in `block in run_initializers'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:228:in `block in tsort_each'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:422:in `block (2 levels) in each_strongly_connected_component_from'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:431:in `each_strongly_connected_component_from'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:421:in `block in each_strongly_connected_component_from'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/initializable.rb:50:in `each'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/initializable.rb:50:in `tsort_each_child'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:415:in `call'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:415:in `each_strongly_connected_component_from'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:349:in `block in each_strongly_connected_component'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:347:in `each'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:347:in `call'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:347:in `each_strongly_connected_component'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:226:in `tsort_each'
/home/tobias/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:205:in `tsort_each'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/initializable.rb:60:in `run_initializers'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/application.rb:372:in `initialize!'
/home/tobias/projects/spina-name-error/config/environment.rb:5:in `<main>'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:35:in `require'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/application.rb:348:in `require_environment!'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/application.rb:510:in `block in run_tasks_blocks'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `block in execute'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `each'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `execute'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:243:in `block in invoke_prerequisites'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `each'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `invoke_prerequisites'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:218:in `block in invoke_with_call_chain'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:243:in `block in invoke_prerequisites'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `each'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `invoke_prerequisites'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:218:in `block in invoke_with_call_chain'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/task.rb:188:in `invoke'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/application.rb:160:in `invoke_task'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block (2 levels) in top_level'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `each'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block in top_level'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/application.rb:125:in `run_with_threads'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/application.rb:110:in `top_level'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/commands/rake/rake_command.rb:24:in `block (2 levels) in perform'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/application.rb:186:in `standard_exception_handling'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/commands/rake/rake_command.rb:24:in `block in perform'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/rake-13.0.6/lib/rake/rake_module.rb:59:in `with_application'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/commands/rake/rake_command.rb:18:in `perform'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/command.rb:51:in `invoke'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/railties-7.0.2.2/lib/rails/commands.rb:18:in `<main>'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/home/tobias/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => db:migrate => db:load_config => environment

Content of user.rb

# app/models/spina/parts/user.rb
module Spina
  module Parts
    class User < Base
      attr_json :user_id, :integer

      def content
        # changed from .find to .find_by as described in spina pr#971
        Spina::Page.find_by(id: user_id)

      end
    end
  end
end
tobiasbhn commented 2 years ago

Interestingly, if I comment out Spina::Part.register(Spina::Parts::User) in the Initializer, the Error does not occur and any rails command, like rails c, runs perfectly fine. Using the exact same Code Snippet Spina::Part.register(Spina::Parts::User) in the rails console itself, it does work and it adds the custom Part to Spina::PARTS. No NameError here

Maybe there is something changed or wrong in the order of loading / require?

tobiasbhn commented 2 years ago

This is related to https://github.com/rails/rails/issues/40904 (I think!).

Actually I got an Deprecation warning for all custom Parts all time long in previous 6.1.4.1 Rails, which did not get displayed to console but in logs. It stated following:

DEPRECATION WARNING: Initialization autoloaded the constants Spina::Part, Spina::Parts, Spina::Parts::Base, and Spina::Parts::User.

Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed during
initialization does not run again. So, if you reload Spina::Part, for example,
the expected changes won't be reflected in that stale Class object.

These autoloaded constants have been unloaded.

In order to autoload safely at boot time, please wrap your code in a reloader
callback this way:

    Rails.application.reloader.to_prepare do
      # Autoload classes and modules needed at boot time here.
    end

That block runs when the application boots, and every time there is a reload.
For historical reasons, it may run twice, so it has to be idempotent.

Check the "Autoloading and Reloading Constants" guide to learn more about how
Rails autoloads and reloads.
 (called from <main> at /home/tobias/projects/nwdl-web/config/environment.rb:5)

For now I got it working as simple as its statet in the Deprecation Warning:

# config/intializers/spina_custom_parts.rb

# Load Custom Parts for Spina
Rails.application.reloader.to_prepare do
  Spina::Part.register(Spina::Parts::User)
end

But I don't know if that is the intended way to do it in Rails (?), or whether something should be done differently according to convention. If Rails.application.reloader.to_prepare is the final understanding, it should be corrected in the documentation.

Bramjetten commented 2 years ago

I'm sorry, this is an error in our documentation. It was already edited in this guide: https://spinacms.com/guides/themes-content/how-to-create-custom-content-parts

You can use:

Rails.application.reloader.to_prepare do
  Spina::Part.register(Spina::Parts::User)
end