Change ruby hash to jsonl - JSON seperated by newlines - ruby-on-rails

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")

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 console compare model instances

Is there a way to compare two instances of model like
Model.compare_by_name("model1", "model2") which would list the differing column fields
You can use ActiveRecord::Diff if you want a mapping of all the fields that differ and their values.
alice = User.create(:name => 'alice', :email_address => 'alice#example.org')
bob = User.create(:name => 'bob', :email_address => 'bob#example.org')
alice.diff?(bob) # => true
alice.diff(bob) # => {:name => ['alice', 'bob'], :email_address => ['alice#example.org', 'bob#example.org']}
alice.diff({:name => 'eve'}) # => {:name => ['alice', 'eve']}
There is no standard comparator for this. The standard ActiveModel comparator:
Returns true if comparison_object is the same exact object, or comparison_object is of the same type and self has an ID and it is equal to comparison_object.id.
You can write your own by using Hash#diff from activesupport. Something like the following should hopefully get you started:
def Model.compare_by_name(model1, model2)
find_by_name(model1).attributes.diff(find_by_name(model2).attributes)
end
Without using a library or defining a custom method, you can easily get a diff between two models.
For instance,
a = Foo.first
b = Foo.second
a.attributes = b.attributes
a.changes #=> {"id" => [1,2] }

How to insert multiple records into database

How can I insert multiple records into a database using rails syntax.
INSERT INTO users (email,name) VALUES ('a#ao.in','a'),('b#ao.in','b'),
('c#ao.in','c');
This is how we do it in MySQL. How is this done in Rails?
Check out this blog post: http://www.igvita.com/2007/07/11/efficient-updates-data-import-in-rails/
widgets = [ Widget.new(:title => 'gizmo', :price => 5),
Widget.new(:title => 'super-gizmo', :price => 10)]
Widget.import widgets
Depending on your version of rails, use activerecord-import 0.2.6 (for Rails 3) and ar-extensions 0.9.4 (for Rails 2)
From the author: http://www.continuousthinking.com/tags/arext
While you cannot get the exact SQL that you have there, you can insert multiple records by passing create or new on an array of hashes:
new_records = [
{:column => 'value', :column2 => 'value'},
{:column => 'value', :column2 => 'value'}
]
MyModel.create(new_records)
I use following in my project but it is not proper for sql injection.
if you are not using user input in this query it may work for you
user_string = " ('a#ao.in','a'), ('b#ao.in','b')"
User.connection.insert("INSERT INTO users (email, name) VALUES"+user_string)
Just a use activerecord-import gem for rails 3 or ar-extensions for rails 2
https://github.com/zdennis/activerecord-import/wiki
In Gemfile:
gem "activerecord-import"
In model:
import "activerecord-import"
In controller:
books = []
10.times do |i|
books << Book.new(:name => "book #{i}")
end
Book.import books
This code import 10 records by one query ;)
or
##messages = ActiveSupport::JSON.decode(#content)
#messages = JSON(#content)
#prepare data for insert by one insert
fields = [:field1, :field2]
items = []
#messages.each do |m|
items << [m["field1"], m["field2"]]
end
Message.import fields, items
You can use Fast Seeder to do multiple insert.
In People_controller.rb
# POST people
NAMES = ["Sokly","Nary","Mealea"]
def create
Person.transaction do
NAMES.each do |name|
#name = Person.create(:name => name)
#name.save
end
end
end
Just pass an array of hashs to the create method like this:
User.create([{:email => "foo#com", :name => "foo"}, {:email => "bar#com", :name => "bar"}])

Is there find_or_create_by_ that takes a hash in Rails?

Here's some of my production code (I had to force line breaks):
task = Task.find_or_create_by_username_and_timestamp_and_des \
cription_and_driver_spec_and_driver_spec_origin(username,tim \
estamp,description,driver_spec,driver_spec_origin)
Yes, I'm trying to find or create a unique ActiveRecord::Base object. But in current form it's very ugly. Instead, I'd like to use something like this:
task = Task.SOME_METHOD :username => username, :timestamp => timestamp ...
I know about find_by_something key=>value, but it's not an option here. I need all values to be unique. Is there a method that'll do the same as find_or_create_by, but take a hash as an input? Or something else with similat semantics?
Rails 3.2 first introduced first_or_create to ActiveRecord. Not only does it have the requested functionality, but it also fits in the rest of the ActiveRecord relations:
Task.where(attributes).first_or_create
In Rails 3.0 and 3.1:
Task.where(attributes).first || Task.create(attributes)
In Rails 2.1 - 2.3:
Task.first(:conditions => attributes) || Task.create(attributes)
In the older versions, you could always write a method called find_or_create to encapsulate this if you'd like. Definitely done it myself in the past:
class Task
def self.find_or_create(attributes)
# add one of the implementations above
end
end
I also extend the #wuputah's method to take in an array of hashes, which is very useful when used inside db/seeds.rb
class ActiveRecord::Base
def self.find_or_create(attributes)
if attributes.is_a?(Array)
attributes.each do |attr|
self.find_or_create(attr)
end
else
self.first(:conditions => attributes) || self.create(attributes)
end
end
end
# Example
Country.find_or_create({:name => 'Aland Islands', :iso_code => 'AX'})
# take array of hashes
Country.find_or_create([
{:name => 'Aland Islands', :iso_code => 'AX'},
{:name => 'Albania', :iso_code => 'AL'},
{:name => 'Algeria', :iso_code => 'DZ'}
])

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