The following function allows me to build a query consisting of a url and a number of options:
require 'cgi'
require 'openssl'
require 'open-uri'
def function(url, options={})
key = '...'
secret = '...'
parameters = {
:url => url,
:param1 => options[:param1],
:param2 => options[:param2]
}
query = parameters.
sort_by {|s| s[0].to_s }.
select {|s| s[1] }.
map {|s| s.map {|v| CGI::escape(v.to_s) }.join('=') }.
join('&')
secret_key = Digest::MD5.hexdigest(url + secret_keyword)
"https://domain.com/action?key=#{access_key}&secret=#{secret}&#{query}"
end
puts function "www.domain.com"
In the last line (puts function "www.domain.com") the url is defined - but how can I define the options?
I tried something like:
puts function ("www.domain.com", param1 = "1", param2 = "2")
but this is obviously wrong. Please excuse my lack of Ruby knowledge. Hope someone can help!
Try like this,
puts function("www.domain.com", { param1: "1", param2: "2"})
Related
I'm storing configuration data in hashes written in flat files. I want to import the hashes into my Class so that I can invoke corresponding methods.
example.rb
{
:test1 => { :url => 'http://www.google.com' },
:test2 => {
{ :title => 'This' } => {:failure => 'sendemal'}
}
}
simpleclass.rb
class Simple
def initialize(file_name)
# Parse the hash
file = File.open(file_name, "r")
#data = file.read
file.close
end
def print
#data
end
a = Simple.new("simpleexample.rb")
b = a.print
puts b.class # => String
How do I convert any "Hashified" String into an actual Hash?
You can use eval(#data), but really it would be better to use a safer and simpler data format like JSON.
You can try YAML.load method
Example:
YAML.load("{test: 't_value'}")
This will return following hash.
{"test"=>"t_value"}
You can also use eval method
Example:
eval("{test: 't_value'}")
This will also return same hash
{"test"=>"t_value"}
Hope this will help.
I would to this using the json gem.
In your Gemfile you use
gem 'json'
and then run bundle install.
In your program you require the gem.
require 'json'
And then you may create your "Hashfield" string by doing:
hash_as_string = hash_object.to_json
and write this to your flat file.
Finally, you may read it easily by doing:
my_hash = JSON.load(File.read('your_flat_file_name'))
This is simple and very easy to do.
Should it not be clear, it is only the hash that must be contained in a JSON file. Suppose that file is "simpleexample.json":
puts File.read("simpleexample.json")
# #{"test1":{"url":"http://www.google.com"},"test2":{"{:title=>\"This\"}":{"failure":"sendemal"}}}
The code can be in a normal Ruby source file, "simpleclass.rb":
puts File.read("simpleclass.rb")
# class Simple
# def initialize(example_file_name)
# #data = JSON.parse(File.read(example_file_name))
# end
# def print
# #data
# end
# end
Then we can write:
require 'json'
require_relative "simpleclass"
a = Simple.new("simpleexample.json")
#=> #<Simple:0x007ffd2189bab8 #data={"test1"=>{"url"=>"http://www.google.com"},
# "test2"=>{"{:title=>\"This\"}"=>{"failure"=>"sendemal"}}}>
a.print
#=> {"test1"=>{"url"=>"http://www.google.com"},
# "test2"=>{"{:title=>\"This\"}"=>{"failure"=>"sendemal"}}}
a.class
#=> Simple
To construct the JSON file from the hash:
h = { :test1=>{ :url=>'http://www.google.com' },
:test2=>{ { :title=>'This' }=>{:failure=>'sendemal' } } }
we write:
File.write("simpleexample.json", JSON.generate(h))
#=> 95
I'm sending a POST request using HTTParty. One of the variables required is an array. This is the code I'm using to send:
response = HTTParty.post url, :body =>
{"key"=>'XYZ123',
"content"=>
[{"placename"=>"placeholder",
"placecontent"=>"sample content"}],
etc. }
The API needs to see:
"content": [
{
"placename": "placeholder",
"placecontent": "sample content"
}
],
However, when I check the request received logs on the API, I see that my code is producing:
"content": [
{
"placename": "placeholder"
},
{
"placecontent": "sample content"
}
],
How can I stop the array record from being split in two?
Thanks.
EDIT:
The desired output of the code is the equivalent of:
...&content[0][placename]=placeholder&content[0][placecontent]=sample%20content...
By default, HTTParty uses HashConversions to convert a Hash body to parameters:
Examples:
{ :name => "Bob",
:address => {
:street => '111 Ruby Ave.',
:city => 'Ruby Central',
:phones => ['111-111-1111', '222-222-2222']
}
}.to_params
#=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111
Ruby Ave."
You can override this with your own convertor by using HTTParty.query_string_normalizer:
Override the way query strings are normalized. Helpful for overriding
the default rails normalization of Array queries.
For a query:
get '/', :query => {:selected_ids => [1,2,3]}
The default query string normalizer returns:
/?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
Let’s change it to this:
/?selected_ids=1&selected_ids=2&selected_ids=3
Pass a Proc to the query normalizer which accepts the yielded query.
#example Modifying Array query strings
class ServiceWrapper
include HTTParty
query_string_normalizer proc { |query|
query.map do |key, value|
value.map {|v| "#{key}=#{v}"}
end.join('&')
}
end
#param [Proc] normalizer custom query string normalizer. #yield [Hash,
String] query string #yieldreturn [Array] an array that will later be
joined with ‘&’
or simply pass it in your options:
response = HTTParty.post url, :body =>
{"key"=>'XYZ123',
"content"=>
[{"placename"=>"placeholder",
"placecontent"=>"sample content"}]},
:query_string_normalizer => -> (h) { ... your own implementation here ...}
To get a serialization of a[1]=val1&a[2]=val2 instead of a[]=val1&a[]=val2 you can create your own HashConversions based on the current one
class MyHashConversions
def to_params(hash)
params = hash.map { |k,v| normalize_param(k,v) }.join
params.chop! # trailing &
params
end
def normalize_param(key, value)
param = ''
stack = []
if value.is_a?(Array)
#### THE CHANGE IS HERE
param << value.each_with_index.map { |element, i| normalize_param("#{key}[#{i}]", element) }.join
####
elsif value.is_a?(Hash)
stack << [key,value]
else
param << "#{key}=#{URI.encode(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&"
end
stack.each do |parent, hash|
hash.each do |k, v|
if v.is_a?(Hash)
stack << ["#{parent}[#{k}]", v]
else
param << normalize_param("#{parent}[#{k}]", v)
end
end
end
param
end
end
The code above is not tested, but if it works, and is generic enough, you might consider forking the project and github, making the fix there, so it will work out of the box :)
I've scrapped the part of the data from the pages with Nokogiri .
require 'net/http'
require 'nokogiri'
require 'open-uri'
require 'json'
sources = {
cb: "http://www.cbbankmm.com/fxratesho.php",
}
puts "Currencies from CB Bank are"
if #page = Nokogiri::HTML(open(sources[:cb]))
(1..3).each do |i|
puts #page.css("tr")[i].text.gsub(/\s+/,'')
end
end
The result is
Currencies from CB Bank are
USD873883
SGD706715
EURO11241135
I would like to format the output to the below JSON format
{
"bank":"CB",
"rates": {
"USD":"[873,883]",
"SGD":"[706,715]",
"EURO":"[1124,1135]"
}
}
Which gems, method do I have to use to get the above Hash or JSON format?
Some abstraction might be an idea. So, perhaps a class to help you with the job:
class Currencies
def initialize(page, bank)
#page = page
#bank = bank
end
def parsed
#parsed ||= #page.css("tr").collect{ |el| el.text.gsub(/\s+/,'') }
end
def to_hash
{
bank: #bank,
rates: {
USD: usd,
SGD: sgd,
....
}
}
end
def usd
parsed[0].gsub(/^USD/, '')
end
def sgd
parsed[1].gsub(/^SGD/, '')
end
...
end
Use it like this
Currencies.new(Nokogiri::HTML(open(sources[:cb])), "CB").to_hash.to_json
Just make an equivalent hash structure in Ruby, and do e.g.
hash = {
"bank" => "CB",
"rates" => {
"USD" => "[873,883]",
"SGD" => "[706,715]",
"EURO" => "[1124,1135]"
}
}
hash.to_json
You are already including the json gem. Obviously you build the Ruby hash up in places where you currently have puts statements.
Edit: If the layout is important to you, you may prefer:
JSON.pretty_generate( hash )
What's the simplest method to convert YAML to dot-separated strings in Ruby?
So this:
root:
child_a: Hello
child_b:
nested_child_a: Nesting
nested_child_b: Nesting Again
child_c: K
To this:
{
"ROOT.CHILD_A" => "Hello",
"ROOT.CHILD_B.NESTED_CHILD_A" => "Nesting",
"ROOT.CHILD_B.NESTED_CHILD_B" => "Nesting Again",
"ROOT.CHILD_C" => "K"
}
It's not a one-liner, but perhaps it will fit your needs
def to_dotted_hash(source, target = {}, namespace = nil)
prefix = "#{namespace}." if namespace
case source
when Hash
source.each do |key, value|
to_dotted_hash(value, target, "#{prefix}#{key}")
end
when Array
source.each_with_index do |value, index|
to_dotted_hash(value, target, "#{prefix}#{index}")
end
else
target[namespace] = source
end
target
end
require 'pp'
require 'yaml'
data = YAML.load(DATA)
pp data
pp to_dotted_hash(data)
__END__
root:
child_a: Hello
child_b:
nested_child_a: Nesting
nested_child_b: Nesting Again
child_c: K
prints
{"root"=>
{"child_a"=>"Hello",
"child_b"=>{"nested_child_a"=>"Nesting", "nested_child_b"=>"Nesting Again"},
"child_c"=>"K"}}
{"root.child_c"=>"K",
"root.child_b.nested_child_a"=>"Nesting",
"root.child_b.nested_child_b"=>"Nesting Again",
"root.child_a"=>"Hello"}
I would like to take out a parameter from a URL by its name without knowing which parameter it is, and reassemble the URL again.
I guess it is not that hard to write something on my own using CGI or URI, but I imagine such functionality exists already. Any suggestions?
In:
http://example.com/path?param1=one¶m2=2¶m3=something3
Out:
http://example.com/path?param2=2¶m3=something3
I prefer to use:
require 'addressable/uri'
uri = Addressable::URI.parse('http://example.com/path?param1=one¶m2=2¶m3=something3')
params = uri.query_values #=> {"param1"=>"one", "param2"=>"2", "param3"=>"something3"}
params.delete('param1') #=> "one"
uri.query_values = params #=> {"param2"=>"2", "param3"=>"something3"}
uri.to_s #=> "http://example.com/path?param2=2¶m3=something3"
Maybe a little off-topic, but for anyone who's attempting to do this in the context of a rails app you can simply do:
url_for(params.except(:name_of_param_to_delete))
N.B. Tested in rails v2.3.9.
If you don't want to include an extra Gem and if you don't want nasty Regex, here's my prefered way:
require 'cgi'
require 'uri'
url = "http://example.com/path?param1=one¶m2=2¶m3=something3"
uri = URI(url) #=> #<URI::HTTP:0x007fbe25141a78 URL:http://example.com/path?param1=one¶m2=2¶m3=something3>
params = Rack::Utils.parse_nested_query(uri.query || "") #=> {"param1"=>["one"], "param2"=>["2"], "param3"=>["something3"]}
params.delete('param1') #=> ["one"]
uri.query = URI.encode_www_form(params) #=> "param2=2¶m3=something3"
uri.to_s #=> "http://example.com/path?param2=2¶m3=something3"
Note, choose wisely:
CGI.parse('a=b&a=c') #=> {"a"=>["b", "c"]}
Rack::Utils.parse_nested_query('a=b&a=c') #=> {"a"=>"c"}
And:
URI.encode_www_form({ a: :b, c: { d: 2 }}) #=> "a=b&c=%7B%3Ad%3D%3E2%7D"
{ a: :b, c: { d: 2 }}.to_query #=> "a=b&c%5Bd%5D=2"
The addressable gem will do this nicely; please see the superior answer by The Tin Man. But if you want to roll your own, here's how. The only claim this code has to elegance is that it hides the ugly in a method:
#!/usr/bin/ruby1.8
def reject_param(url, param_to_reject)
# Regex from RFC3986
url_regex = %r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$"
raise "Not a url: #{url}" unless url =~ url_regex
scheme_plus_punctuation = $1
authority_with_punctuation = $3
path = $5
query = $7
fragment = $9
query = query.split('&').reject do |param|
param_name = param.split(/[=;]/).first
param_name == param_to_reject
end.join('&')
[scheme_plus_punctuation, authority_with_punctuation, path, '?', query, fragment].join
end
url = "http://example.com/path?param1=one¶m2=2¶m3=something3"
p url
p reject_param(url, 'param2')
# => "http://example.com/path?param1=one¶m2=2¶m3=something3"
# => "http://example.com/path?param1=one¶m3=something3"
I came up with something like this
def uri_remove_param(uri, params = nil)
return uri unless params
params = Array(params)
uri_parsed = URI.parse(uri)
return uri unless uri_parsed.query
escaped = uri_parsed.query.grep(/&/).size > 0
new_params = uri_parsed.query.gsub(/&/, '&').split('&').reject { |q| params.include?(q.split('=').first) }
uri = uri.split('?').first
amp = escaped ? '&' : '&'
"#{uri}?#{new_params.join(amp)}"
end
One line should be enough:
url.sub(/\?param_to_remove=[^&]*/, '?').sub(/\¶m_to_remove=[^&]*/, '').sub(/\?$/,'')
Here's a clean one if your url is a string:
def remove_param(url, param_name)
uri = URI(url)
params = []
URI.decode_www_form(uri.query || '').each do |param|
next if param[0] == param_name
params << param
end
uri.query = URI.encode_www_form(params)
uri.to_s
end
note: you could use this to remove a param with a specific value by changing:
def remove_param(url, param_value)
and
next if param[1] == param_value