Why is attr_accessor necessary in Rails? - ruby-on-rails

I occasionally see attribute accessors/readers/writers in the code for models. Are these necessary if I want to be able to update attributes from the view / controller code?
I am a beginner so I am really talking about basic applications.

attr_accessor is a core feature of Ruby and is used to generate instance variables with getter and setter methods. Its use is never required in basic Ruby (it's a convenience).
In the case of ActiveRecord models, getters and setters are already generated by ActiveRecord for your data columns. attr_accessor is not needed or desirable.
If you have additional instance data you don't need to persist (i.e. it's not a database column), you could then use attr_accessor to save yourself a few lines of code.
The similarly-named attr_accessible — which is frequently seen in Rails code and confused with attr_accessor — is a deprecated method of controlling mass assignment within ActiveRecord models. Rails 4 doesn't support it out of the box; it has been replaced by Strong Parameters, which allows more granular control.

If you declare an attr_accessor then you can use it as a virtual attribute, which is basically an attribute on the model that isn't persisted to the database.
Example case: you declare attr_accessor :password in your User model so that you can use it as a field in a new user form. When you receive their password in the corresponding create action, you can derive a hashed_password, persist it to the database, and discard the given password (which is done automatically at the end of the request).

Generally it is a pretty good idea to decorate attr_accessor for anything on a model that is not an actual column in the SQL table. Rails 4 or not. This gives you clear understanding of what's in the model and what is persisted.

Generally, i use attr_accessor for attributes that is not in model/database, but i think it's not necessary using them.

Related

Wrapping an ActiveRecord object as a PORO. How do get all the attributes?

I'm trying to wrap an AR object as a PORO as part of an API response for the purpose of preventing the client application from calling .save on the object. However, I want all the attributes and this object has like 50 columns. Is there an easy way to create attr_accessors for all of the attributes?
Say this is the beginning of my class:
module Something
class Apple
attr_accessor [...]
Say the AR model is also called Apple.
What goes inside there? Is there a way for me to quickly get all the attributes of the AR Apple as attr_accessors?
I wouldn't bother. It'd be simpler to use the readonly active record method.
docs
Apple.readonly.first
Apple.readonly.where(display: true)

How to handle a single form to update multiple unrelated models?

I am working on a legacy Rails 3.2 application that has a lot of settings a user can manage. Settings are associated with 3 types of model in the system: User, Company and CompanyUser. In order to avoid having to write database migrations each time a new type of setting is added
I've essentially created a key/value store (1 row for each setting) that has a polymorphic association with each of the above mentioned models. A base Setting class handles all of the common functionality like setting the key, relationships etc. each type of setting extends the base class and can contain it's own validation and/or logic. For example:
class Settings::EmailSignature < Setting
validates :whatever
end
For any model that requires a setting I've implemented a has_setting helper method that sets up the association and provides some delegates to directly get and set the setting without needing to go via the associated model object, the User model might look like:
class User < ActiveRecord::Base
has_setting :email_signature
end
This side of the code is working well, however the problem I have is when I create the form for the settings. For the user it might make sense to have User, Company and CompanyUser settings mixed together in the same form. Using nested attributes doesn't feel like a good solution in this situation as the settings are not related and there is no common parent object. I've considered using a form object to handle mapping each setting to the correct object but that doesn't feel like a great option either as each setting would require knowing it's id, the associated records id and it's type. This would not be particularly easy to manage when building the form.
I'm about to go down the route of having each setting in it's own form and having the record save automatically as the user edits each item. This would mean only a single record is ever saved at a time and will make things much simpler at the controller layer and also provide a lot of flexibility in how settings a grouped. Before I go down this route I wanted to see if there are any other options for submitting a single form in a single transaction that I may have overlooked?
Please note, this application is written in Rails 3.2 and is not in a state in which it can be easily upgraded to Rails 4 right now so any solutions need to work with Rails 3.2.

Hash getter and setter on ActiveRecord object

