Hash getter and setter on ActiveRecord object - ruby-on-rails

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.

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)

Is it possible to add a variable to an object in rails?

I'm really frustrated with rails because of a ton of little problems like this.
I have a model called timelines. In it is a datetime column but the javascript that I'm passing the data too is expecting a date only. So I want to add a new string to my model object before passing it to the view. This seems impossible.
First I gather the data from the database and put it into an instance variable:
#timeline = Timeline.where(token: params[:token]).first
Then I try to add on to that variable like this:
#timeline.birthday = "1/1/2000"
I get an error that says:
undefined method birthday= for #<Timeline:0x00000105add0d0>
I'm guessing it's saying this because birthday is not a column in my database. I know it's not, and I don't care that it's not in my database. I just want to add onto my variable. Is this possible?
You need to add accessor methods to be able to get/set birthday. It won't be persisted, though.
class Timeline
attr_accessor :birthday
end
Alternative solution
The underlying problem you seem to be having ("javascript expects something else") can be solved in a couple of ways. Presenter pattern, for example. Or use ActiveModel Serializers.
This will allow you to keep the model as is, and, upon rendering, transform data to format expected by client-side.
You could convert the original datetime column to date by using:
your_column_name.strftime("%d/%m/%y")

Why is attr_accessor necessary in 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.

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.

Rails ActiveRecord::Base remap column names programmatically

Given a table with fields like title, title_sp, title_jp, and knowing a particular language value for a user (like jp), what would be a method by which to remap table fields to model attributes at runtime. Something like setting alias_attribute on a per invocation basis in find(*args).
Something along the lines of:
Posts.find(:all, :conditions => {:published => true}, :language => "jp")
and have the returned Posts.title be populated by the value in title_jp. added ideal pointer would also have it able to fall back to title/title_en if title_jp is nil/empty.
I've been digging around overriding .find, but not able to sort out how to bulk remap the field names.
It is dangerous to do such thing. You are able to do this with some find_by_sql, but if you persist the object back, you will propably alter the original title, because the model won't know, that it is not the original value.
The better solution is to create a virtual attribute, which selects the best column to return, based on the parameter. It may be a bit problematic to use, but it is much more safer this way. It is not so hard to write a wrapper, so a whole collection can be accessed with a single call, and setting the virtual attribute in each to the right value.
If you have more than one field to override, you can write a small solution to create a virtual attribute to each. If you need more on this, just let me know!

Resources