find_or_create_by ignore if column not nil - ruby-on-rails

I have a rake file setup to import JSON data from an external URL. In this JSON there is a field titled 'deleted'. This field is a BIGINT, and if the field has been deleted then it will fill with random BIGINT.
I want to import all rows except those that have the 'deleted' field populated.
This is what I have so far, which works well except importing all rows.
data_json['Agent'].each do |data|
agent = Agent.find_or_create_by(agent_id: data['id'])
agent. agent_id = data['id']
agent.first_name = data['first_name']
agent.last_name = data['last_name']
agent.deleted = data['deleted']
agent.save
end

I believe what you're looking for it the next keyword. You could try something like this:
data_json['Agent'].each do |data|
agent = Agent.find_or_create_by(agent_id: data['id'])
agent. agent_id = data['id']
agent.first_name = data['first_name']
agent.last_name = data['last_name']
agent.deleted = !data['deleted'].nil? # Force this into a bool
next if agent.deleted?
agent.save
end

Related

Rails Import: "No Implicit conversion of string into array"

I'm attempting to import values from a spreadsheet into a table. I am adding new records for each row to an array, and then importing that array. My Code is below:
def import(path)
spreadsheet = Roo::Spreadsheet.open(path+"Data.xlsx")
sales = spreadsheet.sheet('Accounts')
sales = sales.parse(headers: true)
accounts = []
sales.each do |row|
a = HM::NewBusiness.new
a.dealer_id = row["Dlr #"]
a.dealer_name = row["Dealer Name"]
a.duns = row["Duns Name"]
a.industry = row["Type"]
a.volume_2016 = row["volume_2016"]
a.volume_2017 = row["volume_2017"]
a.volume_2018 = row["volume_2018"]
a.volume_2019 = row["volume_2019"]
accounts << a
end
pp accounts
HM::NewBusiness.import(accounts)
end
However when I run import, I get:
TypeError: no implicit conversion of String into Array
I can't figure out where I'm going wrong. Any help would be appreciated.
Figured it out. The problem was that I coincidentally named the method itself "import". In short a 1d10t error.

Lua Table Access with Location Given as a String

