In Rails, serialized column loaded as String when it is in association - ruby-on-rails

I have a model Model that have a serialized column named data.
This model is in associatoin with other model named SuperModel.
When I want to do validation on :models, that serialized item returns String instead of hash. But when using Model.data it whorks normally and returns a hash. see below ...
class SuperModel < ActiveModel::Base
has_many :models, JSON
validate :my_validation
def my_validation
self.models #returns "{\"attr\": \"1\"}" instead of hash {"attr": 1}
end
end
class Model < SuperModel
serialize: data
end
It also works when calling self.models.reload in my_validation but in case of editting an instance of super_model, all changes get lost when calling reload.

Related

Rails model -- non persistent class member or property?

Is it possible / advisable to have a member of a class that is not persisted to the database for a rails model?
I want to store the last type the user selects in a session variable. Since I cant set the session variable from my model, I want to store the value in a "dummy" class member that just passes the value back to the controller.
Can you have such a class member?
Adding non-persisted attributes to a Rails model is just like any other Ruby class:
class User < ActiveRecord::Base
attr_accessor :someattr
end
me = User.new(name: 'Max', someattr: 'bar')
me.someattr # "bar"
me.someattr = 'foo'
The extended explanation:
In Ruby all instance variables are private and do not need to be defined before assignment.
attr_accessor creates a setter and getter method:
class User < ActiveRecord::Base
def someattr
#someattr
end
def someattr=(value)
#someattr = value
end
end
There is one special thing going on here; Rails takes the hash you pass to User.new and maps the values to attributes. You could simulate this behavior in a plain ruby class with something like:
class Foo
attr_accessor :bar
def initialize(hash)
hash.keys.each do |key|
setter = "#{key}=".intern
self.send(setter, hash[key]) if self.respond_to? setter
end
end
end
> Foo.new(bar: 'baz')
=> <Foo:0x0000010112aa50 #bar="baz">
Classes in Ruby can also be re-opened at any point, ActiveRecord uses this ability to "auto-magically" add getters and setters to your models based on its database columns (ActiveRecord figures out which attributes to add based on the database schema).
Yes you can, the code below allows you to set my_class_variable and inside the model reference it as #my_class_variable
class MyCLass < ActiveRecord::Base
attr_accessor :my_class_variable
def do_something_with_it
#my_class_variable + 10
end

ActiveRecord serialize attribute not returned as a 'hash' but rather a string

