Ruby set an argument variable in an object - ruby

I need a quick hack for a ruby script which parse a json file for some variables. It stores in #{name} the name string from the json file where the id is the input for the argument.
The issue is that I cannot set the ID argument which is #{b} in the object data-on_day_you_want. If I put it in "#{b}" it won't match with the value from the json file.If I hardcoded it with 281 it works perfectly.
ps:Please find below the a small part from the json.
#!/usr/bin/env ruby
require 'rubygems'
require 'json'
a, b = ARGV #(environment and ID arguments)
file = File.read('file.json')
data_hash = JSON.parse(file)
data_on_day_you_want = data_hash["tenantAuthzProfiles"].detect{ |h| h["id"] == #{b} } # HERE IS THE ISSUE
name = data_on_day_you_want["name"]
puts "NAME is: #{name}"
the json:
{
"name": "Production Environment",
"roles": ["Host Admin"],
"id": 281
}
Do you have any suggestions?

I believe you want to parse b as integer:
data_on_day_you_want = data_hash["tenantAuthzProfiles"].detect do |h|
h["id"] == b.to_i
end
In your code, b receives the value "281" which is a string. When you compare string to integer, it'll always be false:
"281" == 281
# => false
The to_i method parses the string, and returns the integer value it represents:
"281".to_i == 281
# => true
(note that, contrary to other languages' parse methods, to_i is very lenient, so you might get surprising results "surprise".to_i == 0)
The notation of #{} is relevant to String interpolation, and is only valid inside a string (surrounded by double-quoutes)

Related

How to do user-inputted string templating in Ruby?

I know writing like
a=23
p "the value of a is #{a}"
it will print: the value of a is 23.
but now I am actually receiving this string as a parameter like
def evaluate string
a=23
puts string
end
calling method pass that string as a parameter
evaluate "the value of a is #{a}"
Is there any way to evaluate this string inside the method? puts string has to interpolate the value a=23.
Edit:
I have to read and execute the program from Excel.
At the first line,
Excel entry is,
"id=something" setvalue a
So now corresponding program will read the value from locator id=something and set it into the instance variable #a.
and user's next excel entry would be
"the value of a is 23" compare "the value of a is #{a}"
Now the program will read "the value of a is 23" and this "the value of a is #{a}" for comparison, but before it compares, it has to replace the value a. That's all I want. I hope now my question is very clear.
For ruby you can change how you "format" your strings in Excel, than you can use "classic" formatting
a = 23
s = 'the value of a is %s'
def evaluate(text, value)
puts text % value
end
You can use different formatting keys, for example %d for integers, %f for float numbers
You can use named arguments
dynamic_text = 'the value of the %<product_name>s is %<product_price>0.2f'
def evaluate(text, args)
puts text % args
end
name = "Product"
price = 78.99
evaluate dynamic_text, product_name: name, product_price: price
Without names, use order of the given values
dynamic_text = 'the value of the %s is %0.2f'
def evaluate(text, args)
puts text % args
end
name = "Product"
price = 78.99
evaluate dynamic_text, [name, price]
You can make a block and then evaluate the string:
def evaluate &block
a=23
block.call(a)
end
evaluate { |a| "the value of a is #{a}" } #=> "the value of a is 23"
It's a very odd thing you're attempting to do. When you have some sort of a pattern with placeholders, you do it like:
def evaluate(string)
a=23
format string, a: a
end
evaluate "the value of a is %{a}"
String interpolation with #{..} is not meant for the case you're describing as the value is evaluated at the time of constructing the string, not later. You could do some regexp matching and replace the #{..} with %{..} as a workaround.
There's a few ways:
"Code" Dynamic
lazy evaluation with lambdas:
def evaluate(str_template)
a = 23
str_template.call(a)
end
user_input = gets
my_lambda = lambda do |str|
user_input.size > 10 ? "dynamic 1 #{str}" : "dynamic 2 #{str}"
end
evaluate(my_lambda)
# => "dynamic 1/2 23"
This is "code dynamic", but not "input dynamic", i.e. you can't receive the string template from the user.
"Input" Dynamic 1
ERB templating:
require 'erb'
user_input_erb = gets
puts user_input_erb # "Hello <%= name %>"
name = gets # also user input, e.g. "World"
ERB.new(user_input_erb).result
# => "Hello World"
Note that in general, getting string templates from the user and evaluating them is a potential security vulnerability. If there's any possibility user input can be adversarial, you'll want to see if you can find a "guaranteed to be safe against all user input" string templating library.
"Input" Dynamic 2
user_input_template = gets
puts user_input_template # "Hello %s"
name = gets # also user input, e.g. "World"
user_input_template % name
# => "Hello World"
"Input" Dynamic 3
Really dangerous, but:
user_input_ruby_code = gets
puts user_input_ruby_code # '"Hello #{name}"'
name = gets # also user input, e.g. "World"
eval user_input_ruby_code # DANGER
# => "Hello World"

how to pass variable from a class to another class in ruby

I'm trying to extract data from mongodb to Elasticsearch, getMongodoc = coll.find().limit(10)
will find the first 10 entries in mongo.
As you can see , result = ec.mongoConn should get result from method mongoConn() in class MongoConnector. when I use p hsh(to examine the output is correct), it will print 10 entires, while p result = ec.mongoConn will print #<Enumerator: #<Mongo::Cursor:0x70284070232580 #view=#<Mongo::Collection::View:0x70284066032180 namespace='mydatabase.mycollection' #filter={} #options={"limit"=>10}>>:each>
I changed p hsh to return hsh, p result = ec.mongoConn will get the correct result, but it just prints the first entry not all 10 entries. it seems that the value of hsh did not pass to result = ec.mongoConn correctly, Can anyone tell me what am I doing wrong? is this because I did something wrong with method calling?
class MongoConncetor
def mongoConn()
BSON::OrderedHash.new
client = Mongo::Client.new([ 'xx.xx.xx.xx:27017' ], :database => 'mydatabase')
coll = client[:mycollection]
getMongodoc = coll.find().limit(10)
getMongodoc.each do |document|
hsh = symbolize_keys(document.to_hash).select { |hsh| hsh != :_id }
return hsh
# p hsh
end
end
class ElasticConnector < MongoConncetor
include Elasticsearch::API
CONNECTION = ::Faraday::Connection.new url: 'http://localhost:9200'
def perform_request(method, path, params, body)
puts "--> #{method.upcase} #{path} #{params} #{body}"
CONNECTION.run_request \
method.downcase.to_sym,
path,
((
body ? MultiJson.dump(body) : nil)),
{'Content-Type' => 'application/json'}
end
ec = ElasticConnector.new
p result = ec.mongoConn
client = ElasticConnector.new
client.bulk index: 'myindex',
type:'test' ,
body: result
end
You are calling return inside a loop (each). This will stop the loop and return the first result. Try something like:
getMongodoc.map do |document|
symbolize_keys(document.to_hash).select { |hsh| hsh != :_id }
end
Notes:
In ruby you usually don't need the return keyword as the last value is returned automatically. Usually you'd use return to prevent some code from being executed
in ruby snake_case is used for variable and method names (as opposed to CamelCase or camelCase)
map enumerates a collection (by calling the block for every item in the collection) and returns a new collection of the same size with the return values from the block.
you don't need empty parens () on method definitions
UPDATE:
The data structure returned by MongoDB is a Hash (BSON is a special kind of serialization). A Hash is a collection of keys ("_id", "response") that point to values. The difference you point out in your comment is the class of the hash key: string vs. symbol
In your case a document in Mongo is represented as Hash, one hash per document
If you want to return multiple documents, then an array is required. More specifically an array of hashes: [{}, {}, ...]
If your target (ES) does only accept one hash at a time, then you will need to loop over the results from mongo and add them one by one:
list_of_results = get_mongo_data
list_of_results.each do |result|
add_result_to_es(result)
end

Ruby interpreted variables is_a?

I am looking to check a variabe for its type based on the value held in another variable but am struggling with it. I'm completely new to ruby but can anyone tell me how to have the value of a variable interpreted in the expression? My current code looks like:-
if variable.is_a?("#{variable_type}")
puts variable
end
Where variable could contain anything and variable_type contains the type of a variable like String or Fixnum. But currently this code gives me TypeError: Class or module required. Any thoughts?
Your code sends a String object to the #is_a? method and the #is_a method expects a Class.
For example, String vs. "String":
variable = "Hello!"
variable_type = String
"#{variable_type}" # => "String"
# your code:
if variable.is_a?("#{variable_type}")
puts variable
end
#is_a? expects the actual Class (String, Fixnum, etc') - as you can see in the documentation for #is_a?.
You can adjust your code in two ways:
pass the Class, without the string.
convert the string to the class using Module.const_get.
here is an example:
variable = "Hello!"
variable_type = String
"#{variable_type}" # => "String"
# passing the actual class:
if variable.is_a?(variable_type)
puts variable
end
# or,
# converting the string to a the type:
if variable.is_a?( Module.const_get( variable_type.to_s ) )
puts variable
end
Just a little example:
variable = 1
variable_type = String
puts variable if variable.is_a?(variable_type)
#=> nil
variable_type = Integer
puts variable if variable.is_a?(variable_type)
#=> 1
Or when your variable_type is a string:
variable_type = 'Integer'
puts variable if variable.is_a?(Object.const_get(variable_type))
#=> 1
TypeError: Class or module required
It means, that to use is_a? varibale_type should hold a class name (any).
Therefore if you hold anything else except for class name in variable_type it will give you this error.
a = :a
variable_type = Symbol
a if a.is_a? variable_type
# => :a
If variable type is a string, you will have to use Module#const_get:
variable_type = 'Symbol'
a if a.is_a? Object.const_get(variable_type)
# => :a

Ruby joining error

I am trying to unpack some variables then have them joined when passed to the def this is what i'm trying, but it errors.
class Try
def test
#name = "bob"
#password = "password"
self.send(#name,#password)
end
def send(*data)
print data #prints orginal data
print ":".join(data) #errors
end
end
Is there something I'm doing wrong?
Here you should do as below using Array#join:
class Try
def test
#name = "bob"
#password = "password"
self.send(#name,#password)
end
def send(*data)
print data.join(":")
end
end
Try.new.test
# >> bob:password
The join is for Array instances. It is not a String instance method. See below:
Array.instance_methods.include?(:join) # => true
String.instance_methods.include?(:join) # => false
I think perhaps you are confusing the built-in function join of a Python string with the join method of the Ruby Array class.
From help(":".join) in Python:
Return a string which is the concatenation of the strings in the
iterable. The separator between elements is S.
And from the docs on Ruby's Array:
Returns a string created by converting each element of the array to a string,
separated by the given separator.
So you see, the Python join built-in function concatenates the strings of the given list argument, while in Ruby the Array#join method will convert the elements into their String equivalents, and then concatenate them using the separator argument.
Hope this clears up the confusion between Python's join and Ruby's Array#join!

How do I convert a String object into a Hash object?

I have a string which looks like a hash:
"{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }"
How do I get a Hash out of it? like:
{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }
The string can have any depth of nesting. It has all the properties how a valid Hash is typed in Ruby.
For different string, you can do it without using dangerous eval method:
hash_as_string = "{\"0\"=>{\"answer\"=>\"1\", \"value\"=>\"No\"}, \"1\"=>{\"answer\"=>\"2\", \"value\"=>\"Yes\"}, \"2\"=>{\"answer\"=>\"3\", \"value\"=>\"No\"}, \"3\"=>{\"answer\"=>\"4\", \"value\"=>\"1\"}, \"4\"=>{\"value\"=>\"2\"}, \"5\"=>{\"value\"=>\"3\"}, \"6\"=>{\"value\"=>\"4\"}}"
JSON.parse hash_as_string.gsub('=>', ':')
Quick and dirty method would be
eval("{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }")
But it has severe security implications.
It executes whatever it is passed, you must be 110% sure (as in, at least no user input anywhere along the way) it would contain only properly formed hashes or unexpected bugs/horrible creatures from outer space might start popping up.
The string created by calling Hash#inspect can be turned back into a hash by calling eval on it. However, this requires the same to be true of all of the objects in the hash.
If I start with the hash {:a => Object.new}, then its string representation is "{:a=>#<Object:0x7f66b65cf4d0>}", and I can't use eval to turn it back into a hash because #<Object:0x7f66b65cf4d0> isn't valid Ruby syntax.
However, if all that's in the hash is strings, symbols, numbers, and arrays, it should work, because those have string representations that are valid Ruby syntax.
I had the same problem. I was storing a hash in Redis. When retrieving that hash, it was a string. I didn't want to call eval(str) because of security concerns. My solution was to save the hash as a json string instead of a ruby hash string. If you have the option, using json is easier.
redis.set(key, ruby_hash.to_json)
JSON.parse(redis.get(key))
TL;DR: use to_json and JSON.parse
Maybe YAML.load ?
The solutions so far cover some cases but miss some (see below). Here's my attempt at a more thorough (safe) conversion. I know of one corner case which this solution doesn't handle which is single character symbols made up of odd, but allowed characters. For example {:> => :<} is a valid ruby hash.
I put this code up on github as well. This code starts with a test string to exercise all the conversions
require 'json'
# Example ruby hash string which exercises all of the permutations of position and type
# See http://json.org/
ruby_hash_text='{"alpha"=>{"first second > third"=>"first second > third", "after comma > foo"=>:symbolvalue, "another after comma > foo"=>10}, "bravo"=>{:symbol=>:symbolvalue, :aftercomma=>10, :anotheraftercomma=>"first second > third"}, "charlie"=>{1=>10, 2=>"first second > third", 3=>:symbolvalue}, "delta"=>["first second > third", "after comma > foo"], "echo"=>[:symbol, :aftercomma], "foxtrot"=>[1, 2]}'
puts ruby_hash_text
# Transform object string symbols to quoted strings
ruby_hash_text.gsub!(/([{,]\s*):([^>\s]+)\s*=>/, '\1"\2"=>')
# Transform object string numbers to quoted strings
ruby_hash_text.gsub!(/([{,]\s*)([0-9]+\.?[0-9]*)\s*=>/, '\1"\2"=>')
# Transform object value symbols to quotes strings
ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>\s*:([^,}\s]+\s*)/, '\1\2=>"\3"')
# Transform array value symbols to quotes strings
ruby_hash_text.gsub!(/([\[,]\s*):([^,\]\s]+)/, '\1"\2"')
# Transform object string object value delimiter to colon delimiter
ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>/, '\1\2:')
puts ruby_hash_text
puts JSON.parse(ruby_hash_text)
Here are some notes on the other solutions here
#Ken Bloom and #Toms Mikoss's solutions use eval which is too scary for me (as Toms rightly points out).
#zolter's solution works if your hash has no symbols or numeric keys.
#jackquack's solution works if there are no quoted strings mixed in with the symbols.
#Eugene's solution works if your symbols don't use all the allowed characters (symbol literals have a broader set of allowed characters).
#Pablo's solution works as long as you don't have a mix of symbols and quoted strings.
This short little snippet will do it, but I can't see it working with a nested hash. I think it's pretty cute though
STRING.gsub(/[{}:]/,'').split(', ').map{|h| h1,h2 = h.split('=>'); {h1 => h2}}.reduce(:merge)
Steps
1. I eliminate the '{','}' and the ':'
2. I split upon the string wherever it finds a ','
3. I split each of the substrings that were created with the split, whenever it finds a '=>'. Then, I create a hash with the two sides of the hash I just split apart.
4. I am left with an array of hashes which I then merge together.
EXAMPLE INPUT: "{:user_id=>11, :blog_id=>2, :comment_id=>1}"
RESULT OUTPUT: {"user_id"=>"11", "blog_id"=>"2", "comment_id"=>"1"}
I prefer to abuse ActiveSupport::JSON. Their approach is to convert the hash to yaml and then load it. Unfortunately the conversion to yaml isn't simple and you'd probably want to borrow it from AS if you don't have AS in your project already.
We also have to convert any symbols into regular string-keys as symbols aren't appropriate in JSON.
However, its unable to handle hashes that have a date string in them (our date strings end up not being surrounded by strings, which is where the big issue comes in):
string = '{'last_request_at' : 2011-12-28 23:00:00 UTC }'
ActiveSupport::JSON.decode(string.gsub(/:([a-zA-z])/,'\\1').gsub('=>', ' : '))
Would result in an invalid JSON string error when it tries to parse the date value.
Would love any suggestions on how to handle this case
works in rails 4.1 and support symbols without quotes {:a => 'b'}
just add this to initializers folder:
class String
def to_hash_object
JSON.parse(self.gsub(/:([a-zA-z]+)/,'"\\1"').gsub('=>', ': ')).symbolize_keys
end
end
Please consider this solution. Library+spec:
File: lib/ext/hash/from_string.rb:
require "json"
module Ext
module Hash
module ClassMethods
# Build a new object from string representation.
#
# from_string('{"name"=>"Joe"}')
#
# #param s [String]
# #return [Hash]
def from_string(s)
s.gsub!(/(?<!\\)"=>nil/, '":null')
s.gsub!(/(?<!\\)"=>/, '":')
JSON.parse(s)
end
end
end
end
class Hash #:nodoc:
extend Ext::Hash::ClassMethods
end
File: spec/lib/ext/hash/from_string_spec.rb:
require "ext/hash/from_string"
describe "Hash.from_string" do
it "generally works" do
[
# Basic cases.
['{"x"=>"y"}', {"x" => "y"}],
['{"is"=>true}', {"is" => true}],
['{"is"=>false}', {"is" => false}],
['{"is"=>nil}', {"is" => nil}],
['{"a"=>{"b"=>"c","ar":[1,2]}}', {"a" => {"b" => "c", "ar" => [1, 2]}}],
['{"id"=>34030, "users"=>[14105]}', {"id" => 34030, "users" => [14105]}],
# Tricky cases.
['{"data"=>"{\"x\"=>\"y\"}"}', {"data" => "{\"x\"=>\"y\"}"}], # Value is a `Hash#inspect` string which must be preserved.
].each do |input, expected|
output = Hash.from_string(input)
expect([input, output]).to eq [input, expected]
end
end # it
end
Here is a method using whitequark/parser which is safer than both gsub and eval methods.
It makes the following assumptions about the data:
Hash keys are assumed to be a string, symbol, or integer.
Hash values are assumed to be a string, symbol, integer, boolean, nil, array, or a hash.
# frozen_string_literal: true
require 'parser/current'
class HashParser
# Type error is used to handle unexpected types when parsing stringified hashes.
class TypeError < ::StandardError
attr_reader :message, :type
def initialize(message, type)
#message = message
#type = type
end
end
def hash_from_s(str_hash)
ast = Parser::CurrentRuby.parse(str_hash)
unless ast.type == :hash
puts "expected data to be a hash but got #{ast.type}"
return
end
parse_hash(ast)
rescue Parser::SyntaxError => e
puts "error parsing hash: #{e.message}"
rescue TypeError => e
puts "unexpected type (#{e.type}) encountered while parsing: #{e.message}"
end
private
def parse_hash(hash)
out = {}
hash.children.each do |node|
unless node.type == :pair
raise TypeError.new("expected child of hash to be a `pair`", node.type)
end
key, value = node.children
key = parse_key(key)
value = parse_value(value)
out[key] = value
end
out
end
def parse_key(key)
case key.type
when :sym, :str, :int
key.children.first
else
raise TypeError.new("expected key to be either symbol, string, or integer", key.type)
end
end
def parse_value(value)
case value.type
when :sym, :str, :int
value.children.first
when :true
true
when :false
false
when :nil
nil
when :array
value.children.map { |c| parse_value(c) }
when :hash
parse_hash(value)
else
raise TypeError.new("value of a pair was an unexpected type", value.type)
end
end
end
and here are some rspec tests verifying that it works as expected:
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe HashParser do
describe '#hash_from_s' do
subject { described_class.new.hash_from_s(input) }
context 'when input contains forbidden types' do
where(:input) do
[
'def foo; "bar"; end',
'`cat somefile`',
'exec("cat /etc/passwd")',
'{:key=>Env.fetch("SOME_VAR")}',
'{:key=>{:another_key=>Env.fetch("SOME_VAR")}}',
'{"key"=>"value: #{send}"}'
]
end
with_them do
it 'returns nil' do
expect(subject).to be_nil
end
end
end
context 'when input cannot be parsed' do
let(:input) { "{" }
it 'returns nil' do
expect(subject).to be_nil
end
end
context 'with valid input' do
using RSpec::Parameterized::TableSyntax
where(:input, :expected) do
'{}' | {}
'{"bool"=>true}' | { 'bool' => true }
'{"bool"=>false}' | { 'bool' => false }
'{"nil"=>nil}' | { 'nil' => nil }
'{"array"=>[1, "foo", nil]}' | { 'array' => [1, "foo", nil] }
'{foo: :bar}' | { foo: :bar }
'{foo: {bar: "bin"}}' | { foo: { bar: "bin" } }
end
with_them do
specify { expect(subject).to eq(expected) }
end
end
end
end
I built a gem hash_parser that first checks if a hash is safe or not using ruby_parser gem. Only then, it applies the eval.
You can use it as
require 'hash_parser'
# this executes successfully
a = "{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' },
:key_b => { :key_1b => 'value_1b' } }"
p HashParser.new.safe_load(a)
# this throws a HashParser::BadHash exception
a = "{ :key_a => system('ls') }"
p HashParser.new.safe_load(a)
The tests in https://github.com/bibstha/ruby_hash_parser/blob/master/test/test_hash_parser.rb give you more examples of the things I've tested to make sure eval is safe.
This method works for one level deep hash
def convert_to_hash(str)
return unless str.is_a?(String)
hash_arg = str.gsub(/[^'"\w\d]/, ' ').squish.split.map { |x| x.gsub(/['"]/, '') }
Hash[*hash_arg]
end
example
> convert_to_hash("{ :key_a => 'value_a', :key_b => 'value_b', :key_c => '' }")
=> {"key_a"=>"value_a", "key_b"=>"value_b", "key_c"=>""}
I came to this question after writing a one-liner for this purpose, so I share my code in case it helps somebody. Works for a string with only one level depth and possible empty values (but not nil), like:
"{ :key_a => 'value_a', :key_b => 'value_b', :key_c => '' }"
The code is:
the_string = '...'
the_hash = Hash.new
the_string[1..-2].split(/, /).each {|entry| entryMap=entry.split(/=>/); value_str = entryMap[1]; the_hash[entryMap[0].strip[1..-1].to_sym] = value_str.nil? ? "" : value_str.strip[1..-2]}
Ran across a similar issue that needed to use the eval().
My situation, I was pulling some data from an API and writing it to a file locally. Then being able to pull the data from the file and use the Hash.
I used IO.read() to read the contents of the file into a variable. In this case IO.read() creates it as a String.
Then used eval() to convert the string into a Hash.
read_handler = IO.read("Path/To/File.json")
puts read_handler.kind_of?(String) # Returns TRUE
a = eval(read_handler)
puts a.kind_of?(Hash) # Returns TRUE
puts a["Enter Hash Here"] # Returns Key => Values
puts a["Enter Hash Here"].length # Returns number of key value pairs
puts a["Enter Hash Here"]["Enter Key Here"] # Returns associated value
Also just to mention that IO is an ancestor of File. So you can also use File.read instead if you wanted.
I had a similar issue when trying to convert a string to a hash in Ruby.
The result from my computations was this:
{
"coord":{"lon":24.7535,"lat":59.437},
"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],
"base":"stations",
"main":{"temp":283.34,"feels_like":281.8,"temp_min":282.33,"temp_max":283.34,"pressure":1021,"humidity":53},
"visibility":10000,
"wind":{"speed":3.09,"deg":310},
"clouds":{"all":75},
"dt":1652808506,
"sys":{"type":1,"id":1330,"country":"EE","sunrise":1652751796,"sunset":1652813502},
"timezone":10800,"id":588409,"name":"Tallinn","cod":200
}
I checked the type value and confirmed that it was of the String type using the command below:
result =
{
"coord":{"lon":24.7535,"lat":59.437},
"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],
"base":"stations",
"main":{"temp":283.34,"feels_like":281.8,"temp_min":282.33,"temp_max":283.34,"pressure":1021,"humidity":53},
"visibility":10000,
"wind":{"speed":3.09,"deg":310},
"clouds":{"all":75},
"dt":1652808506,
"sys":{"type":1,"id":1330,"country":"EE","sunrise":1652751796,"sunset":1652813502},
"timezone":10800,"id":588409,"name":"Tallinn","cod":200
}
puts result.instance_of? String
puts result.instance_of? Hash
Here's how I solved it:
All I had to do was run the command below to convert it from a String to a Hash:
result_new = JSON.parse(result, symbolize_names: true)
And then checked the type value again using the commands below:
puts result_new.instance_of? String
puts result_new.instance_of? Hash
This time it returned true for the Hash

Resources