Rails: NameError with lib module - ruby-on-rails

In my device model I try to call a class method on a lib class, but on production I get a
NameError (uninitialized constant Device::Push):
It works on develoment, though.
But I don't call a Device::Push class. Instead that's what I'm doing in my Device model:
class Device < ActiveRecord::Base
...
after_update :send_notifications
...
def send_notifications
Push::NotificationFactory.send(device: self) if (self.push_token_changed? && ! self.push_token.nil?)
end
end
And in lib/push/notification_factory.rb the following class should be called:
module Push
class NotificationFactory
def self.send(device: )
n = Rpush::Apns::Notification.new
n.app = Rpush::Apns::App.find_by_name(Rails.configuration.x.app['push']['app_name'])
n.device_token = device.push_token
n.alert = 'Welcome to PrizeArena!'
n.save!
end
end
end
That's the error message I get:
2016-02-28T13:40:51.407542+00:00 app[web.1]: NameError (uninitialized constant Device::Push):
2016-02-28T13:40:51.407543+00:00 app[web.1]: app/models/device.rb:77:in `send_notifications'
2016-02-28T13:40:51.407544+00:00 app[web.1]: app/controllers/api/v1/users_controller.rb:44:in `update'
Would be great if somebody could point out where the NameError is coming from.

I figured out the problem why it worked on development but not on production.
I was adding lib to the auto_load path, but needed to add it to the eager_load_path.
You need to add
config.eager_load_paths << Rails.root.join('lib')
to application.rb
The problem was only happening on production, not on development, because on production Rails eager_loads the environment by default.
More information here:
http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload/

NameError (uninitialized constant Device::Push)
You need to use include to embed a module in a class.
class Device < ActiveRecord::Base
include Push
...
after_update :send_notifications
...
def send_notifications
Push::NotificationFactory.send(device: self) if (self.push_token_changed? && ! self.push_token.nil?)
end
end

Related

Overriding const_missing returns `NameError uninitialized constant` in non-dev environments

I have the following code in my rails app
# app/models/test_module/text_class.rb
module TestModule
class TestClass
end
end
# app/models/test_module.rb
module TestModule
def self.const_missing(name)
super(delete_end_number(name.to_s).to_sym)
end
def self.delete_end_number(str)
str.gsub(/\d+$/,'')
end
end
When it runs in development it works
>> TestModule::TestClass1
=> TestModule::TestClass
When I run it in production however I get
NameError (uninitialized constant TestModule::TestClass)
If I just copy TestModule::TestClass into the console it works. It seems just to not work with the const_missing method.
I suspect it may have something to do with the autoloading as when I set config.cache_classes and config.eager_load to true in development.rb it happens there too. I can't seem to figure out how to get it to work in cached environments though.
Change from
super(delete_end_number(name.to_s).to_sym)
To
const = delete_end_number(name.to_s).to_sym
ActiveSupport::Dependencies.load_missing_constant(self, const)

"NameError: uninitialized constant" when fake exists in same file

Say I have:
app/models/food/fruit.rb:
module Food
class Fruit
class_attribute :field_mapping
self.field_mapping = MyOrg::Trees::Details.field_mapping
# other things including:
end
end
In local/development mode, I can open a console and do/get this:
rails console
[1] pry(main)> Food::Fruit
NameError: uninitialized constant MyOrg::Trees::Details
MyOrg::Trees::Details will eventually be defined via a gem, but until then I want to fake it by adding the following to the top of the same file; why isn't this doing the trick?
module MyOrg
module Trees
class Details
def field_mapping
end
end
end
end
Note: when I paste the above into a local/development console, I can then successfully do this:
[2] pry(main)> MyOrg::Trees::Details.new.field_mapping
=> nil
When I added the above fake, I accidentally started trying to reference it to test what I was doing...
rails console
[1] pry(main)> MyOrg::Trees::Details
NameError: uninitialized constant MyOrg::Trees::Details
...but I should have called Food::Fruit as before, and I would have isolated the issue...
rails console
[1] pry(main)> Food::Fruit
NoMethodError: undefined method `field_mapping' for MyOrg::Trees::Details:Class
...ah, I need to make it a class method...
module MyOrg
module Trees
class Details
def self.field_mapping
end
end
end
end
...and then...
rails console
[1] pry(main)> Food::Fruit
NoMethodError: undefined method `values' for nil:NilClass
So, I appeased the undefined method error, and now have a new problem, which is that further down the original code there is actually another reference to field_mapping (field_mapping.values) which now has to be appeased...
module MyOrg
module Trees
class Details
def self.field_mapping
{}
end
end
end
end
...and now...
rails console
[1] pry(main)> Food::Fruit
=> Food::Fruit

How to require and auto load files properly

I'm running into quite a few errors around how to require files property. Hoping for some insight.
There are files as so:
app/models
model.rb
app/workers
parent_worker.rb
app/workers/directory_1
directory_worker.rb
foo_worker.rb
bar_worker.rb
class DirectoryWorker < ParentWorker
end
class FooWorker < DirectoryWorker
def method_called_by_model
end
end
When I call the method, method_called_by_model I get the following error:
NameError: uninitialized constant Model::FooWorker
I've added the following to application.rb, didn't add app/workers since it should be loaded automatically according to the documentation.
config.autoload_paths << "#{Rails.root}/app/workers/directory_1"
When I require_relative the worker files in the model I get the following error referring to the inherited class being unknown:
NameError: uninitialized constant DirectoryWorker
from project/app/workers/directory_1/FooWorker.rb:2:in `<top (required)>'
Any have any ideas what I can do?
You need to namespace those workers since they are inside a directory.
First remove the autoload call you added.
Here's how the files should be named and what they should look like inside.
# app/workers/parent_worker.rb
class ParentWorker
end
# app/workers/directory_1/directory_worker.rb
class Directory1::DirectoryWorker < ParentWorker
end
# app/workers/directory_1/foo_worker.rb
class Directory1::FooWorker < Directory1::DirectoryWorker
def method_called_by_model
end
end
# app/workers/directory_1/bar_worker.rb
class Directory1::BarWorker < Directory1::DirectoryWorker
end

Rails_admin undefined method `associations' for nil:NilClass

