Converting a nested OpenStruct to JSON - ruby-on-rails

It seems to be pretty easy to create nested OpenStruct objects using JSON.parse:
JSON.parse( '{"a":{"b":0}}', object_class:OpenStruct )
#<OpenStruct a=#<OpenStruct b=0>>
Is there a simpler method of converting it back to json, without creating a recursive function (as demonstrated here: Deep Convert OpenStruct to JSON)?

Calling OpenStruct#to_json on your struct should do it:
[2] pry(main)> JSON.parse('{"a":{"b":0}}', object_class:OpenStruct).to_json
=> "{\"a\":{\"b\":0}}"
And from plain irb OpenStruct#to_json does not work:
irb(main):003:0> require 'ostruct'
=> true
irb(main):004:0> require 'json'
=> true
irb(main):005:0> JSON.parse('{"a":{"b":0}}', object_class:OpenStruct).to_json
=> "\"#<OpenStruct a=#<OpenStruct b=0>>\""
ruby 2.5.3, rails 4.2.11.1

Related

Rails Digest::UUID v5 (vs) Postgresql uuid-ossp v5

I'm getting different V5 UUIDs when generating with Rails Digest::UUID and Postgresql uuid-ossp.
Rails:
[58] pry(main)> Digest::UUID.uuid_v5('e90bf6ab-f698-4faa-9d0f-810917dea53a', 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
=> "db68e7ad-332a-57a7-9638-a507f76ded93"
Postgresql uuid-ossp:
select uuid_generate_v5('e90bf6ab-f698-4faa-9d0f-810917dea53a', 'e90bf6ab-f698-4faa-9d0f-810917dea53a');
uuid_generate_v5
--------------------------------------
6c569b95-a6fe-5553-a6f5-cd871ab30178
What would be the reason? I thought both should generate the same UUID when the input is the same, but it is different!
It's not an answer to the question about why Rails produces a different result, but if you want to produce v5 UUID in your Ruby code, you could use uuidtools. It returns the same result as PSQL:
~ pry
[1] pry(main)> require 'uuidtools'
=> true
[2] pry(main)> UUIDTools::UUID.sha1_create(UUIDTools::UUID.parse('e90bf6ab-f698-4faa-9d0f-810917dea53a'), 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
=> #<UUID:0x3fe09ea60dd8 UUID:6c569b95-a6fe-5553-a6f5-cd871ab30178>
[3] pry(main)>
It seems that a patch is proposed so that working string-representation of namespaces can be enabled explicitly
The new behavior will be enabled by setting the config.active_support.use_rfc4122_namespaced_uuids option to
true.
but, the patch is very recent and it could be still under test. People can be afraid it breaks things. Check
https://github.com/rails/rails/issues/37681
https://github.com/rails/rails/pull/37682/files
Meanwhile, a workaround is to pack the namespace string
ns=n.scan(/(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})/).flatten.map { |s| s.to_i(16) }.pack("NnnnnN")
In your example
irb(main):037:0> n='e90bf6ab-f698-4faa-9d0f-810917dea53a'
=> "e90bf6ab-f698-4faa-9d0f-810917dea53a"
irb(main):038:0> ns=n.scan(/(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})/).flatten.map { |s| s.to_i(16) }.pack("NnnnnN")
=> "\xE9\v\xF6\xAB\xF6\x98O\xAA\x9D\x0F\x81\t\x17\xDE\xA5:"
irb(main):039:0> puts Digest::UUID.uuid_v5(ns, 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
6c569b95-a6fe-5553-a6f5-cd871ab30178

Difference between Ruby’s Hash and ActiveSupport’s HashWithIndifferentAccess

What is the difference between Ruby’s Hash and ActiveSupport’s HashWithIndifferentAccess? Which is the best for dynamic hashes?
Below is the simple example that will show you difference between simple ruby hash & a "ActiveSupport::HashWithIndifferentAccess"
HashWithIndifferentAccess allows us to access hash key as a symbol or string
Simple Ruby Hash
$ irb
2.2.1 :001 > hash = {a: 1, b:2}
=> {:a=>1, :b=>2}
2.2.1 :002 > hash[:a]
=> 1
2.2.1 :003 > hash["a"]
=> nil
ActiveSupport::HashWithIndifferentAccess
2.2.1 :006 > hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1, b:2)
NameError: uninitialized constant ActiveSupport
from (irb):6
from /home/synerzip/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'
2.2.1 :007 > require 'active_support/core_ext/hash/indifferent_access'
=> true
2.2.1 :008 > hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1, b:2)
=> {"a"=>1, "b"=>2}
2.2.1 :009 > hash[:a]
=> 1
2.2.1 :010 > hash["a"]
=> 1
class HashWithIndifferentAccess is inherited from ruby "Hash" & above special behavior is added in it.
In Ruby Hash:
hash[:key]
hash["key"]
are different. In HashWithIndifferentAccess as the name suggests, you can access key either way.
Quoting official documentation to this:
Implements a hash where keys :foo and "foo" are considered to be the
same.
and
Internally symbols are mapped to strings when used as keys in the
entire writing interface (calling []=, merge, etc). This mapping
belongs to the public interface. For example, given:
hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
You are
guaranteed that the key is returned as a string:
hash.keys # => ["a"]

