Rails: How do I unserialize from database? - ruby-on-rails

I am currently trying to save information for an invoice/bill. On the invoice I want to show what the total price is made up of. The procedures & items, their price and the qty. So in the end I hope to get it to look like this:
Consult [date] [total_price]
Procedure_name [price] [qty]
Procedure_name [price] [qty]
Consult [date] [total_price]
Procedure_name [price] [qty]
etc...
All this information is available through the database but i want to save the information as a separate copy. That way if the user changes the price of some procedures the invoice information is still correct. I thought i'd do this by serializing and save the data to a column (consult_data) in the Invoice table.
My Model:
class Invoice < ActiveRecord::Base
...stuff...
serialize :consult_data
...
end
This is what I get from the form (1 consult and 3 procedures):
{"commit"=>"Save draft", "authenticity_token"=>"MZ1OiOCtj/BOu73eVVkolZBWoN8Fy1skHqKgih7Sbzw=", "id"=>"113", "consults"=>[{"consult_date"=>"2010-02-20", "consult_problem"=>"ABC", "procedures"=>[{"name"=>"asdasdasd", "price"=>"2.0", "qty"=>"1"}, {"name"=>"AAAnd another one", "price"=>"45.0", "qty"=>"4"}, {"name"=>"asdasdasd", "price"=>"2.0", "qty"=>"1"}], "consult_id"=>"1"}]}
My save action:
def add_to_invoice
#invoice = #current_practice.invoices.find_by_id(params[:id])
#invoice.consult_data=params[:consults]
if #invoice.save
render :text => "I think it worked"
else
render :text => "I don't think it worked'"
end
end
It does save to the database and if I look at the entry in the console I can see that it is all there:
consult_data: "--- \n- !map:HashWithIndifferentAccess \n consult_da..."
(---The question---)
But I can't seam to get back my data. I tried defining a variable to the consult_data attribute and then doing "variable.consult_problem" or "variable[:consult_problem]" (also tried looping) but it only throws no-method-errors back at me.
How do I unserialize the data from the database and turn it back into hash that i can use?
Thank you very much for any help!

I think you should determine class of object
IE:
serialize :consult_data, Hash
So then
invoice = Invoice.last
invoice.consult_data[:date] # will return date
etc

Related

accessing multiple values from hash ruby on rails

This code snippet
room = Room.find(roomId)
returns a single column from room table, the returned value contains multiple attributes, like id, name, description, duration.
I want when coding
render json: room
to return only duration and name.
Do i have to write something like that
render json: room[:duration, :name]
query that will only give you the attributes that you want :
room = Room.select(:id, :duration, :name).find(room_id)
You can use the only option of as_json to include only certain attributes.
render json: room.as_json(:only => [:duration, :name])
You're slightly incorrect when you say Room.find(roomId) returns a "single column from the room table". It actually returns a single row.
Anyway, there are a few ways you can do this.
A good starting point is to use the .attributes method on a model to convert it to a Hash.
So for example say you have a user with a name, email, and id columns in the database. You can call user.attributes to get
{ "name" => "max", "email" => "max#max.com", "id" => 5 }.
you can select just the name and email with
user.select { |k,v| k.in?("name", "email") } and then call to_json on the result.
To avoid having to write custom to_json code for each controller response, you can overwrite .attributes in the model. Say my User table has a password column that I never want to expose in my API:
class User
def attributes
super.reject { |k,v| v.in?("password") }
end
end
Then, if you need to return a list of users as JSON:
render json: { users: #users.map(&:attributes) }.to_json, status: 200
The answer by Pavan will work as well, by the way.
Another approach is to use jbuilder and create json "views" for your records.

Rails multilinguism stored in DataBase (translation by the end-user)

We are having a dilemna about a feature we need to implement. We want to support n languages in our app (0 < n < +infinity).
We decided to go with the solution below:
module TranslatedAttributes
def use_translated_attributes
has_many :translated_attributes, as: :owner
accepts_nested_attributes_for :translated_attributes
define_method :translate_attribute do |attribute, locale = I18n.locale|
TranslatedAttribute.where(attribute_name: attribute.to_s, owner_type: self.class.model_name, owner_id: self.id, locale: locale.to_s).first.try(:translation)
end
end
end
The User should be able to define X translation(s) for an instance of the Product model for example. Also, depending on the user's locale set in his profile, he will see the translated version of the attribute.
Example:
Product
id: 12
TranslatedAttribute
attribute_name: 'name'
owner_id: 12
owner_type: 'Product'
locale: 'en'
translation: 'Magnificent shiny shoes'
TranslatedAttribute
attribute_name: 'name'
owner_id: 12
owner_type: 'Product'
locale: 'fr'
translation: 'Magnifiques chaussures brillantes'
In the view, it would be called like this:
product.translate_attribute(:name)
# or we will eventually define_method for each attribute to be translated
# so we could use the following
# product.name
This works, already tested.
The problem is when we will try to load tons of records, each one needing to query the DB to know the proper translation to display.
My question is: How would you handle a CACHE about this?
Another question I have is: is there another problem you see that I might not see so far? Also, I thought about accepts_nested_attributes_for :translated_attributes to build the translation's form with fields_for. Do you think it is a bad idea to handle it like this?
Thanks you!
You could use something such as the globalize3 gem to implement this feature.
A first optimization could be to fetch all the translations in the desired locale[1] for a Product instance at the first call to translate_attribute, and cache them in an instance variable.
This way, the number of requests for translations would be reduced to just one by Product instance.
Quick example:
define_method :translate_attribute do |attribute, locale = I18n.locale|
locale = locale.to_s
#attributes_translations_cache ||= {}
#attributes_translations_cache[locale] ||= Hash[
self.translated_attributes
.where(locale: locale)
.map do |translated_attribute|
[translated_attribute.name, translated_attribute.translation]
end
]
#attributes_translations_cache[locale][attribute]
end
I think that it should also be possible to join or at least include the translation to the Products in some way, but I haven't given much thought to this idea yet. I'll try to update this answer.
[1] This assumes that you only use a single locale in a given page, but you could also fetch all the locales at the same time, or any combination of locale and attributes.

