Ruby change hash to single layer with square brackets - ruby-on-rails

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.

Related

Change ruby hash to jsonl - JSON seperated by newlines

I am looking at posting to an endpoint on Bubble.io using Ruby and they require jsonl (plain text, new-line seperated) instead of JSON.
Is there a way to take a hash and make it jsonl? Something like hash.to_jsonl.
For jsonl (or ndjson), the json need to be formated as single line.
Therefor use to_json method.
require 'json'
group = [{:name => "Tom", :age => 27}, {:name => "Jerry", :age => 37}]
puts group.map { |r| JSON.generate(r) }.join("\n")
This code generates the following:
{"name":"Tom","age":27}
{"name":"Jerry","age":37}
Here is the solution I went with:
group = [{name => "Tom"}, {name => "Jerry"}]
generated = []
group.each do |r|
generated << JSON.generate(r)
end
jsonl_text = generated.join("\n")

Rails to_json response converting datetime to unixtimestamp

I created a REST API with rails and use .to_json to convert the API output to json response. E.g. #response.to_json
The objects in the response contains created_at and updated_at fields, both Datetime
In the JSON response, both these fields are converted to strings, which is more costly to parse than unixtimestamp.
Is there a simple way I can convert the created_at and updated_at fields into unixtimestamp without having to iterate through the whole list of objects in #response?
[updated]
danielM's solution works if it's a simple .to_json. However, if I have a .to_json(:include=>:object2), the as_json will cause the response to not include object2. Any suggestions on this?
Define an as_json method on your response model. This gets run whenever you return an instance of the response model as JSON to your REST API.
def as_json(options={})
{
:id => self.id,
:created_at => self.created_at.to_time.to_i,
:updated_at => self.updated_at.to_time.to_i
}
end
You can also call the as_json method explicitly to get the hash back and use it elsewhere.
hash = #response.as_json
Unix timestamp reference: Ruby/Rails: converting a Date to a UNIX timestamp
Edit: The as_json method works for relationships as well. Simply add the key to the hash returned by the function. This will run the as_json method of the associated model as well.
def as_json(options={})
{
:id => self.id,
:created_at => self.created_at.to_time.to_i,
:updated_at => self.updated_at.to_time.to_i,
:object2 => self.object2,
:posts => self.posts
}
end
Furthermore, you can pass in parameters to the method to control how the hash is built.
def as_json(options={})
json = {
:id => self.id,
:created_at => self.created_at.to_time.to_i,
:updated_at => self.updated_at.to_time.to_i,
:object2 => self.object2
}
if options[:posts] == true
json[:posts] = self.posts
end
json
end
hash = #response.as_json({:posts => true})

Rails - Parameter with multiple values in the URL when consuming an API via Active Resource

I am consuming an API that expects me to do requests in the following format:
?filter=value1&filter=value2
However, I am using Active Resource and when I specify the :params hash, I can't make the same parameter to appear twice in the URL, which I believe is correct. So I can't do this:
:params => {:consumer_id => self.id, :filter => "value1", :filter => "value2" }, because the second filter index of the hash will be ignored.
I know I can pass an array (which I believe is the correct way of doing it) like this:
:params => {:consumer_id => self.id, :filter => ["value1","value2"] }
Which will produce a URL like:
?filter[]=value1&filter[]=value2
Which to me seems ok, but the API is not accepting it. So my question are:
What is the correct way of passing parameters with multiple values? Is it language specific? Who decides this?
http://guides.rubyonrails.org/action_controller_overview.html#hash-and-array-parameters
Try :filter[] => value, :filter[] => value2
to create a valid query string, you can use
params = {a: 1, b: [1,2]}.to_query
http://apidock.com/rails/Hash/to_query
http://apidock.com/rails/Hash/to_param
You can generate query strings containing repeated parameters by making a hash that allows duplicate keys.
Because this relies on using object IDs as the hash key, you need to use strings instead of symbols, at least for the repeated keys.
(reference: Ruby Hash with duplicate keys?)
params = { consumer_id: 1 } # replace with self.id
params.compare_by_identity
params["filter"] = "value1"
params["filter"] = "value2"
params.to_query #=> "consumer_id=1&filter=value1&filter=value2"

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

good way to handle a bunch of data in a hash

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.

Resources