Convert json to hash to insert with Active Record - ruby-on-rails

I'm still practising processing arrays and hashes, and particularly getting to details in 2d or 3d structures. I'm trying to use details in a json file to process some data ready to insert into the db with Active Record.
Here is my json structure for 'my_file.json'
# my_file.json
[
{
"name": "Joe Bloggs",
"telephone": "012-345-6789"
},
{
"name": "Hilda Bloggs",
"telephone": "012-345-6789"
}
]
and here is the code I'm using to convert the json data into something I can insert into my db
def json_insert_to_db
require 'json'
file = File.read('my_file.json')
json_data = JSON.parse(file)
details = json_data.map do |x|
user = User.new
user.name = json_data[x]['name']
user.telephone = json_data[x]['telephone']
end
end
With this I get
NameError: uninitialized constant User
(User does exist in the database, by the way)
I can't work out where I'm going wrong, but I know it's something simple I am overlooking. Thanks for any help.

The User model was set up but my db had a migration issue. However, beyond that, at the time I was still unable to build what I needed when importing the json file.
I have now worked out how to do it. The migration issue was resolved and I also revised the structure of my json file first, for clarity.
# my_file.json
{
"new_users":
[{
"name": "Joe Bloggs",
"telephone": "012-345-6789",
},
{
"name": "Hilda Bloggs",
"telephone": "012-345-6789",
}]
}
And my script...
require 'json'
file = File.read('my_file.json')
json_data = JSON.parse(file)['new_users']
#new_users = json_data.each do |key,value|
#new_user = User.new
#new_user.name = key['name']
#new_user.telephone = key['telephone']
end
#new_users.each { |x| puts x }

Related

Rails app: I need to build a json object from params inside a loop

I need to build a json object inside a loop using params.
My params look like this...
params[:answers]
returns => {"1"=>"answer1", "2"=>"answer2"}
The keys in this json object are the id's of the survey question.
So I planed to loop through the keys to build the json object like this...
def build_answersheet_json(params[:answers], params[:survey_id])
params[:answers].keys.each do |question_id|
current_question = question_id
current_answer = params[:answers][question_id]
end
end
Since im using "t.json" in my migration to save json to postgres, I wanted to use the extracted question_id and answer to build a json object that looks something like this...
{
survey_id: '1',
answers: {
question: [{
question_id: 1,
answer: 'answer1'
}, {
question_id: 2,
answer: 'answer2'
}]
}
}
Ive been trying to do this using a method that looks somthing like this...
build_answersheet_json(params[:answers], params[:survey_id])
Ive tried JSON.parse() and Ive tried to just logically work through it but I cant seem to figure this out.
Any help is appreciated.
Maybe you can try something like that:
/* fake params (to test) */
params = {
survey_id: '1',
answers: {
"1"=>"answer1",
"2"=>"answer2",
"3"=>"answer3",
"4"=>"answer4"
}
}
def build_answersheet_json(answers, survey_id)
{
survey_id: survey_id,
answers: answers.map { |k,v| { question_id: k.to_i, answer: v } }
}
end
survey = build_answersheet_json(params[:answers], params[:survey_id])
puts survey.class
#Hash
puts survey.to_json
# formated JSON string:
# {
# "survey_id":"1",
# "answers":[
# {"question_id":1,"answer":"answer1"},
# {"question_id":2,"answer":"answer2"},
# {"question_id":3,"answer":"answer3"},
# {"question_id":4,"answer":"answer4"}
# ]
# }
In order to save to a t.json postgress column type, just pass the Hash survey object, like that:
YourModel.create(survey: survey)
Source: http://edgeguides.rubyonrails.org/active_record_postgresql.html
Try
{
survey: ¯\_༼◉ل͟◉༽_/¯,
}
Json may not be parsed if json have construction like this:
survey = {
}
Json may not contain = and assignment
Check real variables values with puts varname.inspect near at code lines where you meet unexpected behaviour.

Whats the proper logic and way to save JSON data that has a nested array

I figured this out the answer is below
So say I have something like this JSON POST coming in:
{
"contact":{
"first_name": "Bill",
"last_name": "Clinton",
"phone_numbers":[
{
"name": "blah",
"number": "555-555-5555"
},
{
"name": "blah2",
"number": "555-555-5555"
}
]
}
}
Forgive me for the crappy formatting of the JSON.
Anyway, I want to save this into my DB. The Rails controller will be taking care of this. Now
I am a bit tired but I am unsure how to handle this since there is a nested array.
Please Help.
So I have a contacts table which a has a phone_number_id field. phone_number table has name and number fields
Current attempt until I realized I have a nested array:
#phone_number = Phone_Number.new
#phone_number.contact_id = #contact.id
#phone_number.name = #params[:phone_number_name]
Here is The solution I came up with
if #contact.save
#params[:phone_numbers].each do |counter|
#phone = PhoneNumber.new
#phone.contact_id = #contact.id
#phone.name = counter[:name]
#phone.number = counter[:number]
#phone.save
end

How to flatten a JSON response when using ActiveResource?

