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 )
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
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"})
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 think I misunderstand hashes as concerns the code below:
require 'rest-client'
require 'json'
def get_from_mashable
res = JSON.load(RestClient.get('http://mashable.com/stories.json'))
res["hot"].map do |story|
s = {title: story["title"], category: story["channel"]}
add_upvotes(s)
end
end
def add_upvotes(hash)
hash.map do |story|
temp = {upvotes: 1}
if story[:category] == "Tech"
temp[:upvotes] *= 10
elsif story[:category] == "Business"
temp[:upvotes] *= 5
else
temp[:upvotes] *= 3
end
end
hash.each {|x| puts x}
end
get_from_mashable()
I get the following error from this:
ex_teddit_api_news.rb:16:in `[]': no implicit conversion of Symbol into Integer (TypeError)
I am trying to add an upvotes key and corresponding integer value into each hash created from the JSON object in get_from_mashable. In the loop, I am not trying to erase the content of each hash and replace it with only the new key/value pair, which I have a feeling I may be doing.
Any help is appreciated.
Since you have not provided sufficient information, we can only guess, but most likely, story is an array, not a hash.
This returns an array of hash, where each hash has the keys title, category and upvotes.
require 'rest-client'
require 'json'
def get_from_mashable
res = JSON.load(RestClient.get('http://mashable.com/stories.json'))
res["hot"].map do |story|
s = {title: story["title"], category: story["channel"], upvotes: get_upvotes(story["channel"]) }
end
end
def get_upvotes(category)
case category
when "Tech"
10
when "Business"
5
else
3
end
end
get_from_mashable()
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"}