good way to handle a bunch of data in a hash - ruby-on-rails

I'm returning a complex result of indeterminate size that I will need to handle again and again, so I'm wondering what is a good way to package it?
something like this
loop>>>
#results = { external_id => { :name => name, :type => type } }
or
#results = [ { :external_id => external_id, :name => name, :type => type } ]
or?
end>>>>
and if it ends up being a hash of a hash, do i just use merge?
Thanks

I ended up with an array of a hash... and it works fine.

Related

Ruby change hash to single layer with square brackets

I've got a hash and I've found that with net/http posting I have to convert it into a flat format.
Example
invoice = { :no => "100", :date => "08/08/2022", :client => {:name => "Foo" } }
Would become
params = { "invoice[no]" => "100", "invoice[date]" => "08/08/2022", "invoice[client][name]" => "Foo" }
Is there a way to do this automatically? I've tried to_param & to_query, flatten and encode_www_form but they don't convert it to this required format.
The post action I'm doing is to a Ruby On Rails backend which I use Devise Tokens to authorise.
res = Net::HTTP.post_form(uri, params)
You need CGI.parse method. It parses an HTTP query string into a hash of key => value pairs
CGI.parse({ invoice: invoice }.to_query)
# => {"invoice[client][name]"=>["Foo"], "invoice[date]"=>["08/08/2022"], "invoice[no]"=>["100"]
Don't care about single-element arrays as values. It will works well
params = CGI.parse({ invoice: invoice }.to_query)
res = Net::HTTP.post_form(uri, params)
I think this snippet should do the job:
invoice = { :no => "100", :date => "08/08/2022", :client => {:name => "Foo" } }
CGI.unescape({invoice:}.to_query)
.split('&')
.map{ |p| p.split('=') }
.to_h
{"invoice[client][name]"=>"Foo", "invoice[date]"=>"08/08/2022", "invoice[no]"=>"100"}
First of all, we let ActiveRecord generate the query-like structure from a hash using the method to_query. We need to unescape the query string afterward since we don't want to have URL-encoded output there. After that we split the string by parameter using split('&') and every parameter into key-value using split('='). Finally, we convert the output back into a hash.

Rails - JSON object with an array?

I'm able to create and send a JSON object like so:
#mylist << {
:id => item.id,
:name => name.id
}
render :json => { :result => 'success', :mylist => #mylist }
That works great. Problem I'm having now is that I need to include users with are 1 or more per item.
#mylist << {
:id => item.id,
:name => name.id,
:users => item.users
}
Where item.users contains a list of (user.id, user.name, user.desc).
how do I include an array like users inside a json object? How to build in Rails and then how to parse it with jQuery?
Thanks
UPDATE
the code above is inside a:
#items.each_with_index do |item, i|
end
Perhaps that is a problem here?
If items.users is an array then it will be rendered as a JSON array.
When you get the JSON response in your JavaScript, you'll just need to loop over the array:
for (var i = 0; i < data.users.length; i++) {
//do something with data.users[i]
}
where data is the JSON data returned from the Ajax call.
This should work fine out of the box. Arrays inside arrays is no problem. Rails walks down your objects and tries to convert them to simple objects like hashes, arrays, strings and numbers. ActiveRecord objects will turn all their attributes into a hash when you convert it to JSON. If item.users is an array of ActiveRecord instances, than your example will automatically work. You can retrieve them in Javascript exactly as you would walk through a hash and array in Ruby. So something like:
response['mylist']['users'][0]['name']
Edit
To limit the fields in the user, add a method to the user class:
class User < ActiveRecord::Base
def as_json(*)
{ :name => name, :desc => desc }
end
end

Need to return all users with genre 'Acoustic'

Is there a better way to write this:
User.where(:genre_id => Genre.where(:name => 'Acoustic').first.id).first.first_name
Basically, I am trying to return all users with genre 'Acoustic'.
Above will work if there are users with the Acoustic setting. But if i do:
User.where(:genre_id => Genre.where(:name => 'Pop').first.id).first.first_name
I will get an error, since there are no users associated with the pop
genre...
Any suggestions to get this to work?
In a general way, many-to-many relationships really suck in mongo (the price you pay for has_one/has_many being so awesome)
I am assuming the problem is that Genre.where(:name => 'Pop').first returns nil? I would do this
User.where(:genre_id => g).first.first_name if g = Genre.where(:name => 'Pop').first.try(:id)
or if massive one line expressions aren't your thing
g = Genre.where(:name => 'Pop').first.try(:id)
if g
User.where(:genre_id => g).first.first_name
end
Doesn't the usual nested where work?
User.where(:genre => { :name => 'Pop' })
You could try with find instead of where for Genre:
User.where(:genre_id => Genre.find(:first, :conditions => { :name => 'Pop' }))
This should work even when Genre.find returns nil.