I have an API and a client app, and I am using rails with ActiveResource.
I have a Recruiter model that inherits from ActiveResource::Base
Let's say on the client side I write:
dave = Recruiter.new(email: "email#recruiter.com", password: "tyu678$--è", full_name: "David Blaine", company: "GE")
dave.save
The request I send is formatted like so:
{"recruiter":{
"email": "email#recruiter.com",
"password": "tyu678$--è",
"full_name": "David Blaine",
"company": "GE"
}
}
and the Json response I get from the API is formatted like:
{"recruiter":{
"email": "email#recruiter.com",
"password": "tyu678$--è",
"full_name": "David Blaine",
"company": "GE",
"foo": "bar"
},
"app_token":"ApfXy8YYVtsipFLvJXQ"
}
The problem is that this will let me access the app token with dave.app_token but I can't for instance write dave.foo, which raises an error.
Is there a way to flatten the response or read through it recursively si that I can access all of my instance's attributes while keeping the API response formatted as it is?
Looking through the whole ActiveResource process, you can overwrite the load method in your Recruiter model.
I just added the code in the #HACK section which "flatten" your attributes.
def load(attributes, remove_root = false, persisted = false)
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
#prefix_options, attributes = split_options(attributes)
# HACK
if !attributes[:app_token].nil?
attributes[:recruiter]["app_token"] = attributes[:app_token]
attributes = attributes[:recruiter]
end
# /HACK
if attributes.keys.size == 1
remove_root = self.class.element_name == attributes.keys.first.to_s
end
attributes = ActiveResource::Formats.remove_root(attributes) if remove_root
attributes.each do |key, value|
#attributes[key.to_s] =
case value
when Array
resource = nil
value.map do |attrs|
if attrs.is_a?(Hash)
resource ||= find_or_create_resource_for_collection(key)
resource.new(attrs, persisted)
else
attrs.duplicable? ? attrs.dup : attrs
end
end
when Hash
resource = find_or_create_resource_for(key)
resource.new(value, persisted)
else
value.duplicable? ? value.dup : value
end
end
self
end

Stripe_ruby Unable to store card_last4 and card_type after successful customer creation

After creating a customer successfully, I can inspect the object with:
Rails.logger.debug("single card object has: #{customer.cards.data.card.inspect}")
which returns a json like this:
#<Stripe: : Customer: 0x2801284>JSON: {
"id": "cus_2WXxmvhBJgSmNY",
"object": "customer",
"cards": {
"object": "list",
"data": [
{
"id": "card_2WXxcCsdY0Jjav",
"object": "card",
"last4": "4242",
"type": "Visa",
"exp_month": 1,
"exp_year": 2014,
}
]
},
"default_card": "card_2WXxcCsdY0Jjav"
}
But I will do Customer.cards.data.last4 it gives a NOMethodError.
If I remove the last4 and just call Customer.cards.data, it gives
#<Stripe: : Card: 0x1ed7dc0>JSON: {
"id": "card_2Wmd80yQ76XZKH",
"object": "card",
"last4": "4242",
"type": "Visa",
"exp_month": 1,
"exp_year": 2015,
}
Now I seem to have the direct card object but if I do
card = Customer.cards.data
self.last4 = card.last4
I still get a noMethodError
Here is shortened version of my model:
class Payment < ActiveRecord::Base
def create_customer_in_stripe(params)
if self.user.stripe_card_token.blank?
user_email = self.user.email
customer = Stripe::Customer.create(email: user_email, card: params[:token])
card = customer.cards.data
self.card_last4 = card.last4
self.card_type = card.type
self.card_exp_month = card.exp_month
self.card_exp_year = card.exp_year
self.user.save
end
self.save!
end
end
customer.cards, as the name implies, returns multiple cards in an array.
You can't call card accessor methods because you don't have a Stripe::Card object; you have an array of Stripe::Card objects. You need to either call customer.cards.first (the most likely answer) or iterate over the array for a specific card you're looking for.
Once you have a Stripe::Card object, all the accessor methods will work correctly.
cself.card_last4 = card.last4 should be self.card_last4 = card["last4"] as the gem itself doesn't have a last4 method when searching on github. I know i have to use Hash syntax.
I have a feeling that all of your methods on card will need this syntax.
EDit:
So it sounds like your model's last4 column is an integer, do card["last4"].to_i or change the migration to a string column in the DB.
card = Customer.cards.data
self.last4 = card[0].last4
how do you get the default active card in
rails "default_card": "card_2WXxcCsdY0Jjav" in the list of customer.cards?
is there a better way rather than to loop thru customer.cards to get it or even easier way?
Any pointers?
Hope this help someone who is wondering as well :- )
default_active_card = customer.cards.data.detect{|obj| obj[:id] == customer.default_card}

What's the optimal way to deal with non-existent variables in Rails?

I'm working on a Rails app that pulls data in from Groupon's API and displays them on our site.
Take the follow data structure, for example:
---
- "id": deal one
"options":
"redemptionLocations":
- "streetAddress1": 123 Any Street"
- "id": deal two
"options": []
If I wanted to loop through each deal, and display the streetAddress1 if it exists, what's the optimal way to do that in Rails?
Just do:
if(defined? streetAddress1) then
print streetAddress1 + " is set"
end
Hope it helps
The best practice should be to use present?:
puts "It is #{object.attribute}" if object.attribute.present?
If you have an array of objects and want to loop only over those that have the attribute set, you can use select:
array.select{|object| object.attribute.present?}.each do |object|
...
end
If you have a deeply nested structure you can create a custom function to check if a key exists and display its value:
def nested_value hash, *args
tmp = hash
args.each do |arg|
return nil if tmp.nil? || !tmp.respond_to?(:[]) || (tmp.is_a?(Array) && !arg.is_a?(Integer))
tmp = tmp[arg]
end
tmp
end
For example, if you have the following YAML loaded from your example:
k = [
{ "id"=>"deal one",
"options"=>{"redemptionLocations"=>[{"streetAddress1"=>"123 Any Street\""}]}},
{ "id"=>"deal two",
"options"=>[]}]
Then you can do this:
nested_value k.first, 'options', 'redemptionLocations', 0, 'streetAddress1'
=> "123 Any Street \""
nested_value k.last, 'options', 'redemptionLocations', 0, 'streetAddress1'
=> nil

Resources