How to integrate a simple bitcoin API into a Rails app

I am new to API's and backend development in general and have been trying for a few hours now to figure out how to do something simple like call the current bitcoin market price into my Rails app.
I tried referencing http://blockchain.info/ticker with the following code in my model
require 'rest-client'
require 'json'
base_url = "http://blockchain.info/ticker"
response = RestClient.get base_url
data = JSON.load response
cool = data[0]["CNY"]
#test = JSON.pretty_generate cool
and then put this in my view
<%= #test %>
I know this is way off but I'm at a loss and figured I would see if someone could provide a good resource or maybe get me going in the right direction. Many thanks
Dude, its all working good.
Replace data[0]["CNY"] with data["CNY"], thats all.
To get more handle, execute these lines 1 by 1 in irb,
Just like this,
1.9.3p385 :001 > require 'rest-client'
=> true
1.9.3p385 :002 > require 'json'
=> true
1.9.3p385 :004 > base_url = "http://blockchain.info/ticker"
=> "http://blockchain.info/ticker"
1.9.3p385 :005 > response = RestClient.get base_url
1.9.3p385 :006 > data = JSON.load response
1.9.3p385 :007 > cool = data["CNY"]
=> {"15m"=>5519.13613, "last"=>5519.13613, "buy"=>5578.16433, "sell"=>5853.54832, "24h"=>5616.47, "symbol"=>"¥"}
1.9.3p385 :008 > #test = JSON.pretty_generate cool
=> "{\n \"15m\": 5519.13613,\n \"last\": 5519.13613,\n \"buy\": 5578.16433,\n \"sell\": 5853.54832,\n \"24h\": 5616.47,\n \"symbol\": \"¥\"\n}"
1.9.3p385 :009 > p #test
"{\n \"15m\": 5519.13613,\n \"last\": 5519.13613,\n \"buy\": 5578.16433,\n \"sell\": 5853.54832,\n \"24h\": 5616.47,\n \"symbol\": \"¥\"\n}"
=> "{\n \"15m\": 5519.13613,\n \"last\": 5519.13613,\n \"buy\": 5578.16433,\n \"sell\": 5853.54832,\n \"24h\": 5616.47,\n \"symbol\": \"¥\"\n}"
I would recommend you use httparty which makes sending requests much simpler.
With regards to your example, you could do
require 'httparty'
require 'json'
base_url = "http://blockchain.info/ticker"
response = HTTParty.get(base_url)
data = JSON.parse(response.body)
data.each_pair do |ticker, stats|
pp "Ticker: #{ticker} - 15m: #{stats['15m']}"
end
Obviously I am pp (printing) out a string just to show the data. You would actually render the data in the view if you were to do a real implementation.

