How to remove special characters from params hash? - ruby-on-rails

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
#...

Related

Why can't I access contents of flash when it's a hash?

According to the Flash documentation, I should be able to pass strings, arrays or hashes through Flash. Strings and arrays work fine but hashes aren't working.
Here's a stripped down (but still failing) version of my code:
Flash messages partial
# views/layouts/flash_messages.html.haml
- flash.each do |type, content|
- message = content if content.class == String
- message, detail = content if content.class == Array
- message, detail = content[:message], content[:detail] if content.class == Hash
- if message || detail
.alert
%h3= message if message.present?
%p= detail if detail.present?
Home controller
class HomeController < ApplicationController
def index
end
def test_string
redirect_to root_url, alert: 'Plain string works'
end
def test_array
redirect_to root_url, alert: ['Array works', 'Tested with 0, 1, 2 or more items without problems']
end
def test_hash
redirect_to root_url, alert: {message: 'Hash not working :(', detail: 'Even though I think it should'}
end
end
The problem seems to be with the assignment line in the case of the hash, the keys are present but method and detail always end up nil for hashes. But when I try the same code in the console it works fine...
IRB
irb> content = { message: 'This hash works...', detail: '...as expected' }
=> {:message=>"This hash works...", :detail=>"...as expected"}
irb> message, detail = content[:message], content[:detail] if content.class == Hash
=> [ 'This hash works...', '...as expected' ]
irb> message
=> 'This hash works...'
irb> detail
=> '...as expected'
Closer inspection revealed that, while the keys were indeed set, they'd been converted from symbols to strings.
To fix this I had to change line 4 of the controller from symbols to strings:
- message, detail = content[:message], content[:detail] if content.class == Hash
- message, detail = content['message'], content['detail'] if content.class == Hash
If I understand correctly, this is a result of flashes being stored in the session and the session object being stored in cookies as JSON objects. JSON doesn't support symbolised keys.
As an experiment I tried setting matching string and symbol keys. If you try doing both in one assignment, Ruby takes the first key and the second value (with a warning):
irb> content = { message: 'Symbol key first', 'message': 'String key second' }
=> warning: key :message is duplicated and overwritten on line X
=> {:message=>"String key second"}
But if you deliberately duplicate the keys in a hash passed to flash, whichever one is defined last "wins" (in my limited testing, but it makes sense given hashes are most likely iterated in insertion order):
symbol_first = {}
symbol_first[:message] = 'Symbol wins'
symbol_first['message'] = 'String wins'
flash[:alert] = symbol_first # 'String wins' is displayed
string_first = {}
string_first['message'] = 'String wins'
string_first[:message] = 'Symbol wins'
flash[:alert] = string_first # 'Symbol wins' is displayed

Special character not allowed error on website while intregating paytm

I want to integrate paytm using ruby on rails for web only, now the problem is that when I fetched the values of attributes form database like MID, CUST_ID, WEBSITE, CALLBACK_URL, INDUSTRY_TYPE_ID and some others attributes, then one problem is occurring which is below.
Attributes:
paramList["WEBSITE"] = "WEBSTAGING"
Error:
invalid: Special character not allowed
def start_payment
unless #paytm_keys.nil?
paramList = Hash.new
paramList["MID"] = ""
paramList["ORDER_ID"] = "#{Time.now.to_i.to_s}"
paramList["CUST_ID"] = "#{Time.now.to_i.to_s}"
paramList["INDUSTRY_TYPE_ID"] = #paytm_keys.industry_type_id
paramList["CHANNEL_ID"] = #paytm_keys.channel_id
paramList["TXN_AMOUNT"] = #payable_amount
paramList["MOBILE_NO"] = #paytm_keys.mobile_number
paramList["EMAIL"] = #paytm_keys.email
paramList["WEBSITE"] = "WEBSTAGING"
paramList["CALLBACK_URL"] = #paytm_keys.paytm_url
#paramList=paramList
puts #paramList
#checksum_hash = generate_checksum()
respond_to do |format|
format.js
format.html
end
else
redirect_to new_checkout_path, alert: "Right now you don't have to pay."
end
end
It seems there are some special characters in one of the params you're passing in your hash. Have a look at the output of
puts #paramList
It might be helpful if you could post this in your question, but you probably want to obfuscate real credentials by perhaps substituting or removing all valid alphanumeric characters from your puts so you can give us the output of:
puts paramList.to_s.gsub(/\w/, '')
# this should only contain non alphanumeric characters which
# would be safe to post in your question.

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

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.

Problems querying JSON nested hash response in Ruby

Cannot seem to solve this problem:
I'm getting JSON nested hash responses from Lastfm and everything works fine when the response is structures as such:
{"topalbums" =>{"album" =>{"name =>"Friday Night in Dixie"}}}
However if the artist does not have a top album the response is structured this way and I get a NoMethodError undefined method '[]' for nil:NilClass.
{"topalbums" =>{"#text"=>"\n ", "artist"=>"Mark Chestnutt"}}
What I want to do is query the response so I do not keep getting this error.
Here is my method:
def get_albums
#albums = Array.new
#artistname.each do |name|
s = LastFM::Artist.get_top_albums(:artist => name, :limit => 1)
r = JSON.parse(s.to_json)['topalbums']['album']['name']
#albums.push(r)
end
end
which gives me exactly what I want if the artist has a top album, what I need to do is somehow add a condition to query the keys in the nested hash. However, I cannot seem to grasp how to do this as when I add this line of code to check key values:
s.each_key { |key, value| puts "#{key} is #{value}" }
the output I get is this:
topalbums is
so topalbums key does not have a value associated with it.
This is what I have tried so far:
def get_albums
#albums = Array.new
#artistname.each do |name|
s = LastFM::Artist.get_top_albums(:artist => name, :limit => 1)
if s.has_key?('album') #I know this won't work but how can I query this?
r = JSON.parse(s.to_json)['topalbums']['album']['name']
#albums.push r
else
#albums.push(name << "does not have a top album")
end
end
end
How can I fix this so I get 'Mark Chestnut does not have a top album' instead of the NoMethodError? Cheers
Use Hash#fetch default values, I would do as below:
No "album" key present
hash = {"topalbums" =>{"#text"=>"\n ", "artist"=>"Mark Chestnutt"}}
default_album = {"name" => "does not have a top album"}
hash["topalbums"].fetch("album", default_album)["name"]
#=> "does not have a top album"
"album" key present
hash = {"topalbums" =>{"#text"=>"\n ", "artist"=>"Mark Chestnutt", "album" => {"name" => "Foo"}}}
hash["topalbums"].fetch("album", default_album)["name"]
#=> "Foo"
So if the hash does not have an "album" key fetch defaults to default_album else it uses the key it find as in the second case

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

Resources