Automatically Map JSON Objects into Instance Variables in Ruby - ruby

I would like to be able to automatically parse JSON objects into instance variables. For example, with this JSON.
require 'httparty'
json = HTTParty.get('http://api.dribbble.com/players/simplebits') #=> {"shots_count":150,"twitter_screen_name":"simplebits","avatar_url":"http://dribbble.com/system/users/1/avatars/thumb/dancederholm-peek.jpg?1261060245","name":"Dan Cederholm","created_at":"2009/07/07 21:51:22 -0400","location":"Salem, MA","following_count":391,"url":"http://dribbble.com/players/simplebits","draftees_count":104,"id":1,"drafted_by_player_id":null,"followers_count":2214}
I'd like to be able to do this:
json.shots_count
And have it output:
150
How could I possibly do this?

You should definitely use something like json["shots_counts"], but if you really need objectified hash, you could create a new class for this:
class ObjectifiedHash
def initialize hash
#data = hash.inject({}) do |data, (key,value)|
value = ObjectifiedHash.new value if value.kind_of? Hash
data[key.to_s] = value
data
end
end
def method_missing key
if #data.key? key.to_s
#data[key.to_s]
else
nil
end
end
end
After that, use it:
ojson = ObjectifiedHash.new(HTTParty.get('http://api.dribbble.com/players/simplebits'))
ojson.shots_counts # => 150

Well, getting what you want is hard, but getting close is easy:
require 'json'
json = JSON.parse(your_http_body)
puts json['shots_count']

Not exactly what you are looking for, but this will get you closer:
ruby-1.9.2-head > require 'rubygems'
=> false
ruby-1.9.2-head > require 'httparty'
=> true
ruby-1.9.2-head > json = HTTParty.get('http://api.dribbble.com/players/simplebits').parsed_response
=> {"shots_count"=>150, "twitter_screen_name"=>"simplebits", "avatar_url"=>"http://dribbble.com/system/users/1/avatars/thumb/dancederholm-peek.jpg?1261060245", "name"=>"Dan Cederholm", "created_at"=>"2009/07/07 21:51:22 -0400", "location"=>"Salem, MA", "following_count"=>391, "url"=>"http://dribbble.com/players/simplebits", "draftees_count"=>104, "id"=>1, "drafted_by_player_id"=>nil, "followers_count"=>2214}
ruby-1.9.2-head > puts json["shots_count"]
150
=> nil
Hope this helps!

Related

Create a Ruby Hash out of an xml string with the 'ox' gem

