Multi value attributes in Rails ActiveRecord model? - ruby-on-rails

I am having a Property model that should contain multiple values (just Strings). With Rails/ActiveRecord it seems that I have to create a new model (and a new table) for those values (like PropertyValue). As every one of those values just stores one String the PropertyValue only need one attribute (like value).
I don't like that idea cause to access one of those values I now have to call property.values[0].value and that looks a bit ugly.
Is there a nicer solution?

Try serialize method
class Property < ...
serialize :value, ::Array
end
value array will be stored as string in properties table and you can access it as normal array: property.value[3].
More details in docs.

Related

Global array for ruby on rails

I want to include an array which stocks the information of a model. Whenever an object of model A is created, the attribute name of this object is added in the array and this array won't be re-set when I restart the server. I try put the array to the associated model class and controller, but it does not work. Can you tell me how to do this?
I think what you want is every name of every ModelA that is stored in the DB?
According to #Leito, this would work:
ModelA.all.pluck(:name)
Maybe:
Model.all.uniq.pluck(:name), which translates to SELECT DISTINCT name FROM models

mongoid pluck - not returning correct values with default values in model

I have a User model where there is a department field which has a default value of "Engineering".
This field was introduced 2 months after our website went live,and since its mongo, there was no migration.
When I try to get the object using where, correct value is returned
User.find_by(:name => "John).department
However, if I try to pluck values, it returns nil and not the default value.
User.limit(2).pluck(:department)
returns
[nil,"Finance"]
I researched a bit and came across this blog post http://ahmadsherif.com/blog/2013/01/29/mongoid-default-fields-can-give-you-hard-time/
I think I am facing the same issue. Is there any work around for this? I chose to go with pluck because it's not memory intensive and saves time.
Basically the behaviour here can be explained by the difference in how MongoDB deals with default values vs a traditional relational database like Postgres.
In the SQL world you set defaults via the database schema and the DB will fill a NULL field everytime you insert a row.
Since MongoDB is schemaless document fields have a default value of nil, which cannot be changed* since there is no schema where we could define defaults on the database level. Instead defaults are implemented on the application level. For Mongoid this means when you initialize a new model instance it will fill in default values if they are nil.
In ActiveRecord terms it would look like this:
class Thing < ActiveRecord::Base
after_initialize :set_default_foo, if: -> { self.foo.nil? }
private
def set_default_foo
self.foo = "bar"
end
end
However if you have existing documents and add a new field or defaults to an existing field Mongoid does not update the existing documents for you!
So how does this explain these two cases?
User.find_by(:name => "John").department
User.limit(2).pluck(:department)
In the first case you a pulling a document out of storage and using it to inialize a model instance. When the model instance is initialized a callback is run which sets the default values.
When .pluck is called on the hand Mongoid pulls the values directly from the store without initializing any model instances. Thus for any "legacy" documents it will return a nil value.
To remedy this you you need to set the default value for any document with a nil.
User.where(department: nil).update_all(department: 'engineering')

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.

How do i add an array to an activerecord class in rails

I understand how to store lots of datatypes in the model, but I'm not sure how to add an integer array.
You can use serialize
class YourModel < ActiveRecord::Base
serialize :array
end
ActiveRecord will automatically serialize the content of array to database and back.
My understanding is that in general, it's not best to try and store arrays in the database, especially for data that doesn't really need to be in the array form. My suggestion would be to write some kind of helper method in your model that converts an array to a string of comma separated values using the join method (or even just converting the array to a string using to_s). You could then write a wrapper method for your model so that whenever you call the method to retrieve the array field, it converts the string result back into the array form. So all of the code necessary to handle converting to a string is abstracted, and the database representation is a little simpler. But yeah, serialize works too. Hope that helps!

Ruby on Rails 2.3 populating and validating non-AR models from form params

I have a ActiveRecord model which contains a serialized field which consists of an Array of a custom non ActiveRecord model (a simple class, with a couple of attributes).
I'm trying to figure out a way to integrate population and validation for each of the serialized items together with the parent model but can't find a elegant way for doing so.
The serialized model "initialize" method receives an "attributes" hash, and simply populates the class attributes with those. This is just fine, beside the fact that when receiving the data from a form, the data is received as a string data rather than an actual type.
ActiveRecord model knows how to deal with this, and does typecasting based on the Schema before
the model instance is saved to the db. Is there anything I can do (beside typecasting right in the "initialize" method) to make the behavior similar to the AR models?
Also, I'm trying to use the "Validatable" gem for validating my NON AR-Model, but seems like there are validations which cannot be defined there like, when using "validates_numericality_of" and defining that the number is bigger than 0.
There is the "if" parameter which can be passed to to it, but it occurs before the numericality checking and then when executing:
validates_numericality_of :price, :if => lambda { self.price >= 0.0 }, :message => "Price must be greater or equal to 0"
If I entered a string inside the "price" field, when I try to check if the model is valid, I get an exception thrown since "price" is a string and i'm trying to compare it to a number...
hope it's ok I've asked two questions in one thread... but they're kind of related.
Thanks
Have you tried self.price.to_i?

Resources