Rails, production-env, "Object is not missing constant" - ruby-on-rails

So I seems I was stupid and haven't checked running in production-env for a long time, and now that I'm trying to deploy, I'm getting this annoying error. Any ideas?
lib/history_tools.rb
module HistoryTools
def self.included(base)
base.has_many :history, :dependent => :destroy
History::TYPES.each do |htype|
base.has_many "history_#{htype}", :class_name => "History::#{htype.capitalize}"
end
end
# ... other minor things removed ...
end
app/models/user.rb
class User < InheritedResources::Base
include HistoryTools
end
config/environment.rb
# ... the usual stuff, then, at the very bottom:
require 'history_tools'
This gives the error:
activesupport-2.3.8/lib/active_support/dependencies.rb:417:in
`load_missing_constant':ArgumentError: Object is not missing
constant HistoryTools!
If I add an additional require 'history_tools' at the top of user.rb, it fixes that error, I believe, but then it fails at finding other things in #{RAILS_ROOT}/lib, that were required in the environment.rb in the same manner.
The kicker: this works perfectly in development mode. It only gives this error in production. Most of my googling seems to suggest that "not missing constant" errors relates to how Rails autoloads files, which should go away in production when nothing is unloaded. This seems to be the opposite of that behavior?

When I've gotten this error, it's because there's an error in an inner class/module that's inside the class/module mentioned in the error.

I can't say if this is a typo or the real code but:
class User < InheritedResources::Base
include HistoryTools
end
Should probably be
class User < ActiveRecord::Base
include HistoryTools
end
InheritedResources should be used for controllers, not models.

You shouldn't have to have the require 'history_tools' in the environment.rb. In that version of Rails all files in the lib folder should be auto-loaded.

ok, after many more hours of digging, it seems it wasn't even related to this stuff at all. It was an error about 3 more classes down the tree, that was failing for another strange reason, and the exception was apparently being caught by the internals of rails somewhere and just ignored.
That doesn't explain why it worked in development mode, unfortunately, but at least all my stuff works now. Thanks anyway!

Related

uninitialised constant after upgrade of Gemfile

I've just updated my Gemfile.
At the beginning I thought problem came from Zeitwerk (from 2.4.2 to 2.5.4) but I've downgraded it and I still have an issue on my spec. I've isolated that the problem does not come from RSpec and dependencies.
Actually, RSpec does not found a class which is defined within another file and does not match the file name/class name.
Important point: Filter::MyStandardError is found.
# app/services/filter/my_standard_error.rb
module Filter
class MyStandardError < StandardError; end
class MySpecificError < MyStandardError; end
# ...
end
# app/services/filter/my_tested_service.rb
module Filter
class MyTestedService
def initialize
raise ::Filter::MySpecificError
end
end
end
RSpec.describe Filter::MyTestedService do
subject { described_class.new }
it 'raises an error'
expect{subject}.to raise_error(::Filter::MySpecificError)
end
end
And I got the error:
NameError:
uninitialized constant Filter::MySpecificError
I got the Changelog but breaking changes are not used on my configuration.
Does anybody have an idea for this one?
You do not need to add app/services to the autoload paths, that is done automatically by Rails. I'd suggest to remove that configuration to keep things simple/idiomatic.
The implementation of app/services/filter.rb should not be needed. Your application is doing something that is not right, we just need to find it.
Could you please delete app/services/filter.rb, throw Rails.autoloaders.log! in config/application.rb, trigger the error, and share the traces?
After reading one-file-one-constant-at-the-same-top-level
I found this to fix my issue
# app/services/filter.rb
class Filter
class MyStandardError < StandardError; end
class MySpecificError < MyStandardError; end
end
# app/services/filter/my_tested_service.rb
class Filter
class MyTestedService
def initialize
raise ::Filter::MySpecificError
end
end
end
I still don't know why it was working before..
You cannot define two constants at the same level in the same file. It is one constant, one file. This has not changed in Zeitwerk upgrades. You need one file for the standard error, and another file for the specific error.

How to properly extend ApplicationRecord in Rails 6 with Zeitwerk

Consider a Rails 6 application that has app/models/application_record.rb. This Rails 6 application is using Zeitwerk loader.
class ApplicationRecord
end
If I want to add functionality to ApplicationRecord via a module:
# app/models/concerns/fancy_methods.rb
module FancyMethods
def fancy_pants
puts "I'm wearing fancy pants"
end
end
and do the following:
class ApplicationRecord
include FancyMethods
end
I will get a deprecation warning or error:
DEPRECATION WARNING: Initialization autoloaded the constant FancyMethods.
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 FancyMethods, for example,
the expected changes wont be reflected in that stale Module object.
This autoloaded constant has been unloaded.
Please, check the "Autoloading and Reloading Constants" guide for solutions.
(called from <top (required)> at /Users/peter/work/recognize/config/environment.rb:5)
I've read lots of articles including the Rails autoload docs, but nothing really addresses this minimal but common case of extending ApplicationRecord. Yes, I could wrap ApplicationRecord in a .to_prepare block like:
Rails.configuration.to_prepare do
class ApplicationRecord < ActiveRecord::Base
include FancyMethods
end
end
But this seems like a code smell and could cause other unexpected problems now or down the line.
Figured it out! The issue was there was an explicit require in an initializer that loaded ApplicationRecord.
# config/initializers/setup_other_fancy_thing.rb
require 'application_record'
module OtherFancyThing
def also_fancy
puts 'also fancy'
end
end
ApplicationRecord.send(:include, OtherFancyThing)
The way I debugged this was that I:
Created a new Rails app of the same version and could not reproduce the error
I copied the default application.rb and development.rb and still got the error
Moved the entire config/initializers directory to a temp directory and it made the warning go away! So, I knew it had to be one of the initializers. From there it was just a matter of dividing and conquering until I found the offending initializer.
Great! So, basically, you do not need to define the module and include it that way. Idiomatically, you define the module in its own file, normal:
# app/models/other_fancy_thing.rb
module OtherFancyThing
def also_fancy
puts 'also fancy'
end
end
and then
# app/models/application_record.rb
class ApplicationRecord
include FancyMethods
end
Whenever ApplicationRecord is loaded, for example as a side-effect or loading some regular model, it will load FancyMethods just fine.

Rails concern getting stuck in -cache-?

I have a model BusinessLog:
class BusinessLog < ActiveRecord::Base
include BusinessLogging
default_scope { where(business_id: Business.current.id) }
end
it performs operations in a default scope and includes a concern BusinessLog:
class BusinessLog < ActiveRecord::Base
module BusinessLogging
extend ActiveSupport::Concern
module ClassMethods
def log!(options)
create! business_id: options[:business_id], user_id: options[:user_id], action: options[:action]
end
end
end
end
this log! method was being called by a sidekiq worker. Eventually I discovered I couldn't log for several business because of BusinessLog default scope. So I removed the BusinessLog default scope, but it was somehow stuck.
Whenever I tried to perform operations through the module (eg: BusinessLog.log!(options)), it was still working with a default scope (even thought I already removed it). But when I performed operations in the model itself (eg: BusinessLog.create!(options)), it was working as expected, without the -already gone- default scope.
I tried a lot of things to make it work, but only one thing worked: Renamed the module and everything worked perfectly again.
It's worth saying that this just happened on my production environment.
Has anyone been through this? Should I post it as an issue on github? How could I give more information about this -bug-?
I'm using Rails 4.0.1 and ruby 2.0.0

Rails, custom exceptions

I'm following instructions at http://ariejan.net/2011/10/14/rails-3-customized-exception-handling/ and have hit a road block.
I am relatively new to rails, so I'm not sure what I've done right/not-so-right.
The first step was to create class
MyApp::ProfileNotFoundError < StandardError
end
So I went to app/models and created profile_not_found.rb which contains the following, where (APP) is the name of my app as defined by Rails.application.class.parent_name, but I have hidden from this post for security/privacy.
(APP)::ProfileNotFoundError < StandardError
end
In app/controllers/application_controller.rb I added
rescue_from (APP)::ProfileNotFoundError, :with => :profile_not_found
and in my login controller I added
raise (APP)::ProfileNotFoundError if #profile.nil?
However, when I try to test the code, I get a Routing Error stating
uninitialized constant (APP)::BlankUsernameError
In my opinion, this suggests that I did something wrong pertaining to the class creation, but the tutorial is so vague I can't figure it out. Any pointers?
I'm running Rails 3.0.20 & Ruby 1.8.7 on Ubuntu 12.04.2 x86_64
Do you have the class keyword in your class definition?
class MyApp::ProfileNotFoundError < StandardError
end
Secondly, you'll have to require your exceptions where you're using it. This is probably the problem you're encountering with the uninitialized constant error. To do this you will probably have to wrap it in a module:
module Exceptions
class MyApp::ProfileNotFoundError < StandardError
end
end
Also, you should put your error classes in a different directory than /models. This directory should be explicitly for your models. Maybe make one like /errors.

How to extend a mountable engine's model inside another mountable engine with development environment reloading

Using Rails 3.2.2 and Ruby 1.9.2.
I have a rails mountable engine EngineA that declares a User class inheriting form ActiveRecord::Base. I have another engine EngineB that wants to inject functionality into EngineA::User. Right now what I have done is shown below:
Method 1:
#EngineA app/models/engine_a/user.rb
module EngineA
class User < ActiveRecord::Base
has_attached_file :avatar
has_many :somethings
end
end
#EngineB lib/engine_b/user.rb
module EngineB
module User
def self.extended obj
obj.class_eval do
has_many :something_elses
end
end
end
end
EngineA::User.extend EngineB::User
This gives me an uninitialized constant EngineA::User error. Even when I require that specific file I run into the problem of EngineA needing paperclip so that has_attached_file is understood. That road ended when I realized I would have to know and require the dependencies for EngineA inside EngineB.
Method 2:
I used the same code as before except I removed the last line EngineA::User.extend EngineB::User from the EngineB user.rb file. I then moved that call to an initializer inside EngineB.
#EngineB config/initializers/my_mixin.rb
EngineA::User.extend EngineB::User
This worked perfectly!!! Except in development mode when I would change code and the models would refresh. The only thing that was refreshed was the EngineA::User and not the mixin that I had put as an initializer. So once I changed code, I lost all of my extended functionality.
I'm not even positive this is the most 'efficient' way to do this... any help would be greatly appreciated. Thanks in advance.
According to the configuration documentation, you can use an ActionDispatch callback to load items. These callbacks will run when at every request if cache_classes is set to false, like in development mode.
Inside of your EngineB.rb file, you might try something like this:
if Rails.env.development?
ActionDispatch::Callbacks.to_prepare do
load "#{File.expand_path(File.dirname(__FILE__))}/../config/initializers/my_mixin.rb"
end
end

Resources