I am currently trying to create a hash out of an xml documen, with the help of the ox gem
Input xml:
<?xml version="1.0"?>
<expense>
<payee>starbucks</payee>
<amount>5.75</amount>
<date>2017-06-10</date>
</expense>
with the following ruby/ox code:
doc = Ox.parse(xml)
plist = doc.root.nodes
I get the following output:
=> [#<Ox::Element:0x00007f80d985a668 #value="payee", #attributes={}, #nodes=["starbucks"]>, #<Ox::Element:0x00007f80d9839198 #value="amount", #attributes={}, #nodes=["5.75"]>, #<Ox::Element:0x00007f80d9028788 #value="date", #attributes={}, #nodes=["2017-06-10"]>]
The output I want is a hash in the format:
{'payee' => 'Starbucks',
'amount' => 5.75,
'date' => '2017-06-10'}
to save in my sqllite database. How can I transform the objects array into a hash like above.
Any help is highly appreciated.
The docs suggest you can use the following:
require 'ox'
xml = %{
<top name="sample">
<middle name="second">
<bottom name="third">Rock bottom</bottom>
</middle>
</top>
}
puts Ox.load(xml, mode: :hash)
puts Ox.load(xml, mode: :hash_no_attrs)
#{:top=>[{:name=>"sample"}, {:middle=>[{:name=>"second"}, {:bottom=>[{:name=>"third"}, "Rock bottom"]}]}]}
#{:top=>{:middle=>{:bottom=>"Rock bottom"}}}
I'm not sure that's exactly what you're looking for though.
Otherwise, it really depends on the methods available on the Ox::Element instances in the array.
From the docs, it looks like there are two handy methods here: you can use [] and text.
Therefore, I'd use reduce to coerce the array into the hash format you're looking for, using something like the following:
ox_nodes = [#<Ox::Element:0x00007f80d985a668 #value="payee", #attributes={}, #nodes=["starbucks"]>, #<Ox::Element:0x00007f80d9839198 #value="amount", #attributes={}, #nodes=["5.75"]>, #<Ox::Element:0x00007f80d9028788 #value="date", #attributes={}, #nodes=["2017-06-10"]>]
ox_nodes.reduce({}) do |hash, node|
hash[node['#value']] = node.text
hash
end
I'm not sure whether node['#value'] will work, so you might need to experiment with that - otherwise perhaps node.instance_variable_get('#value') would do it.
node.text does the following, which sounds about right:
Returns the first String in the elements nodes array or nil if there is no String node.
N.B. I prefer to tidy the reduce block a little using tap, something like the following:
ox_nodes.reduce({}) do |hash, node|
hash.tap { |h| h[node['#value']] = node.text }
end
Hope that helps - let me know how you get on!
I found the answer to the question in my last comment by myself:
def create_xml(expense)
Ox.default_options=({:with_xml => false})
doc = Ox::Document.new(:version => '1.0')
expense.each do |key, value|
e = Ox::Element.new(key)
e << value
doc << e
end
Ox.dump(doc)
end
The next question would be how can i transform the value of the amount key from a string to an integer befopre saving it to the database

ruby object to_s gives unexpected output

What is the correct way to view the output of the puts statements below? My apologies for such a simple question.... Im a little rusty on ruby. github repo
require 'active_support'
require 'active_support/core_ext'
require 'indicators'
my_data = Indicators::Data.new(Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-08-25', :end_date => '2012-08-30').output)
puts my_data.to_s #expected to see Open,High,Low,Close for AAPL
temp=my_data.calc(:type => :sma, :params => 3)
puts temp.to_s #expected to see an RSI value for each data point from the data above
Maybe check out the awesome_print gem.
It provides the .ai method which can be called on anything.
An example:
my_obj = { a: "b" }
my_obj_as_string = my_obj.ai
puts my_obj_as_string
# ... this will print
# {
# :a => "b"
# }
# except the result is colored.
You can shorten all this into a single step with ap(my_obj).
There's also a way to return objects as HTML. It's the my_obj.ai(html: true) option.
Just use .inspect method instead of .to_s if you want to see internal properties of objects.

Extracting a url comprised of a hash in ruby

I have a query string that looks as follows:
http://localhost:3000/events?appointment_practices%5B10%5D=Injury&appointment_practices%5B18%5D=Immigration&appointment_practices%5B8%5D=Bankruptcy
appointment_practices is actually a hash I inserted into the query string during a redirect:
appointment_practices = practices.reduce({}) do |acc, practice|
acc[practice.id] = practice.class.name
acc
end
redirect_to events_path(appointment_practices: appointment_practices)
Now I want to parse that query string. When I tried to parse it with decode_www_form, it returns an array with a nil element:
[nil]
This is the code that is giving me the nil element:
#http_refer = #_env['HTTP_REFERER']
begin
uri = URI.parse #http_refer
practices = Hash[URI::decode_www_form(uri.query)].values_at('appointment_practices')
puts "practices: #{practices}"
rescue StandardError
end
I am trying to extract the hash. For example, in appointment_practices%5B10%5D=Injury, the id is 10 and the practice is Injury.
What other options do I have besides regex?
You can use Rack::Utils.parse_nested_query:
require 'uri'
require 'rack'
uri = URI.parse('http://localhost:3000/events?appointment_practices%5B10%5D=Injury&appointment_practices%5B18%5D=Immigration&appointment_practices%5B8%5D=Bankruptcy')
Rack::Utils.parse_nested_query(uri.query)
#=> {"appointment_practices"=>{"10"=>"Injury", "18"=>"Immigration", "8"=>"Bankruptcy"}}

How can I parse json and write that data to a database using Sinatra and DataMapper

I'm doing a proof of concept thing here and having a bit more trouble than I thought I was going to. Here is what I want to do and how I am currently doing it.
I am sending my Sinatra app a json file which contains the simple message below.
[
{
title: "A greeting!",
message: "Hello from the Chairman of the Board"
}
]
From there I have a post which I am using to take the params and write them to sqlite database
post '/note' do
data = JSON.parse(params) #<---EDIT - added, now gives error.
#note = Note.new :title => params[:title],
:message => params[:message],
:timestamp => (params[:timestamp] || Time.now)
#note.save
end
When I send the message the timestamp and the id are saved to the database however the title and message are nil.
What am I missing?
Thanks
Edit:
Now when I run my app and send it the json file I get this error:
C:/Users/Norm/ruby/Ruby192/lib/ruby/1.9.1/webrick/server.rb:183:in `block in start_thread'
TypeError: can't convert Hash into String
Edit 2: Some success.
I have the above json in a file call test.json which is the way the json will be posted. In order to post the file I used HTTPClient:
require 'httpclient'
HTTPClient.post 'http://localhost:4567/note', [ :file => File.new('.\test.json') ]
After thinking about it some more, I thought posting the file was the problem so I tried sending it a different way. The example below worked once I changed n my post /note handle to this:
data = JSON.parse(request.body.read)
My new send.rb
require 'net/http'
require 'rubygems'
require 'json'
#host = 'localhost'
#port = '4567'
#post_ws = "/note"
#payload ={
"title" => "A greeting from...",
"message" => "... Sinatra!"
}.to_json
def post
req = Net::HTTP::Post.new(#post_ws, initheader = {'Content-Type' =>'application/json'})
#req.basic_auth #user, #pass
req.body = #payload
response = Net::HTTP.new(#host, #port).start {|http| http.request(req) }
puts "Response #{response.code} #{response.message}:
#{response.body}"
end
thepost = post
puts thepost
So I am getting closer. Thanks for all the help so far.
Sinatra won't parse the JSON automatically for you, but luckily parsing JSON is pretty straightforward:
Start with requiring it as usual. require 'rubygems' if you're not on Ruby 1.9+:
>> require 'json' #=> true
>> a_hash = {'a' => 1, 'b' => [0, 1]} #=> {"a"=>1, "b"=>[0, 1]}
>> a_hash.to_json #=> "{"a":1,"b":[0,1]}"
>> JSON.parse(a_hash.to_json) #=> {"a"=>1, "b"=>[0, 1]}
That's a roundtrip use to create, then parse some JSON. The IRB output shows the hash and embedded array were converted to JSON, then parsed back into the hash. You should be able to break that down for your nefarious needs.
To get the fields we'll break down the example above a bit more and pretend that we've received JSON from the remote side of your connection. So, the received_json below is the incoming data stream. Pass it to the JSON parser and you'll get back a Ruby data hash. Access the hash as you would normally and you get the values:
>> received_json = a_hash.to_json #=> "{"a":1,"b":[0,1]}"
>> received_hash = JSON.parse(received_json) #=> {"a"=>1, "b"=>[0, 1]}
>> received_hash['a'] #=> 1
>> received_hash['b'] #=> [0, 1]
The incoming JSON is probably a parameter in your params[] hash but I am not sure what key it would be hiding under, so you'll need to figure that out. It might be called 'json' or 'data' but that's app specific.
Your database code looks ok, and must be working if you're seeing some of the data written to it. It looks like you just need to retrieve the fields from the JSON.

Is there a pluralize function in Ruby NOT Rails?

I am writing some Ruby code, not Rails, and I need to handle something like this:
found 1 match
found 2 matches
I have Rails installed so maybe I might be able to add a require clause at the top of the script, but does anyone know of a RUBY method that pluralizes strings? Is there a class I can require that can deal with this if the script isn't Rails but I have Rails installed?
Edit: All of these answers were close but I checked off the one that got it working for me.
Try this method as a helper when writing Ruby, not Rails, code:
def pluralize(number, text)
return text.pluralize if number != 1
text
end
Actually all you need to do is
require 'active_support/inflector'
and that will extend the String type.
you can then do
"MyString".pluralize
which will return
"MyStrings"
for 2.3.5 try:
require 'rubygems'
require 'active_support/inflector'
should get it, if not try
sudo gem install activesupport
and then the requires.
Inflector is overkill for most situations.
def x(n, singular, plural=nil)
if n == 1
"1 #{singular}"
elsif plural
"#{n} #{plural}"
else
"#{n} #{singular}s"
end
end
Put this in common.rb, or wherever you like your general utility functions and...
require "common"
puts x(0, 'result') # 0 results
puts x(1, 'result') # 1 result
puts x(2, 'result') # 2 results
puts x(0, 'match', 'matches') # 0 matches
puts x(1, 'match', 'matches') # 1 match
puts x(2, 'match', 'matches') # 2 matches
I personally like the linguistics gem that is definitely not rails related.
# from it's frontpage
require 'linguistics'
Linguistics.use :en
"box".en.plural #=> "boxes"
"mouse".en.plural #=> "mice"
# etc
This works for me (using ruby 2.1.1 and actionpack 3.2.17):
~$ irb
>> require 'action_view'
=> true
>> include ActionView::Helpers::TextHelper
=> Object
>> pluralize(1, 'cat')
=> "1 cat"
>> pluralize(2, 'cat')
=> "2 cats"
require 'active_support'
require 'active_support/inflector'
inf = ActiveSupport::Inflector::Inflections.new
to get the inflector, not sure how you use it
my solution:
# Custom pluralize - will return text without the number as the default pluralize.
def cpluralize(number, text)
return text.pluralize if number != 1
return text.singularize if number == 1
end
So you can have 'review' returned if you call cpluralize(1, 'reviews')
Hope that helps.
I've defined a helper function for that, I use it for every user editable model's index view :
def ovyka_counter(array, name=nil, plural=nil)
name ||= array.first.class.human_name.downcase
pluralize(array.count, name, plural)
end
then you can call it from the view :
<% ovyka_counter #posts %>
for internationalization (i18n), you may then add this to your locale YAML files :
activerecord:
models:
post: "Conversation"

Resources