Ruby - how to create dynamic model attributes?

I have an array with model attributes (these model attributes are columns in DB table). I am trying to iterate through this array and automatically create a record which I would like to save to DB table, something like this:
columns.each_with_index do |c, i|
user.c = data[i]
puts user.c
end
user is model.
But if I try the snippet above, I get
undefined method `c=' for #<User:0x007f8164d1bb80>
I've tried also
columns.each_with_index do |c, i|
user."#{c}" = data[i]
puts user."#{c}"
end
But this doesn't work as well.
Data in columns array are taken from form that sends user, so I want to save only data that he send me, but I still cannot figure it out...
I would like to ask you for help... thank you in advance!
user.send("#{c}=".to_sym, data[i])
Also, you can access the attributes as a hash.
user.attributes[c] = data[i]
The best thing would probably be to build a hash and to use update_attributes:
mydata = {}
columns.each_with_index{|c, i| mydata[c] = data[i]}
user.update_attributes(mydata)
this way you retain the protections provided by attr_accessible.
If this is actually in a controller, you can just make use of some basic rails conventions and build the User record like this:
#user = User.new(params[:user])
if #user.save
# do something
else
# render the form again
end
Although you can set the values using send, I agree with #DaveS that you probably want to protect yourself via attr_accessibles. If your planning to use Rails 4, here's a good overview.

Should I symbolize keys?

1) I am grabbing some records for the DB in HAML to display, and the attributes method on each row returns a hash. The hash's keys are strings. Should I turn those keys into symbols? I am not sure the call to symbolize_keys is worth it. I.e.,
%td #{app['comment']}
or
%td #{app[:comment]
2) I am trying to symbolize the array of hashes I return, but it is not working:
rows = Comment.all(:order => 'created DESC')
result = rows.each_with_object([]) do |row, comments|
comments << row.attributes.symbolize_keys
end
Is it not actually pushing the symbolized hash into the comments array? I also tried symbolize_keys!, and that did not help. What am I doing wrong?
Since you're using Rails, you have access to HashWithIndifferentAccess so you can bypass your "strings or symbols" issue quite easily by allow both:
h = HashWithIndifferentAccess.new(some_model.attributes)
puts h['id'] # Gives you some_model.id
puts h[:id] # Also gives you some_model.id
Your each_with_object approach:
result = rows.each_with_object([]) do |row, comments|
comments << row.attributes.symbolize_keys
end
should work fine so I think your problem with that lies elsewhere.
Do you have a reason for using ActiveRecord::Base#attributes[your_attribute] instead of ActiveRecord::Base#your_attribute directly? You didn't mention a reason.
ActiveRecord::Base automatically sets up accessors for your database fields:
object = Model.new
object.your_column = "foo" # Writer
object.your_column # Reader
You should be able to use the reader in your views instead of accessing the value through ActiveRecord::Base#attributes.
Update:
I'm not sure if this is what confuses you.
Comment.find(:all) already retrieves all columns values for those rows in your database and stores them in your Comment objects (which we assign to #comments below). The values are already stored in your Comment objects, so you may already use them in your views as you please.
In your controller, if you have:
def index
#comments = Commend.find(:all) # Fetch columns and rows.
end
you can do this in your HAML view:
- #comments.each do |comment| # Iterate through array of Comment objects
%tr
%td= comment.comment # Use value for "comment" column.
you can add hook, which symbolizes keys after model load:
class YourModel < ApplicationRecord
after_initialize do |rec|
attributes["some_json_field"].symbolize_keys! if attributes.key? "some_json_field"
end
end

How to get changed values on save

Trying to get previous values from the object on save. Think of this scenario:
#object = {:name => 'Dan', :occupation => 'student'}
#object[:occupation] = 'Full time employee'
#object.value_was[:occupation] # => 'student'
I hope it is understandable enough that there is no method value_was. More over I would like to do the same on model objects:
#student = Student.find(1)
#student.occupation = 'Full time employee'
#student.save
#student.value_was(:occupation) # => 'student'
Any help will be appreciated.
That would be really helpful
ActiveModel includes support for "dirty field marking", which preserves before and after states for changed fields.
You can use #student.occupation_was to get the previous value of occupation, and #student.occupation_changed? to get whether the value has changed or not.
This only works BEFORE the save, as saving resets the changed states of the values. However, you could capture this data in a before_save callback if you need to use it after a record has been saved. You can preserve all of the changes by duplicating #changed_attributes in a before_save, for example, then query on them.

Resources