JSON no implicit conversion of String into Integer in Ruby - ruby-on-rails

I'm grabbing a JSON hash from my server and trying to read it in my ruby script. It seems to be returning the wrong value though, unless I'm just loosing my mind:
I'm calling this from a function:
def get_functions(serial_number)
response = HTTParty.get("#{get_host_url}/v1/devices/#{serial_number}.json")
data = response['scan_option']
return data
end
Here is the returned JSON:
{"can_scan"=>true, "can_brute_ssh"=>false, "can_brute_telnet"=>false, "can_brute_voip"=>false, "can_brute_smtp"=>false, "can_brute_pop3"=>false, "can_google_crawl"=>false, "can_scan_external_ip"=>false, "scan_ip_list"=>["10.10.10.1"], "exclude_ip_list"=>[]}
Which is then read into the following code:
data.each do |d|
#can_scan = d['can_scan']
# ....
end
However this is throwing an error:
no implicit conversion of String into Integer

{foo: :bar}.each do |d|
p d
d['meow']
end
# => [:foo, :bar]
#! TypeError: no implicit conversion of String into Integer
Hash#each yields a two element array ([key, value]). You then try to index that array with d["can_scan"], which fails as arrays can only be indexed with integers.
Instead, directly access the value - data['can_scan'].

I you mean that data in your third snippet (where you call data.each) is the hash mentioned just above it, indeed that would be troublesome. Calling each on a hash will itterate over its key, value pairs, giving you an array in the block var d of the data.each (with a [key, value] pair in it).
You might just want to call data['can_scan'].
Note that the return at the end of your method defenition is not needed in Ruby. You can just do:
def get_functions(serial_number)
response = HTTParty.get("#{get_host_url}/v1/devices/#{serial_number}.json")
response["scan_option"]
end

Related

Writing custom method for a hash on Ruby? on rails

i'm trying (and actually succeded, but i don't understand how it works) to write a custom method for a hash in my model (I'm working on Ruby on Rails 6).
My hash looks like this
my_hash = {
[['name_1', 'slug_1']=>value_1],
[['name_2', 'slug_2']=>value_2],
[['name_1', 'slug_1']=>value_3],
[['name_2', 'slug_2']=>value_4]
}
So basically a hash of arrays. You notice that the 'keys' are arrays that repeat themselves many times, but with different values. What i want to achieve is to write a custom method that "joins" all the keys in only one key, which will have an array of values assigned, so basically i should be able to get:
my_hash = {
['name_1', 'slug_1']=>"values": [value_1, value_3],
['name_2', 'slug_2']=>"values": [value_2, value_4]
}
For that, I have this piece of code, which i use many times:
my_hash.inject({}) do |hash, record|
# each record has the following format => [["unit_name", "axis.slug"]=>average_value(float)]
keys, value = record
# now keys has ["unit_name", "axis.slug"] and values equals average_value
hash[keys.first] ||= {}
hash[keys.first][keys.last] = value.to_f
hash
end
Since I use this many times, i wanted to write a custom method, so i did:
def format_hash_data my_hash
my_hash.inject({}) do |hash, record|
# each record has the following format => [["unit_name", "axis.slug"]=>average_value(float)]
keys, value = record
# now keys has ["unit_name", "axis.slug"] and values equals average_value
hash[keys.first] ||= {}
hash[keys.first][keys.last] = value.to_f
hash
end
end
And used it like: my_hash = format_hash_data(my_hash) with no success(it threw an error saying that 'format_hash_data' was not a valid method for the class).
So I fiddled around and added 'self' to the name of the method, leaving:
def self.format_hash_data my_hash
my_hash.inject({}) do |hash, record|
# each record has the following format => [["unit_name", "axis.slug"]=>average_value(float)]
keys, value = record
# now keys has ["unit_name", "axis.slug"] and values equals average_value
hash[keys.first] ||= {}
hash[keys.first][keys.last] = value.to_f
hash
end
end
Which, to my surprise, worked flawlessly when using my_hash = format_hash_data(my_hash)
I don't really understand why adding 'self' makes my code works, maybe anyone can shed some light? I tried using things like send() or instance_eval first, to just send the piece of code to the actual hash as a method (something like my_hash.instance_eval(my_method)) but I couldn't get it working.
I'm sorry about the long explanation, I hope i was clear enough so any of you who had this same dilemma can understand. Thanks in advance.
Prepending self. to the method name makes it a class method instead of an instance method. If you are not sure of the difference, you should look it up as it is fundamental to properly defining and using classes and methods.
As a class method, you would use it as:
my_hash = MyHash.format_hash_data(my_hash)
Or if you're in scope of the class, simply my_hash = format_hash_data(my_hash), which is why it worked in your case with the self. prepended (class method definition).
If you want to define it as an instance method (a method that is defined for the instance), you would use it like so:
my_hash = my_hash.format_hash_data
And the definition would use the implicit self of the instance:
def format_hash_data
self.inject({}) do |hash, record|
# each record has the following format => [["unit_name", "axis.slug"]=>average_value(float)]
keys, value = record
# now keys has ["unit_name", "axis.slug"] and values equals average_value
hash[keys.first] ||= {}
hash[keys.first][keys.last] = value.to_f
hash
end
end

Ruby to_s isn't converting integer to string

I'm trying to convert some values in a hash into a string but the type stays the same.
recommended_stores = []
results['data'].each do |stores_list|
stores_list['stores'].each do |store|
store['id'].to_s
end
recommended_stores << stores_list['stores']
end
Am I missing something here?
the method #to_s just returns the element converted to a string, but does not actually convert the element to a string permanently. instead of using #each, you could use #map, like this.
results['data'].map do |stores_list|
stores_list['stores'].each do |store|
store['id'] = store['id'].to_s
end
end
That would return an array of arrays, if you want it to be just one array you can use #flat_map.
you got everything but you are not storing it, i think assigning the value of hash with the value.to_s would work, can you try as below
recommended_store = []
results['data'].each do |stores_list|
stores_list['stores'].each do |store|
store['id'] = store['id'].to_s
end
recommended_store << stores_list['stores']
end
Note : in your question array declared is "recommended_store" and last line you are pushing elements in to "recommended_stores" hope its just a typo, and not the cause of problem :-)

