Overriding existing model in Rails 4 - ruby-on-rails

I am trying to override an existing active record model but the new active record model doesn't have methods from the old active record model. Here is the code that I am trying to use
class ModelA < ActiveRecord::Base
def method_modela_1
logger.info "I am in method_modela_1"
end
def method_modelb_2
logger.info " I am in method_modelb_2"
end
end
Next I override the ModelA with ModelB
class ModelB < ModelA
def foo
logger.info "foo method from model B"
end
end
So now I am trying to write a code that is trying to access find_or_create_by using the following code
some_variable = ModelB.find_or_create_by(:id => 1234)
but it says ***** NoMethodError Exception: undefined method 'find_or_create_by' for ModelB:Module**
What am I missing? For record I am using 'rails', '4.2'

The keyword is "NoMethodError Exception: undefined method 'find_or_create_by' for ModelB:Module".
You have a module ModelB somewhere and, due to load order and/or constant lookup rules, it shadows your model.

Related

undefined method error in helper for a model

I'm trying to call a global method in a file in the lib/ directory from a model. I've already tried with concerns and the problem persists.
The app is developed in Ruby on Rails 5.2.1 and Ruby 2.5.3
# Expense model
class Expense < ApplicationRecord
include Helpers
def self.quantity_this_month
select { |e| year_month(e.date) == year_month(Date.today)
}.count
end
end
# Helper in lib/ directory
module Helpers
def year_month(date)
date.strftime('%Y/%m')
end
end
# in console
Expense.quantity_this_month
Implementing the code of the helper directly in the model method gives the expected result, but right now it shows this error:
undefined method `year_month' for #<Class:0x00007f2f6f0e4b88>
Any ideas?
You need to extend instead of include because the method you want to call it from is a class method.
Also, just a side note, your self.quantity_this_month loads all the Expense records into memory, but it's possible to do the date filtering and counting all within a single SQL query. The below is how it's done in MySQL, this might be slightly different with other databases:
class Expense < ApplicationRecord
extend Helpers # <~~~~~~~ changed to extend
def self.quantity_this_month
where(
"DATE_FORMAT(date, '%Y/%m') = ?",
year_month(Date.today)
).count
end
end

Get a reference to a class to output meta information of calling class

I am debugging some callbacks in our ActiveRecord models. I'd like to have a globally available method like the following:
logger.info("at end of ITEM calling check_status")
but I'd like to have a generic version that I could add to any model class. I was thinking of monkey patching ActiveRecord::Base like the following in initializers/my_initializer.rb:
class ActiveRecord::Base
# this worked
def end_log_proof
logger.info("this should work")
end
def end_log(klass)
logger.info("at end of ( #{klass.name} )")
logger.info("calling #{klass.__method__}.upcase at file #{klass.__FILE__} line: #{klass.__LINE__}")
end
end
and if I call like this:
class Item < ActiveRecord::Base
after_save :my_callback
def my_callback
end_log_proof # this works
end_log(self) # ouptuts : "calling MY_CALLBACK at ...."
end
The second one fails with:
NoMethodError (undefined method `name' for #<Item:0x007feab6359e38>):
I am trying to get a reference to the calling context - how would I get this to work?

Define a model method that gets all records

