Add trailing comma and bracket to Ruby JSON - ruby-on-rails

I need to add commas between my hashes and encapsulate them inside square brackets. Can anyone tell me how?
Here is my code:
namespace :fieldfacts do
desc "Export Topics"
task :export_topics => :environment do
out = []
File.open("public/topics.json","w") do |f|
Topic.all.each do |topic|
api = TopicsService.new()
topic_api = api.get(topic.topic_api_id)
out = {
'id' => topic.id,
'name' => topic.name,
'keywords_list' => topic_api.keywords_list,
'organizations_list' => topic_api.organizations_list,
'social_groups_list' => topic_api.social_groups_list,
'feeds_list' => topic_api.feeds_list,
'articles_list' => topic_api.articles_list,
'people' => topic_api.people
}
f.write(JSON.pretty_generate(out))
end
end
end
end
Here is the output:
{
"id": 3,
"name": "Precision Agriculture",
"keywords_list": null,
"organizations_list": null,
"social_groups_list": null,
"feeds_list": null,
"articles_list": null,
"people": null
}{
"id": 4,
"name": "Backcountry Skiing",
"keywords_list": null,
"organizations_list": null,
"social_groups_list": null,
"feeds_list": null,
"articles_list": null,
"people": null
}
Any help would be appreciated. Thanks!

Your issue here is that you are generating JSON multiple times and then appending it together, rather than generating it once.
Something like this should solve your problem (note the change in location of f.write):
namespace :fieldfacts do
desc "Export Topics"
task :export_topics => :environment do
out = []
File.open("public/topics.json","w") do |f|
Topic.all.each do |topic|
api = TopicsService.new()
topic_api = api.get(topic.topic_api_id)
out << {
'id' => topic.id,
'name' => topic.name,
'keywords_list' => topic_api.keywords_list,
'organizations_list' => topic_api.organizations_list,
'social_groups_list' => topic_api.social_groups_list,
'feeds_list' => topic_api.feeds_list,
'articles_list' => topic_api.articles_list,
'people' => topic_api.people
}
end
f.write(JSON.pretty_generate(out))
end
end
end

Related

TypeError no implicit conversion of Symbol into Integer

