I have a name attribute on a Person model and every time I access the name attribute, I want name.capitalize to be returned.
Doing the following inside the model won't work,
def name
name.capitalize
end
so what is the alternative?
I suggest you to create a secondary method with your custom formatters.
class Person
def formatted_name
name.capitalize
end
end
This is a better solution compared with overwriting the default implementation because setter and getters might called when updating/writing/saving the record to the database.
I remember once when I overwrote the default implementation of an attribute and each time a record was saved, the attribute was updated with the formatted value.
If you want to follow this way you can use alias_method_chain or take advantage of inheritance including an external module.
class Person
def name_with_formatter
name_without_formatter.capitalize
end
alias_method_chain :name, :formatter
end
Also you can overwrite name and call read_attribute(:name) from within your custom method.
def name
read_attribute(:name).capitalize
end
def name
self[:name].capitalize
end
Again, don't do this. Go ahead and create a custom method.
But happens when name is null?
capitalize will throw an undefined method for nil::Class if self[:name] returns nil.
The following covers that:
def name
self[:name] ? self[:name].capitalize : nil
end
But I agree that you should create the formatted method and leave the name method as is. Never know when you might need the raw data.
FYI: the reason why your method didn't work was because it was causing, what I like to call, a self referring loop. You are redefining the method but calling the method in the new method. So you have to use self[:name] or read_attribute to get to the internal model data.
Try this:
def name
self[:name].capitalize
end
Related
I have a class method in my User model:
def self.method_name
...
end
In a controller, I need to call this method on a User instance obtained through association:
#user = game_play.player.user
As expected, it threw a no method error because it's a class method.
What is the way to call the method in this case?
EDIT: Adding code for question clarification
#user = #game_play.client.user.
#token = #user.set_login_bypass_token
My model:
def set_login_bypass_token
#We generate a raw token and an encrypted version of the same token
raw, enc = Devise.token_generator.generate(User, :login_bypass_token)
self.login_bypass_token = enc
self.login_bypass_token_set_at = Time.now
self.save(validate: false)
#Raw token is sent to the user via email to provide auto-login
raw
end
The error:
PG::UndefinedColumn: ERROR: column users.login_bypass_token does not exist
Notice the error has it as users.login_bypass_token instead of set_login_bypass_token
EDIT:
My first answer was before you mentioned Devise and assuming you didn't know if you needed a class or instance method. It's clear that your method must be an instance one.
I think you are trying to apply something you found here: https://stackoverflow.com/a/30857087/3372172
This requires that you add a new field to the users database, to manage the :login_bypass_token. Because you will use this column later to perform a find_by. Devise does not add this column to the database.
PREVIOUS ANSWER
If the method needs to access instance variables (which means it acts differently depending the specific object in the User class), it should be an instance method, defined without the self keyword.
If it is a class method, it cannot depend on any attribute from a specific object, and you cannot call it from an instance of the class.
You must decide if it's really a class method or an instance method.
`
If you need a class method to be called from an instance, you can do this (but I don't know why you could need it).
class User
def self.method_name
# blablabla
end
def method_name
User.method_name
end
end
Should be using an instance method instead of a class method. I'm not sure how you would get an error where it's looking for an attribute on the model since those can only be defined in the schema. If you're adding a regular instance method within the model it should work correctly.
So the goal is to turn for instance "ProductCustomer", which comes from the class, into "product customer".
I used to have this:
notification.notifiable.model_name.human.downcase
It didn't work out of course, since if the notifiable is nil it breaks. I don't
want to use try or something similar since it can be solved with using notifiable_type.
So now I changed to this:
notification.notifiable_type.split(/(?=[A-Z])/).join(' ').downcase
But this is way too complex to use every time in the view. So either I would like to define this as a view helper or using some ruby formatting method if there is a simple one.
Can somebody tell me what the Rails convention is in this case? If it's a helper, how does the method looks like and where should I put it?
Options:
Initializer
/your_app/config/initializers/my_initializer.rb
module MyModule
def human_model_name
self.class.to_s.tableize.singularize.humanize.downcase
end
end
ActiveRecord::Base.send(:include, MyModule)
Including MyModule in ActiveRecord::Base will add human_model_name in all ActiveRecord instances. So, you will be able to do...
user.human_model_name #=> user
notification.human_model_name #=> notification
notification.notifiable.human_model_name #=> product customer
any_active_record_instance.human_model_name #=> etc.
To avoid exceptions when notifiable is nil, you can use try method.
notification.try(:notifiable).try(:human_model_name)
A cleaner way can be use delegate
class Notification < ActiveRecord::Base
delegate :human_model_name, to: :notifiable, prefix: true, allow_nil: true
end
Then, you can do:
notification.notifiable_human_model_name # if notifiable is nil will return nil as result instead of an exception
A simple method in your Notification model
class Notification < ActiveRecord::Base
def human_notifable_name
return unless self.notifiable # to avoid exception with nil notifiable
self.notifiable.class.to_s.tableize.singularize.humanize.downcase
end
end
Then...
notification.human_notifable_name
View Helper (If you think this is a view related method only)
module ApplicationHelper # or NotificationHelper
def human_model_name(instance)
return unless instance # to avoid exception with nil instance
instance.class.to_s.tableize.singularize.humanize.downcase
end
end
Then, in your view...
<%= human_model_name(notification.notifiable) %>
Either option is fine. I would use one or the other depending on the case. In this case, I would use the first option. I think you are adding behaviour that can be useful in any model. I mean your method is not directly related with something about notifications. In a more generic way you want a method to return the class name of an ActiveRecord's instance. Today you want the model name of the notifiable ActiveRecord's instance. But, tomorrow you may want the model name of any ActiveRecord model.
To answer the question "Where should I put a method?" I suggest to break (without fear) a little bit the MVC pattern and read about:
Decorators, presenters and delegators
Services
(a little bit old, but you can get the idea)
"ProductCustomer".tableize.singularize.humanize.downcase
I am writing a gem rails4 where i can add dynamic attributes which wont persist in the actual model table, rather it will save in my second table with a reference.
Now i have before_validation to an instance method, where i am trying to set the default value to dynamically added attribute.
I am getting the error as
can't write unknown attribute
The code is
self[attr_name.to_sym]=attr_type[:default_value]
Kindly advice on this, how to do setters from the instance method for the non-existing fields.
I have used instance_variable_set for the same in ClassMethods.
You can use attr_accessor method for this purpose in your model.
class Sample << ActiveRecord::Base
attr_accessor :attr_name
#setter method
def attr_name=(val)
self[:attr_name] = val
end
#getter method
def attr_name
self[:attr_name]
end
end
Now, you can call any where instance method like following.
self[attr_name.to_sym]=attr_type[:default_value]
I would like to do something like:
class TestController < InheritedResources::Base
def test_method
self.var1 + self.var2
end
private
def test_params
params.require(:test).permit(:var1, :var2)
end
end
Where in the view I could call from the built in controller index:
test.test_method
I've tried adding a create method to the controller as follows:
def create
Test.create!(require(:test).permit(:var1, :var2, :test_method))
end
I've also tried updating the params directly:
private
def test_params
params.require(:test).permit(:var1, :var2, :test_method)
end
I've also tried making a helper method, but I knew that was doomed to fail because it wouldn't have access to var1 and var2.
I guess I just don't understand two things: one how to make my var1 and var2 white-listed so I can use them, and more importantly how to add a method to my model using strong parameters, because attr_accessible doesn't work in my models anymore.
EDIT:
Let me rephrase a little, maybe it will help. I can get access to individual Test objects in my view with a simple call to tests.each |test| in the view. I just want to make methods that act on my already defined active record variables for that object, hence var1 and var2. The problem is when I define a new method in my controller it is private to the object and I won't have access to it with a call from an instance of the object. Better yet, I would like to just be able to define a new variable, local to the object, that is created after it has propagated its other fields from the db.
EDIT2: I'm aware I'm probably missing the design pattern here. It can be hard to describe that I want X, when really I need Z. Thanks for the patience.
Thanks for the help.
There's no reason for white-listing parameters that you'll directly use.
White-listing with strong parameters is useful only when you call function like ActiveRecord#update that simply take every key from the dictionary, so you can control with key you want to allow and which not.
In this case, just do:
class TestController < InheritedResources::Base
def test_method
#result = params[:var1] + params[:var2]
end
end
And in your view, just print the #result variable wherever you want
<%= #result %>
This is the Rails way. You can of course call the variable as you want.
Helper methods are useful only for more complex cases.
Im trying to dynamically create a method chain in one attribute in my model.
By now I have this function:
def create_filtered_attribute(attribute_name)
alias_attribute "#{attribute_name}_without_filter", attribute_name
define_method "#{attribute_name}" do
filter_words(self.send("#{attribute_name}_without_filter"))
end
end
so I receive a string with the attribute name, alias it for '_without_filter' (alias_method or alias_method_chain fails here, because the attribute isnt there when the class is created),
and I create a new method with the attribute name, where I filter its contents.
But somehow, when I call "#{attribute_name}_without_filter" it calls my new method (i think because the alias_attribute some how), and the program goes into a stack loop.
Im trying to rename that attribute, so I can use its name for a method...
Can someone please enlighten me on this.
There is a difference between alias_method and alias_attribute. alias_method actually makes a copy of the old method, whereas alias_attribute just defines new methods, which call old ones.
Note, that model.attribute and model.attribute= methods in ActiveRecord simply call read_attribute and write_attribute, so you always can access your attribute, even if you override it's getter or setter:
define_method "#{attribute_name}" do
filter_words(self.read_attribute(attribute_name))
end