I have a rails model called "Post", it represents user's posts on a website.
Each "Post" has a method called "score" which takes its count of views and calculates a score. Score is not stored in the db, it's just a method:
class Post < ActiveRecord::Base
has_many :votes
def score
self.votes * 2
end
# This should return an object with all the posts, ordered by score.
def get_all_posts_sorted_by_rank
...
end
Error in Rails console:
2.0.0-p451 :001 > Post.get_all_posts_sorted_by_rank
NoMethodError: undefined method `get_all_posts_sorted_by_rank' for Post (call 'Post.connection' to establish a connection):Class
If you want it to be a class method, you should use:
def self.get_all_posts_sorted_by_rank
...
end
It's probably what you want.
Otherwise, if you want an instance method, you should use the way you are, but you have to instantiate your object before:
#post = Post.new #or find(params[:id]) or some other way
#post.get_all_posts_sorted_by_rank #use it
But the first case seems more probable.

rails find_by_looking_in_every_field ...or... "why is my function missing?"

This is a two parter. I'd be happy with either of the approaches below or other suggestions.
I'd like to be able to retrieve records/objects using my model by passing it a search term and having it look for that search term in any field in the model, or any field that the model deems viable. So, as an example:
class Product < ActiveRecord::Base
...
def search_all_fields(search_term)
return search_term.length == 0 ? nil : Product.where("serial_number like :find_me", { :find_me => search_term })
end
end
This is from a Product model. The same function in the Company model might look like:
class Company < ActiveRecord::Base
...
def search_all_fields(search_term)
return search_term.length == 0 ? nil : Company.where("customer_number like :find_me or name like :find_me", { :find_me => search_term })
end
end
I would love a "railsy" way to do this, such as "find_by_looking_everywhere" but I haven't been able to find such a thing. I've found lots of suggestions for searching a single field for multiple values, but not searching multiple fields for a single value. So that's "Part 1," is there a "railsy" way to do this?
"Part 2" ... using the code above, why am I getting the following exception?
undefined method `search_all_fields` for #<Class:0xa38f2ac>
I'm calling the methods using #products = Product.search_all_fields("xy3445") or #companies = Company.search_all_fields("high")?? The trace shows that the exception is being raised by just a generic class. It doesn't say #<Product...> or #<Company...>
I'm a little lost... any and all help appreciated.
Thanks, gang.
Your method is an instance method (the Model need to be instanciated to access this method). You need a Class method (means you don't need an instance of Company to call it, like the methods where(), find() etc).
class Company < ActiveRecord::Base
def say_hello
return "Hello world!"
end
end
This method say_hello can only be called from an instance of Company (instance method):
company = Company.first
company.say_hello #=> "Hello world!"
# but this will raise a NoMethodError:
Company.say_hello #=> NoMethodError
In order to define a method as a class method, you can do the following:
class Company < ActiveRecord::Base
def self.say_hello
return "Hello world!"
end
# OR you can use the name of the model instead of the self keyword:
def Company.say_hello
return "HEllo World!"
end
end
Now you can do:
Company.say_hello
#=> "HEllo World!"
# but this will fail:
Company.first.say_hello
#=> NoMethodError

Rails Validation Error

While trying to add an error message using add_to_base, I am getting an undefined method 'errors' message. I am defining it in my model. Am I supposed to include any other file in order to access the errors variable.
Model File - I am defining it inside a method
self.errors.add_to_base("Invalid Name")
Error Message
undefined method `errors' for #<Class:0x0000010179d7a0>
I tried by calling it as errors.add_to_base("Invalid Name") also but still getting the same error.
Thanks.
you should call it in your callback method, something like following
def validate
if !self.interests.blank? && !self.interests.match("<").nil?
self.errors.add :base, 'Please ensure that Interest field do not contain HTML(< and >) tags'
end
end
I suspect that you have defined your method as a class method, instead of as an instance method.
Class methods look like this on ruby:
def self.checkFoo()
...
end
Instance methods looks like this:
def checkFoo()
...
end
Check that your checkFoo method is an instance method, and then use it like this:
class MyModel < ActiveRecord::Base
validate :foo
private
def checkFoo()
self.errors.add etc..
end
end
Typically used the validation callbacks, model errors are used both to cause the prospective database save to fail and to set up a contextual error messages for the end-user. The add_to_base variant is intended for general, non-specific error conditions (i.e. not associated with a particular model attribute).
class MyModel < ActiveRecord::Base
validate do |my_model|
if my_model.some_attribute.blank? # For example
my_model.errors.add :my_model, "must be filled in"
end
end
end
Subsequently
#my_model = MyModel.create(:some_attribute => "")
would fail and the #my_model.errors.full_messages array would contain
[ ..., "Some_attribute must be filled in", ... ]
There is however a shorthand for the above example as follows
class MyModel < ActiveRecord::Base
validates_presence_of :some_attribute, :msg => "must be filled in"
end
Looks like your 'self.errors.add_to_base("Invalid Name")' doesn't have any problem
But your model should inherit from ActiveRecord::Base
cheers
sameera

Resources