Ruby pack - TypeError: can't convert String into Integer

I am getting TypeError: can't convert String into Integer, I found duplicate answer too, but I am facing this error for 'pack'.
Other confusion is, it is working fine with ruby 1.8.7, not with ruby 1.9.3, here is the code, I am using jruby1.7.2
irb(main):003:0> length=nil
=> nil
irb(main):004:0> token_string ||= ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!$:*)
=> "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!$:*"
irb(main):005:0> token = (0..(length ? length : 60)).collect { token_string[rand( token_string.size)]}.pack("c*")
TypeError: can't convert String into Integer
from (irb):5:in `pack'
from (irb):5
from /home/appandya/.rvm/rubies/ruby-1.9.3-p374/bin/irb:13:in `<main>'
irb(main):006:0>
Any Idea?
string[x] in Ruby 1.8 gives you a Fixnum (the character code) and in 1.9 it gives you a single character string.
Array#pack turns an array into a binary sequence. The "c*" template to pack converts an array of Ruby integers into a stream of 8-bit signed words.
Here are the solutions, those comes from the Google Groups
1.
Background
>> %w(a b c).pack('*c')
TypeError: can't convert String into Integer
from (irb):1:in `pack'
from (irb):1
from /usr/bin/irb:12:in `<main>'
>> [1, 2, 3].pack('*c')
=> "\x01"
>> %w(a b c).map(&:ord).pack('*c')
=> "a"
Solution
irb(main):001:0> length=nil
=> nil
irb(main):002:0> token_string ||= ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!$:*)
=> "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!$:*"
irb(main):003:0> (0..(length ? length : 60)).collect { token_string[rand( token_string.size)]}.map(&:ord).pack("c*")
=> "rd!:!LcxU3ON57*t2s520v*zvvdflSNAgU6uq14SiD00VUDlm9:4:tJz5Ri5o"
irb(main):004:0>
2.
The return type of String's [] function was Fixnum in 1.8 but is String in 1.9:
>JRUBY_OPTS=--1.9 ruby -e "puts 'a'[0].class"
String
>JRUBY_OPTS=--1.8 ruby -e "puts 'a'[0].class"
Fixnum
JRuby 1.7.x defaults to acting like Ruby 1.9.3. You need to set JRUBY_OPTS.
3.
try join instead of pack
irb(main):004:0> length=nil
=> nil
irb(main):005:0> token_string ||= ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!$:*)
=> "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!$:*"
irb(main):006:0> token = (0..(length ? length : 60)).collect { token_string[rand( token_string.size)]}.join("c*")
=> "Fc*Dc*1c*6c*ac*Kc*Tc*Qc*Hc*jc*Ec*Kc*kc*zc*sc*3c*ic*hc*kc*wc**c*Wc*$c*Kc*Ic*Uc*Cc*bc*Pc*1c*!c*mc*Bc*lc*dc*ic*Dc*sc*Ac*Bc*nc*Kc*mc*Lc*oc*Zc*Xc*jc*6c*2c*Uc*ec*Yc*Dc*vc*Ic*Uc*5c*Zc*3c*o"
irb(main):007:0>
4.
if you're just trying to make a string of random characters that are 8-bit clean you may want to look at Random#bytes and something like Base64.encode64.
5.
active_support/secure_random also has a nice API for these things
I would recommend you take a look at: Array Method Pack, essentially the .pack("c") expects the elements of the array to be integers. You could try .pack("a*")
Use token = (0..(length ? length : 60)).collect { token_string[rand( token_string.size)]}.pack("a"*61)
You get the same result. I am using 61 because, going from 0..60 has 61 elements.

What is the Ruby/Rails equivalent of Python's urllib.quote_plus?

Basically url escapes a string except uses '+' instead of '%20' for spaces.
CGI::escape does just that:
irb(main):003:0> require 'cgi'
=> true
irb(main):004:0> CGI::escape("foo and/or bar")
=> "foo+and%2For+bar"

Resources