Converting array with .join not saving value

I have a method do_stuff that takes a string as a value. However, an array of two strings is occasionally passed in. In this situation, I need to convert the array to a single string (without commas). So for example, ["hello", "world"] should become "hello world".
So, if value = array, join the two strings, otherwise leave it alone.
The following line I have does what I want, but I am struggling with actually "saving" the value before passing it to the method do_other_stuff.
def do_stuff(value)
value.join("") if value.is_a? Array
do_other_stuff(value)
end
So I think i am close, but what would be the best way to ensure value is manipulated before passing it to do_other_stuff ?
join does not change your object, you're wasting its return value
value = value.join if value.is_a? Array
Note that "" is the default for the join parameter, so I got rid of it
Replace
value.join("") if value.is_a? Array
With
value = value.join("") if value.is_a? Array
Basically you need to reassign result back to value
Use duck typing instead of checking the class:
def do_stuff(value)
do_other_stuff(value.try(:join, '') || value)
end
.try is from ActiveSupport and will return nil if the object does not respond to the method. In plain old ruby you would write this as:
def do_stuff(value)
do_other_stuff(value.respond_to?(:join) ? value.join("") : value)
end

Dropping array into HTTParty API call argument throws `args.ids.split is not a function` error. How do I fix this?

I have a function called value that returns this array below:
[1893, 1724, 257344, 353491, 301337, 305470, 348, 264660, 76341, 68726]
However for more clarity, here is the value function that returns the value array above: It's a module like ListInfoService below and has 4 arguments which are the arguments supplied in the values method below ValueSearchService::ValueSearch.new(params[:one], params['two'] = 1_000_000, params[:three], params[:four]).values
def value
params = set_params_if_not_added
value_search = ValueSearchService::ValueSearch.new(
params[:one],
params['two'] = 1_000_000,
params[:three],
params[:four]
).values
value_search['data']
end
I also have a function where the argument ids is supposed to take the result of value (i.e. comma separated numbers):
module ListInfoService
class ListInfo
include HTTParty
base_uri 'localhost:7000'
def initialize(ids, limit, offset)
#options = { query: { ids: ids, some_params: some_params, another_params: another_params } }
end
def movies
self.class.get('/list', #options)
end
end
end
The URL I interpreted to make the module function above is:
localhost:7000/list?ids=2,3,6&some_params=5&another_params=0
But when I call the function by dropping value result into the function call below, I get args.ids.split is not a function error:
list_info = ListInfoService::ListInfo.new(
value,
params[:some_params],
params[:another_params]
).movies
render json: list_info
So how do I make sure my ids argument takes a comma-separated list of number?
Note: Am using HTTParty, Rails5.1, Ruby 2.6
I think the problem is the array value that I supplied inside ListInfoService::ListInfo that searches an api. If I can probably change how I call my api in the module or find a way to supply comma-separated list of ids needed, it will work.
Fixed. This is what I have done to make this work. Bear in mind this error is thrown as a result of passing an array into the HTTParty API call argument. Hence the args.ids.split is not a function Error.
Fixing this for me is just by converting the value to string instead of array by just adding .join(', ') to the response in the method. See below:
def value
params = set_params_if_not_added
value_search = ValueSearchService::ValueSearch.new(
params[:one],
params['two'] = 1_000_000,
params[:three],
params[:four]
).values
value_search['data'].join(', ') # This will solve it.
end
Thereby when you append value result to the defined HTTParty module call to the ids argument, it takes it as a string and it works fine.
list_info = ListInfoService::ListInfo.new(
value, # Note that this line is `ids` under ListInfoService::ListInfo argument and cant be array.
params[:some_params],
params[:another_params]).movies
render json: list_info

Parse json key value pair in Rails to format the date

I am trying to format date by iterating through the #data json formatted input shown as below.
#data= JSON.parse(request,symbolize_names: true)[:data]
#data = #data.each{ |key,val| k = Date.parse(key).strftime("%Y %m") }
But this way it is showing error "no implicit conversion of Symbol into String".
Could any one please help?
If you're iterating over a hash where the keys are symbols, then the error is telling you where and what's the problem. In order to parse a date, you must pass a string as argument, and as you haven't converted such object, then you're getting the error.
Try using to_s, to convert the symbol to string:
#data.each do |key, _|
puts Date.parse(key.to_s).strftime '%Y %m'
end
Note if you're inside a block, and you're not going to use the k variable you're creating, then you can avoid creating it, it won't be available outside the block. You're just printing the parsed date.
If you're not going to use the value block variable, then you can omit it.
As pointed #mu, you can omit the symbolize_names: true, and this way the keys will be strings, then, the conversion isn't needed:
require 'date'
require 'json'
request = '{
"data": {
"2017-11-22 00:22:26": "foo" ,
"2017-11-22 00:22:27": "bar"
}
}'
#data = JSON.parse(request)['data']
#data.each do |key, _|
puts Date.parse(key).strftime '%Y %m'
end
request is an approximation to your real data.

Resources