I have an ActiveRecord object that was serializing a hash property to one of my database columns. I'd like to get away from this since querying by one of the hash keys is very difficult/not clean. So I've split all the keys of the hash up into separate properties on the model. However, I have a lot of places using this code so in the meantime while I convert everything I'd like to have a property on my ActiveRecord object that is for Rails only (i.e. it doesn't populate back to my database) that wraps up those properties into a hash like it used to be and allows values to be set and get.
So for instance, this is what I used to have:
class MyCls < ActiveRecord::Base
serialize :state, Hash
attr_accessible :id, :mode
I'm getting rid of :state and replace it with 7 different values that made up this hash. But I'd still like to be able to access those values like this: MyObj.state[:obj_num]. Even though I now have obj_num as a property (i.e. MyObj.obj_num). I'm thinking the best way to do this would be to have a state property with a getter and a setter, but I can't quite seem to get the syntax right. For the setter I'd need to support both setting the hash as a whole and setting individual keys.
First to say: I dont think thats the best solution. When you touch this code again in lets say 3 years it will be like "WHAAAAAT HAVE I DONE?"... It whould the best solution to replace all the snippets in your code with other code.
You can prepend the method_missing method of the object after removing this line serialize :state to fetch all calls that want to access the not anymore existing serialized field of the object. Its explained here:
http://blog.enriquez.me/2010/2/21/dont-forget-about-respond-to-when-implementing-method-missing/
Its called metaprogramming. Thats the "rails magic" that makes all the find_by_attribute_name stuff working without defining each of these methods. Can be cool stuff but you need to be very carefull and you need to know what your doing.

Rails ActiveRecord method to list attributes including associated virtual attributes

Is there a method to list all of an active record class's attributes from database column names as well as attributes from associations?
Foo.attribute_names doesn't include virtual attributes generated from associations
Foo.new.methods does include these (as well as a huge list of unrelated methods), but I'd like a more pared down list of methods that are only related to ActiveRecord attributes if possible.
Is there a correct way to do this that I'm overlooking?
In my book, the correct way is to use attr_accessible to define accessible attributes. Then on your model you can simply call Foo.accessible_attributes and get a nice list.
However, associations are trickier, but you can do something like:
Foo.accessible_attributes.to_a + Foo.reflect_on_all_associations.map(&:name)
If you aren't using attr_accessible you'd have to hack it together with
Bundle.new.attributes.keys - Bundle.protected_attributes.to_a
I don't know of exact Rails method for your case, but you can get around by doing something like..
Model.attribute_names + Model.reflect_on_all_associations.map {|a| a.name.to_s }
The second part will give you associated model methods as strings in an array.

What role do ActiveRecord model constructors have in Rails (if any)?

I've just been reading this question which is about giving an ActiveRecord model's date field a default value. The accepted answer shows how to set the default value from within the controller. To my mind, this sort of business logic really belongs in the model itself.
Then I got to thinking how if this were Java I'd probably set the initial field value when declaring the instance variable or within the constructor. Since database-backed fields don't have to be explicitly declared within ActiveRecord models, is this something that you could use the model's initialize method for? I'm curious because I've not really seen much use of constructors for ActiveRecord models within the Rails code that I've looked at. Do they have a role to play and if so, what is it?
I do this quite often actually for default values. It works well and still lets the user change it. Remember, the initialize method is called when you say MyObject.new. However, you may want to read this blog entry (albeit a bit outdated) about using initialize.
You should use after_initialize instead of initialize. The initialize method is required by ActiveRecord::Base to prepare many of the convenience methods. If an after_initialize method is defined in your model it gets called as a callback to new, create, find and any other methods that generate instances of your model.
Ideally you'd want to define it like this:
def after_initialize
#attribute ||= default_value
end
Also note, you cannot use this callback like the others, you must define a method named after_initialize (like above) for it to work. You can't do the following:
after_initialize :run_some_other_method
#TopherFangio's answer is correct. It seems that the ActiveRecord API changed some time between his answer (2009) and now (2015).
As of today (Rails 4 with ActiveRecord 4.2.0), here's how you add initializers according to the ActiveRecord docs:
class Widget < ActiveRecord::Base
after_initialize |new_widget|
new_widget.name ||= 'Unnamed Widget'
end
end
You can verify with puts statements or by inspecting the new object from rails console that it actually initializes correctly.
According to this blog, active record doesn't always use new, so initialize might not be called on your object.

Resources