How can I pass multiple attributes to find_or_create_by in Rails 3?

I want to use find_or_create_by, but this statement does NOT work. It does not "find" or "create" with the other attributes.
productproperty = ProductProperty.find_or_create_by_product_id(:product_id => product.id, :property_id => property.id, :value => d[descname])
There seems to be very little, or no, information on the use of dynamic finders in Rails 3. "and"-ing these together gives me a an unknown method error.
UPDATE:
Originally I couldn't get the following to work. Please assume I'm not an idiot and "product" is an instance of Product AR model.
product.product_properties.find_or_create_by_property_id_and_value(:property_id => 1, :value => "X")
The error methods was:
no such keys: property_id, value
I couldn't figure that out. Only this morning did I find the reference to passing the values like this instead:
product.product_properties.find_or_create_by_property_id_and_value(1, "X")
And voilá, it works fine. I would have expected a hash to work in the same situation but I guess not.
So I guess you get a down vote if you miss something on the internet?
If you want to search by multiple attributes, you can use "and" to append them. For example:
productproperty = ProductProperty.find_or_create_by_product_id_and_property_id_and_value(:product_id => product.id, :property_id => property.id, :value => d[descname])
There is one minor catch to be aware of. It will always return the object you've specified, even if that object can't be saved due to validation errors. So make sure you check to see if the returned object has an id (or is_valid?). Don't assume its in the database.
Alternatively, you can use the 'bang' version of the method to raise an error if the object cannot be saved:
http://guides.rubyonrails.org/active_record_querying.html#find-or-create-by-bang
This applies to Rails 3.
See http://api.rubyonrails.org/classes/ActiveRecord/Base.html:
With single query parameter:
productproperty = ProductProperty.find_or_create_by_product_id(product.id) { |u| u.property_id => property_id, u.value => d[descname] } )
or extended with multiple parameters:
productproperty = ProductProperty.find_or_create_by_product_id(:product_id => product.id, :property_id => property_id, :value => d[descname]) { |u| u.property_id => property_id, u.value => d[descname] } )
Would work with:
conditions = { :product_id => product.id,
:property_id => property.id,
:value => d[descname] }
pp = ProductProperty.find(:first, :conditions => conditions) || ProductProperty.create(conditions)
In Rails 4, you can use find_or_create_by(attr1: 1, attr2: 2) to find or create by multiple attributes.
You can also do something like:
User.create_with(
password: 'secret',
password_confirmation: 'secret',
confirmation_date: DateTime.now
).find_or_create_by(
email: 'admin#domain.com',
admin: true
)
If you need to create the user with some attributes, but cannot search by those attributes.
You could also use where(...).first_or_create - ActiveRecord::Relation#first_or_create.
product_property_attrs = { product_id: product.id,
property_id: property.id,
value: d[descname] }
product_property = ProductProperty.where(product_property_attrs).first_or_create
I've found in Rails 3.1 you do not need to pass the attributes in as a hash. You just pass the values themselves.
ProductProperty.find_or_create_by_product_id_and_property_id_and_value(
product.id, property.id, d[descname])

FasterCSV Parsing issue?

G'day guys, I'm currently using fastercsv to construct ActiveRecord elements and I can't for the life of me see this bug (tired), but for some reason when it creates, if in the rake file i output the column I want to save as the element value, it puts out correctly, as either a Trade or a Quote
but when I try to save it into the activerecord, it won't work.
FasterCSV.foreach("input.csv", :headers => true) do |row|
d = DateTime.parse(row[1]+" "+row[2])
offset = Rational(row[3].to_i,24)
o = d.new_offset(offset)
t = Trade.create(
:name => row[0],
:type => row[4],
:time => o,
:price => row[6].to_f,
:volume => row[7].to_i,
:bidprice => row[10].to_f,
:bidsize => row[11].to_i,
:askprice => row[14].to_f,
:asksize => row[15].to_i
)
end
Ideas?
Name and Type are both strings, every other value works except for type. Have I missed something really simple?
Ruby's Object class has a type method. You need to t[:type] = row[4] to avoid that method.
-Tim

Resources