I have a table/class :
class User < ActiveRecord::Base
serialize :photos
end
When I query, via something like this:
Post.joins(:user).select('users.username, users.photo AS user_photo')
This returns the photo, but not as a 'hash' but rather as the raw string stored in the database:
{
"post_type" = 0;
"user_id" = 1;
"user_photo" = "---\n:photo: http://res.cloudinary.com/jhess2991/image/upload/eb29c343249624.png\n:thumb: http://res.cloudinary.com/jhess2991/image/upload/c_scale,h_180,w_180/eb29c3423429624.png\n";
username = HDILOfficial;
}
Of course, this only happens when I query from a ActiveRecord class other than User. If I query from User (User.select(blah blah blah)) then it returns as a normal hash.
So my question is, how can I make it treat 'photo' as a hash when querying from another class?
At the time being, you can't. There is no way for ActiveRecord to know that the user_photo references the value of the photos attribute you defined in the User model.
Did you see the information an api.rubyonrails.org
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Saving+arrays%2C+hashes%2C+and+other+non-mappable+objects+in+text+columns
Saving arrays, hashes, and other non-mappable objects in text columns
Active Record can serialize any object in text columns using YAML. To do so, you must specify this with a call to the class method serialize. This makes it possible to store arrays, hashes, and other non-mappable objects without doing any additional work.
class User < ActiveRecord::Base
serialize :preferences
end
user = User.create(preferences: { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }
You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a descendant of a class not in the hierarchy.
class User < ActiveRecord::Base
serialize :preferences, Hash
end
user = User.create(preferences: %w( one two three ))
User.find(user.id).preferences # raises SerializationTypeMismatch
When you specify a class option, the default value for that attribute will be a new instance of that class.
class User < ActiveRecord::Base
serialize :preferences, OpenStruct
end
user = User.new
user.preferences.theme_color = "red"
This may helps

Rails model how to reference column in a class method

I currently have 2 models - Issue and Responses and i'm trying to add a method on the response model so I can do issue.responses.latest
I'm currently getting undefined method 'issue_id' for #<Class:...
How can I reference the column issue_id from the responses table in my self.latest method?
class Issue < ActiveRecord::Base
has_many :responses
end
class Response < ActiveRecord::Base
belongs_to :issue
def self.latest
select([:id, :user_id, :issue_id, :response, 'MAX(created_at)'])
.where(:issue_id => self.issue_id) <!-- How to reference the issue_id column here?
.group(:issue_id)
end
end
You're referencing the issue_id column fine. The problem is that you're trying to provide a value of self.issue_id, but there is no issue_id class method of Response.
Also, a latest class method for Response won't be invoked by issue.responses.latest, since responses is an <ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_Collec‌​tionProxy_Table:0x007f85220bd3b8>. You could, however, define an Issue instance method of latest_response.
On a related point, since responses is an Enumerable, you can do issues.responses.max_by {|response| response.created_at}

Custom serialization for fields in Rails

Is there a way to have a custom serialization for fields in rails, a method that runs when a field is saved and loaded to convert from/to a string which is what ultimately is saved on the database.
Specifically what I want to do is have a field of type symbol like gender, with possible values :male and :female storing "male" and "female" on the database. There are some workarounds, like:
def gender
read_attribute(:gender).try(:to_sym)
end
but that leaves obj.attributes unchanged, so it's a leaky abstraction.
You can do it in Rails 3.1. The object you want to serialize has to reply to load and dump methods.
Here is an example of serializing a string in Base64.
class User < ActiveRecord::Base
class Base64
def load(text)
return unless text
text.unpack('m').first
end
def dump(text)
[text].pack 'm'
end
end
serialize :bank_account_number, Base64.new
end
For more details see: http://archives.edgerails.info/articles/what-s-new-in-edge-rails/2011/03/09/custom-activerecord-attribute-serialization/index.html
def whachamacallit
read_attribute("whachamacallit").to_sym
end
def whachamacallit=(name)
write_attribute("whachamacallit", name.to_s)
end
store them as stings in the database, but extract them as symbols when you pull them out then convert back before you save.
would work with any number or combination of strings / symbols.
to limit it to only a select few
validates_inclusion_of :whachamacallit, :in => [ :male, :female, :unknown, :hidden ]
From http://blog.quov.is/2012/05/01/custom-activerecord-attribute-serializers/
class Recipe < ActiveRecord::Base
serialize :ingredients, IngredientsList
end
class IngredientsList < Array
def self.dump(ingredients)
ingredients ? ingredients.join("\n") : nil
end
def self.load(ingredients)
ingredients ? new(ingredients.split("\n")) : nil
end
end
you can define the models to_xml for a model and it will do that
http://api.rubyonrails.org/classes/ActiveRecord/Serialization.html
its possible to define Marshall.dump and put in that way i think, but its something to look into
You could use serialize method inside the model. Please reference to this link:
http://api.rubyonrails.org/classes/ActiveRecord/Base.html
(ps. search keyword "serialize" in that page ;D)
In short, you could do this:
class YourModel < ActiveRecord::Base
serialize :db_field
end
Rails would automatically serialize the field before saving to database, and deserialize it after fetched from the database.
well for just male/female you could just do a Boolean column like male and if it was false assume that meant female, add wrapper methods for it
def female?
return !self.male?
end
We just released a gem (AttributeHelpers) that does exactly this. Disclaimer: I am a maintainer for the gem.
It allows you to call attr_symbol :gender in your class definition and the serialization happens automagically.

Why won't Rails deserialize my field?

I am using the Classifier:Bayes as part of a model class. I have the class set up to serialize the classifier to the db.
class Foo < ActiveRecord::Base
serialize :classifier
end
The yaml appears in the db just fine after doing some training and saving the object.
But when I query for the class, instance.classifier is a string
#f = Foo.find(params[:id])
#f.classifier.class # is String
I was under the impression that Rails / ActiveRecord would magically deserialize my classifier for me. Is there some setting I need to tweak or am I misunderstanding something?
In the past, I've had to add the class name to the method args...
class Foo < ActiveRecord::Base
serialize :classifier, Classifier::Bayes
end

Resources