I have a table and I'm trying to access a specific location that is passed in as a String. What is the easiest way to use the string to access the correct location?
Example, if the table looks like this:
a.b1 = true
a.b2.c1 = true
a.b2.c2 = false
a.b3 = true
How can I change a.b2.c2 to true given a location 'a.b2.c2' as a string.
If you have just a single level, you can use square-brace indexing:
function setSingle(obj, key, value)
obj[key] = value
end
setSingle(a, "b1", "foo")
print(a.b1) --> foo
If you have multiple, you need to do several iterations of this indexing. You can use a loop to do that:
function setMultiple(obj, keys, value)
for i = 1, #keys - 1 do
obj = obj[keys[i]]
end
-- Merely "obj = value" would affect only this local variable
-- (as above in the loop), rather than modify the table.
-- So the last index has to be done separately from the loop:
obj[keys[#keys]] = value
end
setMultiple(a, {"b2", "c1"}, "foo")
print(a.b2.c1) --> foo
You can use string.gmatch to parse a properly formatted list of keys. [^.]+ will match "words" made of non-period symbols:
function parseDots(str)
local keys = {}
for key in str:gmatch "[^.]+" do
table.insert(keys, key)
end
return keys
end
Putting this all together,
setMultiple(a, parseDots("b2.c2"), "foo")
print(a.b2.c2) --> foo
One issue you may run into is that you cannot create new tables with this function; you will have to create the containing table before you can create any keys in it. For example, beforing ading "b4.c3" you would have to add "b4".
You can use loadstring to build the statement you want to execute as a string.
a = { b2 = {} }
a.b1 = true
a.b2.c1 = true
a.b2.c2 = false
a.b3 = true
str = "a.b2.c2"
loadstring(str .. " = true")()
print(a.b2.c2)

rails - get the SQL of a model save

I need to be able to output the SQL UPDATES that would be generated by Rails, without actually running them or Saving the records. I will be outputting the SQL updates to a file instead.
Is there a way to do this in Rails, without using string interpolation?
Is it possible to do something like the following:
p = Post.where (something)
p.some_value = some_new_value
p.to_sql??? # how to generate the update statement
rather than:
"UPDATE TABLE SET field_1 = #{new_field} WHERE ID = " etc etc
Took from #R.F. Nelson and wrap it to a method. You could just calling to_update_sql with your model as the argument to get the SQL.
def to_update_sql(model)
return '' if model.changes.empty?
table = Arel::Table.new(model.class.table_name)
update_manager = Arel::UpdateManager.new(model.class)
update_manager.set(model.changes.map{|name, (_, val)| [table[name], val] })
.where(table[:id].eq(model.id))
.table(table)
return update_manager.to_sql
end
post = Post.first
post.some_value = xxxx
to_update_sql(post)
# => UPDATE `posts` SET `some_value` = xxx WHERE `posts`.`id` = 1
Taken from this post:
You can achieve this goal with AREL:
# Arel::InsertManager
table = Arel::Table.new(:users)
insert_manager = Arel::InsertManager.new
insert_manager.into(table)
insert_manager.insert([ [table[:first_name], 'Eddie'] ])
sql_transaction = insert_manager.to_sql
File.open('file_name.txt', 'w') do |file|
file.write(sql)
end
# Arel::UpdateManager
table = Arel::Table.new(:users)
update_manager = Arel::UpdateManager.new
update_manager.set([[table[:first_name], "Vedder"]]).where(table[:id].eq(1)).table(table)
sql_transaction = update_manager.to_sql
File.open('file_name.txt', 'w') do |file|
file.write(sql)
end
Here you can find all Arel managers, like delete_manager.rb, select_manager.rb and the others.
Good read: http://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html

Creating a record from a third-party object with same field names?

I have a Charge model in my database with field names that match up to the fields returned from a third-party API.
charge = ThirdPartyChargeAPI.find(1)
Charge.create do |e|
e.object = charge.object
e.paid = charge.paid
e.amount = charge.amount
e.currency = charge.currency
e.refunded = charge.refunded
e.amount_refunded = charge.amount_refunded
e.failure_message = charge.failure_message
e.failure_code = charge.failure_code
e.description = charge.description
e.metadata = charge.metadata
e.captured = charge.captured
e.balance_transaction = charge.balance_transaction
e.customer = charge.customer
e.invoice = charge.invoice
e.created = charge.created
end
Seems painfully redundant, though. Is there some way to merge this without having to basically set every single field manually?
Assuming there's no way to get a direct hash from the API (I would imagine there would be, since it's probably coming in as XML or JSON), you could try a direct map of instance variables:
Charge.create do |c|
charge.instance_variables.each do |var|
value = charge.instance_variable_get(var)
c.instance_variable_set(var, value)
end
end
This is making some pretty bold assumptions about the structure of the charge you're getting back from the API though - any instance variable in it that you don't want will be included.

Using Column headers to parse excel sheets using roo - Ruby

Can we use column headers to specify the column number from which we are parsing the excel sheet using roo gem? My current code is like this now:
oo = Openoffice.new("simple_spreadsheet.ods")
oo.default_sheet = oo.sheets.first
(2..oo.last_row).each do |line|
date = oo.cell(line,'A')
start_time = oo.cell(line,'B')
end_time = oo.cell(line,'C')
pause = oo.cell(line,'D')
...
end
I would like to parse from column headers instead of specifying columns as 'A' 'B' 'C' ... Can I acheive this using Roo?
You can grab the entire header row as an array and hash the entire row key'd on the header row.
oo = Openoffice.new("simple_spreadsheet.ods")
oo.default_sheet = oo.sheets.first
header = oo.row(1)
2.upto(oo.last_row) do |line|
row_data = Hash[header.zip oo.row(line)]
...
end
You could also use row_data[line] to nest the hashes for later use.
A cleaner/clearer version of the above is
oo = Openoffice.new("simple_spreadsheet.ods")
oo.default_sheet = file.sheets.first
header = oo.first_row
2.upto(oo.last_row) do |line|
row_data = Hash[*header.zip(row).flatten]
...
end
the original took me a bit to understand because especially as i thought hash was a local variable named hash instead of the class Hash
This will use the header row as the keys. The helpful parts are transpose and strip.
def self.excel_to_hash(folder_name, file_name, tab_name)
# Takes an excel file name and a tab name, and returns an array of stripped, transposed rows
# Sample call: my_data = excel_to_hash File.join(Rails.root,'db/data/data_to_import.xlsx'), 'models'
rows = []
file = File.open(File.join(folder_name, file_name), mode = 'r')
excel = Excelx.new(file.path, nil, :ignore)
excel.default_sheet = excel.sheets.index(tab_name) + 1
header = excel.row(1)
(2..excel.last_row).each do |i|
next unless excel.row(i)[0]
row = Hash[[header, excel.row(i)].transpose]
row.each_key{|x| row[x] = row[x].to_s.strip if row[x]}
rows << row
end
return rows
end
valid through Roo gem 1.10.2
This works for me
require 'roo'
# open excel file
excel_file = Roo::Spreadsheet.open(file_path)
# iterate on each sheet
excel_file.each_with_pagename do |name, sheet|
# iterate on each sheet
sheet.parse(headers: true, pad_cells: true) do |row|
# data should be access by column header if we have column header Name we can access like this
row['Name']
end
end
end

Resources