Rails 7 - autoloading fails for Rails engine RSpec tests with gem - ruby-on-rails

I maintain https://rubygems.org/gems/scimitar. Yesterday, we wanted to upgrade our primary application to Rails 7. This required a corresponding upgrade of Scimitar.
Tests I think are quite straightforward for a Rails engine; you have a dummy Rails app inside your tests, which requires the gem code as usual (require 'scimitar' in application.rb, in this case) and then - well - I guess via the mount in the dummy app's routes.rb, or some other autoloading magic, it thereafter "just works". Classes that are defined in the engine's application components (e.g. /app/models/gemname/foo.rb -> Gemmname::Foo) are autoloaded and available in your dummy application (e.g. /spec/dummy/app/controllers/some_controller.rb can reference Gemname::Foo).
The test suite works fine in Rails 6. If I change the gemspec file to reference Rails 7 and bundle update, tests immediately all fail. None of the constants defined in the engine are visible to the dummy app and since they're referenced by a configuration file in spec/dummy/app/config/initializers/scimitar.rb, the dummy app can't even complete Ruby parsing without raising NameError (uninitialized constant). I also ran a Rails 7 upgrade on that dummy app, but it does kinda nothing and had almost no changes; there were no changes to observed behaviour (tests still failed) and the post-upgrade dummy app ran tests successfully with the gem on Rails 6.
So, it just fails to behave in a recognisably sensible way on Rails 7.
Ruby version is unchanged at 2.7.x (though I would have bump to 3.1 if the Rails 7 update had worked).
I cannot find anything about this in 6->7 upgrade docs; classic autoloader has never been used and there is no specification about it either way; config.load_defaults as 6.0 or 7.0 makes no difference at all; I tried creating a new engine plugin under Rails 7 to play spot-the-difference, but couldn't see what might be wrong and it was obfuscated somewhat since I'm using RSpec but the out-of-box template uses Minitest.
Can anyone please help explain what is going on here?

As it stands in Rails 7.0.1, I found no solution so had to hack around it. Anyone using the Scimitar gem would need to wrap their initializer code (config/initializers/scimitar.rb) with:
Rails.application.config.to_prepare do
...
end
https://api.rubyonrails.org/classes/ActiveSupport/Reloader.html#method-c-to_prepare
Doing this inside the Gem's own engine initializer code and the dummy app initializer code allowed the test suite to run. Likewise, there were quite a few places in our main Rails application beyond just Scimitar that suddenly required this workaround, including some examples of just plain old Ruby gems.

Related

There is a programmatical way to detect Zeitwerk::NameError when upgrading to Rails 6?