I have these models:
Class A
embeds_many :b
end
Class B
belongs_to :c
end
Class C
end
I'm working with rails_admin and mongoid. In admin, when I try to retrieve the list of C records when I'm creating an A instance I'm getting this error:
This only happens on production envirnment not in development
NoMethodError (undefined method `associations' for nil:NilClass):
/home/pablo/.rvm/gems/ruby-2.3.0#mh-backend/bundler/gems/rails_admin-355dc80f8a20/lib/rails_admin/adapters/mongoid/abstract_object.rb:10:in `initialize'
/home/pablo/.rvm/gems/ruby-2.3.0#mh-backend/bundler/gems/rails_admin-355dc80f8a20/lib/rails_admin/adapters/mongoid.rb:24:in `new'
/home/pablo/.rvm/gems/ruby-2.3.0#mh-backend/bundler/gems/rails_admin-355dc80f8a20/lib/rails_admin/adapters/mongoid.rb:24:in `get'
/home/pablo/.rvm/gems/ruby-2.3.0#mh-backend/bundler/gems/rails_admin-355dc80f8a20/app/controllers/rails_admin/main_controller.rb:138:in `get_association_scope_from_params'
Taking a look at rails_admin code we can see that piece of code in mongoid.rb file.
def get(id)
AbstractObject.new(model.find(id))
rescue => e
raise e if %w(
Mongoid::Errors::DocumentNotFound
Mongoid::Errors::InvalidFind
Moped::Errors::InvalidObjectId
BSON::InvalidObjectId
).exclude?(e.class.to_s)
end
If we pay attention at this code we can see that model.find(id) must produce a Mongoid::Errors::DocumentNotFound if document doesn't exists by default.
However, in mongoid you can avoid the raise of this error with raise_not_found_error: true in mongo config file, this produce the undefined method of nil class.
Tracking issue on github

Delayed_job in rails failing

I am just beginning to look into using the delayed_job gem.
To test it, I added "delayed" to the welcome email function and changed that call from
UserMailer.welcome_email(self).deliver
to
UserMailer.delay.welcome_email(self)
This is called inside the User model after_create. I see an entry show up in the delayed_job table after the function executes. Now when I run "rake jobs:work" on command line the task starts but gives errors as below
[Worker(host:Sanjay-PC pid:7008)] Starting job worker
[Worker(host:Sanjay-PC pid:7008)] Class#welcome_email failed with NoMethodError: undefined method `welcome_email' for #<Class:0x4871d60> - 0 failed attempts
[Worker(host:Sanjay-PC pid:7008)] 1 jobs processed at 0.0939 j/s, 1 failed ...
Thinking that if I changed the welcome_email method declaration to a Class method as
def self.welcome_email(user)
(added self. in front) that might help. But then when I run rake jobs:work I get the following error
rake aborted!
undefined method `welcome_email' for class `UserMailer'
C:/Ruby192/lib/ruby/gems/1.9.1/gems/activesupport-3.0.5/lib/active_support/core_ext/module/aliasing.rb:31:in `alias_method'
C:/Ruby192/lib/ruby/gems/1.9.1/gems/activesupport-3.0.5/lib/active_support/core_ext/module/aliasing.rb:31:in `alias_method_chain'
C:/Ruby192/lib/ruby/gems/1.9.1/gems/delayed_job-2.1.4/lib/delayed/message_sending.rb:50:in `handle_asynchronously'
c:/mgn/mgn-r3/app/mailers/user_mailer.rb:10:in `<class:UserMailer>'
c:/mgn/mgn-r3/app/mailers/user_mailer.rb:1:in `<top (required)>'
C:/Ruby192/lib/ruby/gems/1.9.1/gems/activesupport-3.0.5/lib/active_support/dependencies.rb:454:in `load'
<Stack truncated>
It seems to now know the class as UserMailer but it somehow doesn't see the class method welcome_email.
I am on Rails 3.0.5, Ruby 1.9.2p180 and the installed delayed_job gem is 2.1.4 - on Windows
Can't seem to find any related answers anywhere.
Thanks for your thoughts.
-S
Adding UserMailer code per #pjammer's request
class UserMailer < ActionMailer::Base
default :from => "from#example.com"
def welcome_email(user)
#user = user
#url = "http://example.com/login"
mail(:to => user.email,
:subject => "Welcome to My Awesome Site")
end
end
Just use this
UserMailer.delay.welcome_email(self).deliver
instead of
UserMailer.welcome_email(self).delay.deliver
My solution was to redefine function at the handler class (for you it's UserMailer class)
def self.taguri
'tag:ruby.yaml.org,2002:class'
end
It's a hack and I'll try to find a better solution but now it works for me.
(Rails 3.0.9, Ruby 1.9.2-p290, delayed_job 2.1.4)
https://groups.google.com/forum/?fromgroups=#!topic/delayed_job/_gvIcbXrOaE solved my handles_asynchronously error for class methods.
As per Brandon Keeper in the link above, the code is the following:
class ClassName
class << self
def foo
end
handle_asynchronously :foo
end
end
then use ClassName.delay.foo

Resources