Hash
data = {
:recordset => {
:row => {
:property => [
{:name => "Code", :value => "C0001"},
{:name => "Customer", :value => "ROSSI MARIO"}
]
}
},
:#xmlns => "http://localhost/test"
}
Code Used
result = data[:recordset][:row].each_with_object([]) do |hash, out|
out << hash[:property].each_with_object({}) do |h, o|
o[h[:name]] = h[:value]
end
end
I cannot get the following output:
[{"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"}
Error message:
TypeError no implicit conversion of Symbol into Integer
It works correctly in case of multi records
data = {
:recordset => {
:row => [{
:property => [
{:name => "Code", :value => "C0001"},
{:name => "Customer", :value => "ROSSI MARIO"},
{:name => "Phone1", :value => "1234567890"}
]
}, {
:property => [
{:name => "Code", :value => "C0002"},
{:name => "Customer", :value => "VERDE VINCENT"},
{:name => "Phone1", :value => "9876543210"},
{:name => "Phone2", :value => "2468101214"}
]
}]
},
:#xmlns => "http://localhost/test"
}
Code used
data.keys
#=> [:recordset, :#xmlns]
data[:recordset][:row].count
#=> 2 # There are 2 set of attribute-value pairs
result = data[:recordset][:row].each_with_object([]) do |hash, out|
out << hash[:property].each_with_object({}) do |h, o|
o[h[:name]] = h[:value]
end
end
#=> [
# {"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"},
# {"Code"=>"C0002", "Customer"=>"VERDE VINCENT", "Phone1"=>"9876543210", "Phone2"=>"2468101214"}
# ]
In the first case data[:recordset][:row] is not an Array, it's a Hash, so when you iterate it, the hash variable becomes the array:
[:property, [{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}]]
In the second case, it's an Array, not a Hash, so when you iterate it, it becomes the hash:
{:property=>[{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}, {:name=>"Phone1", :value=>"1234567890"}]}
You're always assuming it's the second format. You could force it into an array, and then flatten by 1 level to treat both instances the same:
result = [data[:recordset][:row]].flatten(1).each_with_object([]) do |hash, out|
out << hash[:property].each_with_object({}) do |h, o|
o[h[:name]] = h[:value]
end
end
# => [{"Code"=>"C0001", "Customer"=>"ROSSI MARIO"}] # result from example 1
# => [{"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"},
# {"Code"=>"C0002", "Customer"=>"VERDE VINCENT",
# "Phone1"=>"9876543210", "Phone2"=>"2468101214"}] # result from example 2
It's tempting to try and use Kernal#Array() instead of [].flatten(1), but you have to remember that Hash implements to_a to return a nested array of keys and values, so Kernal#Array() doesn't work like you'd want it to:
Array(data[:recordset][:row]) # using the first example data
# => [[:property, [{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}]]]
You can create an array if it's not an array to normalize the input before processing it.
info = data[:recordset][:row]
info = [info] unless info.is_an? Array
result = info.each_with_object([]) do ....

Grabbing a specific hash in an array that meets a specific criteria

I have a huge array full of a bunch of hashes. What I need to do is single out one index hash from the array that meets a specific criteria. (doing this due to an rspec test, but having trouble singling out one of them)
My array is like this
[
{
"name" => "jon doe",
"team" => "team2",
"price" => 2000,
"eligibility_settings" => {}
},
{
"name" => "jonny doe",
"team" => "team1",
"value" => 2000,
"eligibility_settings" => {
"player_gender" => male,
"player_max_age" => 26,
"player_min_age" => 23,
"established_union_only" => true
}
},
{
"name" => "jonni doe",
"team" => "team3",
"price" => 2000,
"eligibility_settings" => {}
},
]
I need to single out the second one, based on its eligibility settings. I just took three of them from my array, have lots more, so simple active record methods like (hash.second) won't work in this instance.
I've tried things like
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
However when I try this, I get a nil response. (which is odd)
I've also looked into using the ruby detect method, which hasn't gotten me anywhere either
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Would anybody have any idea what to do with this one?
Notes
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Is is players or Players ?
Why is it plural?
If you can call map on team, it probably should be plural
Why do you convert to a hash?
eligibility_settings? isn't a key in your hash. eligibility_settings is
eligibility_settings can be a hash, but it cannot be true
If you want to check if it isn't empty, use !h['eligibility_settings'].empty?
Possible solution
You could use :
data = [
{
'name' => 'jon doe',
'team' => 'team2',
'price' => 2000,
'eligibility_settings' => {}
},
{
'name' => 'jonny doe',
'team' => 'team1',
'value' => 2000,
'eligibility_settings' => {
'player_gender' => 'male',
'player_max_age' => 26,
'player_min_age' => 23,
'established_union_only' => true
}
},
{
'name' => 'jonni doe',
'team' => 'team3',
'price' => 2000,
'eligibility_settings' => {}
}
]
p data.find { |h| !h['eligibility_settings'].empty? }
# {"name"=>"jonny doe", "team"=>"team1", "value"=>2000, "eligibility_settings"=>{"player_gender"=>"male", "player_max_age"=>26, "player_min_age"=>23, "established_union_only"=>true}}
If h['eligibility_settings'] can be nil, you can use :
data.find { |h| !h['eligibility_settings'].blank? }
or
data.find { |h| h['eligibility_settings'].present? }

Calling ActiveRecord relationship with polymorphic model

I'm trying to create a rake task that retrieves data from an API. However the data that I need has data from different models.
Here is my error:
[
{
"id": 3,
"name": "Precision Agriculture",
"topics_owners_ids": "#<User::ActiveRecord_Associations_CollectionProxy:0x000000095fa498>",
"keywords_list": null,
"keywords": [
],
"organizations_list": null,
"organizations": [
],
"social_groups_list": null,
"feeds_list": null,
"articles_list": null,
"people": null
}
]
Here is my rake task:
namespace :fieldfacts do
desc "Export Topics"
task :export_topics => :environment do
out = []
File.open("public/topics.json","w") do |f|
Topic.all.each do |topic|
api = TopicsService.new()
topic_api = api.get(topic.topic_api_id)
out << {
'id' => topic.id,
'name' => topic.name,
'topics_owners_ids' => topic.users.id,
'keywords_list' => topic_api.keywords_list,
'keywords' => topic_api.keywords,
'organizations_list' => topic_api.organizations_list,
'organizations' => topic_api.organizations,
'social_groups_list' => topic_api.social_groups_list,
'feeds_list' => topic_api.feeds_list,
'articles_list' => topic_api.articles_list,
'people' => topic_api.people
}
end
f.write(JSON.pretty_generate(out))
end
end
end
As you can see, the topics_owners_ids is not returning the correct data. I'm not sure how to fetch it.
topic.users will give you the collection object and you need to select ids. Try
topic.users.pluck(:id)

What is an elegant way of modifying hashes inside an array within a nested hash in Ruby

I would like to transform this
def some_process(k,v)
return "#{v}_#{k}"
end
a_hash = {
"i_love_hashes" => {
"thing" => 20,
"other_thing" => "0",
"yet_another_thing" => "i disagree",
"_peculiar_thing" => [
{"count" => 30,
"name" => "freddie"},
{"count" => 15,
"name" => "johhno"},
{"count" => 12,
"name" => "mohammed"},
]
},
"as_do_i" => {
"thing" => 10,
"other_thing" => "2",
"yet_another_thing" => "i strongly agree",
"_peculiar_thing" => [
{"count" => 10,
"name" => "frodo"},
{"count" => 4,
"name" => "bilbo"},
{"count" => 2,
"name" => "elizabeth"},
]
}
}
into this
{
"i_love_hashes"=>{
"thing"=>20,
"other_thing"=>"0",
"yet_another_thing"=>"i disagree",
"_peculiar_thing"=> [
{"count"=>30, "name"=>"freddie", :sinister_name=>"freddie_i_love_hashes"},
{"count"=>15, "name"=>"johhno", :sinister_name=>"johhno_i_love_hashes"},
{"count"=>12, "name"=>"mohammed", :sinister_name=>"mohammed_i_love_hashes"}
]},
"as_do_i"=>{
"thing"=>10,
"other_thing"=>"2",
"yet_another_thing"=>"i strongly agree",
"_peculiar_thing"=>[
{"count"=>10, "name"=>"frodo", :sinister_name=>"frodo_as_do_i"},
{"count"=>4, "name"=>"bilbo", :sinister_name=>"bilbo_as_do_i"},
{"count"=>2, "name"=>"elizabeth", :sinister_name=>"elizabeth_as_do_i"}
]
}
}
this is the code I am currently using to achieve this
a_hash.each_with_object({}) do |(k,v),o|
o.merge!({k =>
v.each_with_object({}) do |(a,b),g|
g.merge!({ a =>
(b.is_a?(Array) ? b.collect {|x| x.merge({sinister_name: (some_process k, x["name"])})} : b)
})
end
})
end
Ignoring the specific details of what is being returned by "some_process" (what is important is that it depends on the outer most key and the inner name values, in this example), are there any alternatives that would be considered more elegant?
Why not do a recursive function?
def add_siniter(hash)
hash[:siniter_name] = "#{hash['name']}_i_love_hashes"
hash
end
def format_hash(item)
case item
when Hash then item.keys.each{|key| format_hash(item[key])}
when Array then item.map!{|h| add_siniter(h)}
end
end
format_hash(a_hash)

rails passing null values when creating using AR create method

In my rails application, I have a button which when clicked, copies data from one database and insert it in another.
I am using octopus gem to link my application to 2 databases.
To copy a record from db_A to db_B, I am using the code below:
Octopus.using(:shard_B) do
#book_new_live = Book.create(
:BK_SUB_FK => #book.BK_SUB_FK,
:BK_TITLE => #book.BK_TITLE,
:BK_SOURCE => "",
:BK_PUB => "",
:BK_COVER => "",
:BK_LABEL_PRODUCT => #book.BK_LABEL_PRODUCT,
:BK_FINAL_LABEL => "",
:BK_VISUAL_METHOD => #book.PRB_VISUAL_METHOD,
:BK_DB => "",
:BK_COVERED_REGION => "",
:BK_VERSION_NO => #book.BK_VERSION_NO,
:BK_SEQ_FILE => "",
)do |primary|
primary.BK_ID = #book.BK_ID
end
end
Database 'db_b', to which data in copied, does not accept null values and the columns cannot be null and the default value is 'NONE'.
Also, I am not allowed to modify the structure of the database so that it can accept null values.
If I use the simplified code below, I get an error message which informs me that the columns 'BK_SOURCE', 'BK_PUB', 'BK_COVER'...cannot be null. By default rails is passing null to those columns.
So I have to pass empty strings to the columns which cannot be null.
Octopus.using(:shard_B) do
#book_new_live = Book.create(
:BK_SUB_FK => #book.BK_SUB_FK,
:BK_TITLE => #book.BK_TITLE,
:BK_LABEL_PRODUCT => #book.BK_LABEL_PRODUCT,
:BK_VISUAL_METHOD => #book.PRB_VISUAL_METHOD,
:BK_VERSION_NO => #book.BK_VERSION_NO,
)do |primary|
primary.BK_ID = #book.BK_ID
end
end
Is there a way of preventing rails from passing null values to the columns not mentioned in the above code?
If i understand you correctly, you can just use the or operator to send your values like this:
Octopus.using(:shard_B) do
#book_new_live = Book.create(
:BK_SUB_FK => #book.BK_SUB_FK,
:BK_TITLE => #book.BK_TITLE,
:BK_SOURCE => #book.BK_SOURCE || "NONE",
:BK_PUB => #book.BK_PUB || "NONE",
:BK_COVER => #book.BK_COVER || "NONE",
:BK_LABEL_PRODUCT => #book.BK_LABEL_PRODUCT,
:BK_FINAL_LABEL => "",
:BK_VISUAL_METHOD => #book.PRB_VISUAL_METHOD,
:BK_DB => "",
:BK_COVERED_REGION => "",
:BK_VERSION_NO => #book.BK_VERSION_NO,
:BK_SEQ_FILE => "",
)do |primary|
primary.BK_ID = #book.BK_ID
end
end
By saying that the value to be passed is #book.BK_SOURCE || "NONE", if the attribute is nil, then the string NONE is passed instead.
EDIT
hash = {
:BK_SUB_FK => #book.BK_SUB_FK,
:BK_TITLE => #book.BK_TITLE,
:BK_SOURCE => #book.BK_SOURCE,
:BK_PUB => #book.BK_PUB,
:BK_COVER => #book.BK_COVER,
:BK_LABEL_PRODUCT => #book.BK_LABEL_PRODUCT,
:BK_FINAL_LABEL => #book.BK_FINAL_LABEL,
:BK_VISUAL_METHOD => #book.PRB_VISUAL_METHOD,
:BK_DB => #book.BK_DB,
:BK_VERSION_NO => #book.BK_VERSION_NO
}
hash = hash.delete_if { |k, v| v.nil? }
Octopus.using(:shard_B) do
#book_new_live = Book.create(hash)do |primary|
primary.BK_ID = #book.BK_ID
end
end
Give it a try.

Resources