I am currently migrating an old Rails application to Rails 6.
It seems that some files in the project are not consistent with the classes defined in it.
I don't see this error when running the tests of the application, but after deploy I receive errors like:
Zeitwerk::NameError: expected file /app/my?_/app/lib/multi_io.rb to define constant MultiIo, but didn't
In previous SO questions I could find suggestions to:
Rails 6: Zeitwerk::NameError doesn't load class from module set config.autoloader = :classic
How to ignore a folder in Zeitwerk for Rails 6? ignore a given path
Autoloading and Zeitwerk Mode in Rails 6 tweak config.autoload_path
I am not looking for a workaround so I corrected the file raising the exception.
I would like to find a programmatical way to detect all inconsistent path/class names without running the application in production mode.
So far, three options came to my mind:
A rake task to validate the autoloading (which is not provided by the gem). Does it exists? Would you have a snippet to run from the rails console in development env instead?
A rubocop cop or another static code analyser. There is any?
Force the eager load in the test environment
What it the suggested approach to validate my code without requiring several iterations/deployments?
thank you very much in advance
You can check the autoload compatibility of a project with the zeitwerk::check task:
$ bin/rails zeitwerk:check
Hold on, I am eager loading the application.
All is good!
It exercises all the autoload paths and will flag issues such as those caused by the change in constant name inference (such as those with acronyms). The task doesn't appear to be widely advertised or documented except in the upgrade guide. But, I actually use it sometimes to just flush out constant naming issues in the project generally (i.e., where the class doesn't match the file).
#rmlockerd Described one of the best tools bin/rails zeitwerk:check for this in his answer here.
The other articles I found informative were:
Understanding Zeitwerk in Rails
9 Tips & Tricks To Help You Enable The Zeitwerk Autoloader In Your
Rails App
Rails doc on autoloading
Rails doc on upgrading from Rails 5.2 to 6.0
The process I took was to run bin/rails zeitwerk:check fix an error, run it again until I saw "All is good". After that worked through errors reported by our spec tests.
One general problem to fix is if a folder is not within the Rails default file structure, Zeitwerk will not be able to find it when autoloading. We had a folder named "services" (app/services) that needed to be added to the autoload path within the application.rb file like:
config.autoload_paths += %W(#{config.root}/app/services/)
Error:
Expected file path/file_name.rb to define constant FileName
Fix:
remove any require or require_relative in other files referencing where the class is defined (Zeitwerk doesn't need them).
Error (class is defined within a module of a sub directory):
NameError: uninitialized constant SubFile::ClassName
Fix:
Detailed in another S/O answer. Whenever method is used within sub folders structure the call as Folder::SubDirectory::Class.method_call
Goodluck!

After installing paper_trail, get "irb: warn: can't alias context from irb_context." from rails console

I've tested this by running rails c both before and after git stash. On Rails 4.1 in Mavericks, after following the instructions to add the versions table and adding has_paper_trail to three models, whenever I run rails c I get
irb: warn: can't alias context from irb_context.
I've spent some time Googling without much luck, there's old threads talking about rspec, but I don't see how that's relevant since I'm not using it. Any ideas why this is happening?
RSpec used to polute provide Object top-level methods, e.g. describe, context, etc. Fortunately they've got rid of all the monkey patching in version 3, and now all these methods are namespaced under RSpec.
One can change this behaviour through the expose_dsl_globally config flag. For backwards compatibility, it defaults to true.
The warning shows up when you open the console because paper_trail automatically loads its rspec helpers when rspec is found. And it calls RSpec.configure before you have the chance to tweak your own configuration.
One possible solution would be paper_trail to disable the automatically loading and let users to load it themselves when they see fit. However, I am not aware of the internals of the library, so I can't guarantee this wouldn't break other things.
Best!
This is now fixed in papertrail 4.0.0, here's the commit.

JasmineRice on Rails 4

Has anyone tried JasminRice gem with Rails 4?
I've got Ember Rails 4 app and I would like to use Jasmine Rice but I'm not sure whether it would work out well as their Github account says Pain free coffeescript testing under Rails 3.1
Jasmine Rice
I would advice you to use jasmine-gem, which recently gained Rails 4 support. jasmine-gem is developed by Pivotal, the company that develops Jasmine itself, and is actively maintained. While this gem was very basic for a long time, it has improved tremendously, especially with version 2.0.0. Spec execution in Phantomjs (in addition to the browser, which is the fastest for development) is supported, and there is full integration into the Asset Pipeline, which lets you e.g. write tests in CoffeeScript. At my student job, we are using jasmine-gem extensively and are very happy with it.
In the jasminerice installation section they mention that it should work on rails 4:
This gem has been tested and run with Rails 3.1 and 3.2. It should
also run on Rails 4.
"Should" is not as strong of a word as I would like, but I have a ember rails 4 app and jaminerice has not given me any issues.

Rails 3.2.6 and dbi

I have a small rails app, which was running fine with Ruby 1.8x and Rails
2.x. In a regrettable decision, I decided to move to Ruby 1.9.x and Rails 3,
and it's a glorious pain.
My Ruby app uses MySQL, and I use Active Record for that.
However there is an earlier pgm I had written to fill in the database
before I did Rails (2.x), which is part of the complete application now.
(I can test/run the standalone pgm outside Rails and there is no
problem.)
This standalone program is using MySQL and dbi gems. I call this program as such from a model:
system("ruby standalonepgm.rb -args ")
In Rails 2.0 this works without any issues.
In 3.0 the program exits without any way to capture the error.
Running under console I see that the program dies because it can't find the
dbi gem!
If I put the dbi gem in the Gemfile and do bundle date, then there is
real trouble. Rails refuses to start - the rails server dies with all kind of issues.
I can put in the screendump, but I think that's unimportant.
There seem to be 2 issues:
DBI is surely incompatible with the gods of Rails
Rails creates a sandbox, and all programs called must live in that
sandbox (that's why just a require statement doesn't suffice .. it has to be in Gemfile).
Is it fixable or I am one of those who got bitten by the hidden black magic of rails, and my past 8+ weeks of effort is down the tubes?
It's fixed by using
Bundle.with_clean_env do
system("ruby pgm.rb"
end
I had never read bundle doc ..this case is described in it ..

ActiveRecord dependency with Ruby, Rails, Cucumber, & RSpec

We are writing a Rails application that is using CouchDB as its data store. We're BDD/TDD'ing with RSpec and Cucumber, which is using WebRat for webpage testing
I'm trying to remove ActiveRecord as one of the resources that is being loaded by rails but its causing the cucumber tests to fail. I've removed all references that I can find (fixtures, environment files, etc...) but it still fails without it.
Has anyone seen this? The application runs fine without, but the test don't.
edit
I did remove the framework in env file, I also removed all the transactional fixture code. We're using the latest version of rspec and rspec-rails.
First stab at the problem.
Really I need a little more information, but...
Assuming you have done this in config/environment.rb:
# Skip frameworks you're not going to use. To use Rails without a database
# you must remove the Active Record framework.
config.frameworks -= [ :active_record ]
and are using rspec-rails 1.2.6, you would be getting an error like uninitialized constant Spec::Matchers::Change::ActiveRecord
which was brought up in ticket #810. It was fixed for 1.2.7, which was released only two weeks ago.
If that turns out not to be your problem, could you post the errors you've been getting and maybe some more information about your test environment?

Resources