Remote API method won't create records based on received params - ruby-on-rails

I have an api for mass creating records of a model Series. A remote machine sends a POST request to my url, with an array #series passed as a json parameter, like this:
#series = [{:id=>1,name:"test"}, {:id=>2,name:"test2"}]
req = Net::HTTP::Post.new(post_uri, 'Content-Type' => 'application/json')
req.body = {series: #series}.to_json
res = http.request(req)
but I cannot for the life of me get the respective Series to be created. Here is the method that receives the data and is supposed to create one Series for each hash in the #series array:
def api
series = params[:series]
series.each do |s|
name = s[:name]
if !Series.where(name: name).exists?
Series.create(s)
end
end
end
The params are definitely passed through, but no Series are created. When I check my logs there's a 500 error, but since it's remote, I have no way of getting a more specific error.
When I remove the params and just create a generic Series for each hash in the #series array, it works. For example, with the following code, if #series has 3 hashes, 3 Series are created:
def post_product_data
series = params[:series]
series.each do |s|
name = s[:name]
if !Series.where(name: name).exists?
Series.create #GENERIC SERIES NOT BASED ON PARAMS
end
end
end
I thought it might be a permissions issue, so I tried permitting all params. But when I changed the method to this, I got a "undefined method "permit!" error:
def post_product_data
series = params[:series]
series.each do |s|
name = s[:name]
if !Series.where(name: name).exists?
Series.create(s.permit!) #TRIED PERMITTING ALL PARAMS
end
end
end
Anyone have any ideas?
UPDATE
I changed the offending line to this:
Series.create({id: s[:id], name: s[:name]})
and now it works. I have no idea why, since the hashes should have been inserting the exact same thing. But at least it works finally.

Related

How to get push value key in Firebase Ruby REST wrapper

I am working on a project to do CRUD Operations to firebase. I made use of this to help facilitate and link my ruby project to firebase.
Functions:
def delete_firebase(event_params,rootpath="Events/")
query = init_firebase.delete(rootpath,event_params)
end
def new_firebase(event_params,rootpath="Events")
query = init_firebase.push(rootpath,event_params)
end
def init_firebase # Inits firebase project with URL and secret
firebaseURL = "myfirebaseprojecturl"
firebaseSecret = "myfirebasesecret"
firebase = Firebase::Client.new(firebaseURL, firebaseSecret)
end
Event params consist of my event parameters as shown below
def event_params
params.require(:event).permit(:eventID, :eventName, :attachment, :eventNoOfPpl, :eventAdminEmail, {eventpics: []})
end
I encountered an issue. When I push with push() into firebase, there is a random key like -LSFOklvcdmfPOWrxgBo. In such case, the structure of the document would look like this:
But I cannot delete anything from -LSFOklvcdmfPOWrxgBo as I do not have the value. I used delete() from Oscar's firebase-ruby gem. I would appreciate any help with this issue.
I re-read the gem docs, and got some help from my friends and came up with two solutions
The body's response has response.body # => { 'name' => "-INOQPH-aV_psbk3ZXEX" } and thus, you're able to find out the name if you'd like
Change the index, and don't use .push, instead I made use of .set and did a random number for every event
Final solution
def load_firebase(root_path = "Events")
firebase_json = init_firebase.get(root_path)
if valid_json?(firebase_json.raw_body)
#json_object = JSON.parse(firebase_json.raw_body)
end
end
def update_firebase(event_params, root_path = "Events/")
init_firebase.update("#{root_path}#{event_params["eventID"]}", event_params)
end
def delete_firebase(event_params, root_path = "Events/")
init_firebase.delete("#{root_path}#{event_params["eventID"]}")
end
def save_firebase(event_params, root_path = "Events/")
init_firebase.set("#{root_path}#{event_params["eventID"]}", event_params)
end

Rails Httparty JSON to params to save

Rails 4.5 Ruby 2.3.1
I am getting json from an API and trying to store the following into a model CLrates
1. timestamp as unix time (date)
2. Currency_code (string)
3. quote (decimal monetary value)
I can use the following in irb after parsing the json and know how to get the elements individually using: response["quotes"]. How can I generate params to be saved in the model above when the body is as follows:
irb(main):036:0> puts response.body
{
"success":true,
"terms":"https:\/\/xxxxx.com\/terms",
"privacy":"https:\/\/xxxxx.com\/privacy",
"timestamp":1504817289,
"source":"USD",
"quotes":{
"USDAED":3.672703,
"USDAFN":68.360001,
"USDCUC":1,
"USDCUP":26.5,
"USDCVE":91.699997,
"USDCZK":21.718701,
............ many more lines removed for brevity
"USDZWL":322.355011
}
I can do this using a separate associated model but have very little idea how to create the params to save to a single table.
The following links got me to this point and well worth a read if you need info on httparty GET (client):
1. http://www.rubydoc.info/github/jnunemaker/httparty/HTTParty/
2. http://eric-price.net/blog/rails-api-wrapper/
3. https://www.driftingruby.com/episodes/playing-with-json
The class and method in lib/clayer.rb:
class clayer
include HTTParty
format :json
read_timeout 10
def self.get_quotes
response = HTTParty.get('http://www.nnnnnnn.net/api/live?
access_key=nnnnnnnnnn&format=1')
end
end
I used irb as I am still learning how to run this through rails c. This will be called in the controller and saved however need to work out how to get the params from the json
Thanks for the help
OK: after digging I think I am on the right track
I get the response["QUOTES"], loop through them and build the params required saving each at the end of the loop
rates = response["QUOTES"]
rates.each do |k,v|
clrate = Realtimerates.new
clrate.date = response["timestamp"]
clrate.countrycode = "#{k}"
clrate.price = "#{v}"
clrate.save
end
Going to give this a whirl
In model
class Realtimerate < ActiveRecord::Base
include HTTParty
format :json
read_timeout 5
def self.get_cl_rates
response = HTTParty.get('http://www.mmmmm.net/api/live?access_key="key"&format=1')
rates = response["quotes"]
rates.each do |k,v|
create!(
date: Time.at(response["timestamp"]),
country_code: "#{k}",
price: "#{v}")
end
end
end
In the controller:
def index
Realtimerate.get_cl_rates
#realtimerates = Realtimerate.all
end
This is working and shows latest GET.
You already have a hash in your response.body. All you need to do now is to assign the relevant key-value to your model's attributes. e.g.
clrate = ClRate.new
res = response.body
clate.time = res["timestamp"]
...
clate.save

Build array of objects from params hash after Transaction block fails, Rails

I have a form where a user can update multiple resources at the same time. The transaction block makes the form atomic: if one validation fails for any of the resources being updated, then none of the resources get updated, and active record rollsback all changes.
When transaction fails, I want to render the form again, display the same input that the user entered along with errors next to each of the error input fields which prevented the transaction from going through.
The transaction block works. What I am having trouble with is building the array of objects from the params log. Each index of the array should contain a hash which holds key/value pairs of all the attributes of a specific resource.
UDPATE: BELOW IS THE ANSWER CODE THANKS TO THE RESPONSES
Code:
def update_multiple
begin
User.transaction do
params[:users].each do |k, v|
User.find(k).update!(v)
end
flash[:notice] = "Update Successful"
redirect_to :users and return
end
rescue
#users = []
params[:users].each do |k,v|
#users.push(User.new({:id => k}.merge(v)))
end
flash[:error] = "Errors found"
render :edit_multiple and return
end
end
And for good measure, here is what the passed in parameters looks like in the log. This transaction fails because the name attribute must be at least 3 characters long.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xyz=", "users"=>{"15"=>
{"name"=>"Neil", "age"=>"11"}, "16"=>{"name"=>"z", "age"=>"33"}, "17"=>
{"name"=>"John", "age"=>"99"}}, "commit"=>"Submit Changes"}
Thanks in advance! Any way to write this code better in ruby is much appreciated as well.
Ok, so you're trying to iterate through a list of objects in your params using a for_each and an external iterator, you really don't want to do that. I'd suggest something like this:
params[:users].each do |k,v|
# k is the "key" of each user while v is the values associated with each key
#users.push(User.new(:id => k, v)
# I'm doing this in my head so you might need:
# #users.push(User.new({:id => k}.merge(v))
# if you start getting errors about looking for a proper hash or something
# I can't remember how good Rails/Ruby is at recognizing nested hashes
end
That should produce a new user for each user object passed in using the ID provided and the values associated with each value.

Sending Simple Email in Rails

I've read a few questions and http://guides.rubyonrails.org/action_mailer_basics.html on how to send emails with Rails but can't seem to get it to work within the context of my current application.
I had an existing emailer.rb with a couple of methods that were identical apart from the parameters they accepted were named differently so I copied their format:
def quotation_notification(q)
#recipients = q.recipient_email
#from = q.partner_name + "<#{q.partner_email}>"
#subject = "New Quotation from " + q.partner_name
#body[:q] = q
end
I then created a new view file in emailers named quotation_notification.rhtml which just contains text for the moment.
I am then calling the function from inside a different controller and sending hardcoded parameters for now:
q = QuotationEmail.new(:recipient_email => 'martin#domain.co.uk', :partner_name => 'Martin Carlin', :partner_email => 'martin#domain.co.uk')
# send email
Emailer.deliver_quotation_notification(q)
Then finally, I created a new model for QuotationEmail
class QuotationEmail
def initialize(recipient_email, partner_name, partner_email)
#recipient_email = recipient_email
#partner_name = partner_name
#partner_name = partner_email
end
end
The error I get is ArgumentError (wrong number of arguments (1 for 3))
Eventually I'll be sending more parameters and hopefully attaching a pdf aswell but just trying to figure out why this isn't working first.
You are getting this error because while initialising QuotationEmail object though you think you're passing 3 params you're essentially passing only one parameter which is a hash. And initialize is expecting 3. See example below
class A
def initialize(a,b=1,c=2)
puts a
puts b
puts c
end
end
a = A.new(:recipient_email => 'martin#domain.co.uk', :partner_name => 'Martin Carlin', :partner_email => 'martin#domain.co.uk')
#=> {:recipient_email=>"martin#domain.co.uk", :partner_name=>"Martin Carlin", :partner_email=>"martin#domain.co.uk"}
#=> 1
#=> 2
If you're trying to use named parameters instead you'd need to redefine your initialize as
def initialize(recipient_email:a,partner_name:b,partner_email:c)
and invoke it as below -
a = A.new(recipient_email:'martin#domain.co.uk', partner_name:'Martin Carlin', partner_email:'martin#domain.co.uk')

How to remove special characters from params hash?

I have one application with the following code:
quantity = 3
unit_types = ['MarineTrac','MotoTrac','MarineTrac']
airtime_plan = 'Monthly Airtime Plan'
url = "http://localhost:3000/home/create_units_from_paypal?quantity=#{quantity}&unit_types=#{unit_types}&airtime_plan=#{airtime_plan}"
begin
resp = Net::HTTP.get(URI.parse(URI.encode(url.strip)))
resp = JSON.parse(resp)
puts "resp is: #{resp}"
true
rescue => error
puts "Error: #{error}"
return nil
end
It sends data to my other application via the URL params query string. This is what the controller method of that other application looks like:
def create_units_from_paypal
quantity = params[:quantity]
unit_types = params[:unit_types]
airtime_plan = params[:airtime_plan]
quantity.times do |index|
Unit.create! unit_type_id: UnitType.find_by_name(unit_types[index]),
airtime_plan_id: AirtimePlan.find_by_name(airtime_plan),
activation_state: ACTIVATION_STATES[:activated]
end
respond_to do |format|
format.json { render :json => {:status => "success"}}
end
end
I get this error:
<h1>
NoMethodError
in HomeController#create_units_from_paypal
</h1>
<pre>undefined method `times' for "3":String</pre>
<p><code>Rails.root: /Users/johnmerlino/Documents/github/my_app</code></p>
I tried using both raw and html_safe on the params[:quantity] and other params, but still I get the error. Note I had to use URI.encode(url) because URI.parse(url) returned bad uri probably because of the array of unit_types.
Change:
quantity.times do |index|
To:
quantity.to_i.times do |index|
The reason you are having this problem is because you are treating the params values as the types that you originally tried to send, but they are actually always going to be strings. Converting back to the expected 'type' solves your problem.
However, you have some more fundamental problems. Firstly, you are trying to send an array by simply formatting it to a string. However, this is not the format that the receiving application expects to translate back to an array. Secondly, there is duplication in your request - you don't need to specify a quantity. The length of the array itself is the quantity. A better method would be to build your url like this:
url = 'http://localhost:3000/home/create_units_from_paypal?'
url << URI.escape("airtime_plan=#{airtime_plan}") << "&"
url << unit_types.map{|ut| URI.escape "unit_types[]=#{ut}" }.join('&')
On the receiving side, you can do this:
def create_units_from_paypal
unit_types = params[:unit_types]
airtime_plan = params[:airtime_plan]
quantity = unit_types.try(:length) || 0
#...

Resources