How to check if a string contain valid hash - ruby-on-rails

i encounter a problem when i want to validate the input string if it contain a valid hash before execute eval on it, for example:
"{:key=>true}" if i run eval it return a correct hash, but if i run eval on this string "{:key=>true" i get syntax error of expected token:
(eval):1: syntax error, unexpected end-of-input, expecting '}' {:redirect=>true ^
i did tried some basic validation but no luck so far.
so basically what i want to know is how to validate a that a string contain correct hash format.

You can't tell without actually parsing it as Ruby, and (assuming you trust the string), the simplest way of parsing it is to simply do the eval call, and handle the exception:
begin
hash = eval(string)
rescue SyntaxError
# It's not valid
end
This is precisely what exceptions are for, instead of littering your code with checks for whether operations will succeed, you just perform the operations, and handle errors that occur.

To validate the string you can use Kernel#eval + checking the type:
def valid_hash?(string)
eval(string).is_a?(Hash)
rescue SyntaxError
false
end
Usage:
string = "{:key=>true}"
valid_hash?(string)
#=> true
string = "I am not hash"
valid_hash?(string)
#=> false

I had a similar problem, but I don't like the eval solution, because it's not safe.
I used the JSON gem and modified the string to match the JSON syntax.
Assuming you only have symbol keys.
'key: value' to '"key": value'
':key => value' to '"key": value'
string1 = "{:key_1=>true,key_2:false}"
string2 = "{:key=>true}"
string3 = "no hash"
def valid_hash?(string)
begin
string = string.gsub(/(\w+):\s*([^},])/, '"\1":\2')
#=> "{:key_1=>true,\"key_2\":false}"
string = string.gsub(/:(\w+)\s*=>/, '"\1":')
#=> "{\"key_1\":true,\"key_2\":false}"
my_hash = JSON.parse(string, {symbolize_names: true})
#=> {:key_1=>true, :key_2=>false}
my_hash.is_a? Hash # or do whatever you want with your Hash
rescue JSON::ParserError
false
end
end
valid_hash? string1
#=> true
valid_hash? string2
#=> true
valid_hash? string3
#=> false

Related

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.

How to convert a find_by_sql hstore string to a hash in Ruby on Rails

This seems ludicrously simple but I cannot figure out how to convert a hash-string to a hash.
When I do a Answer.find_by_sql I get a string like this
deepthought = "\"answertolife\"=>\"42\""
But I cannot figure out how to turn that into a hash.
I have tried:
pry(main)> Hash[deepthought]
ArgumentError: odd number of arguments for Hash
pry(main)> JSON.parse deepthought
JSON::ParserError: 757: unexpected token at '"answertolife"=>"42"'
pry(main)> deepthought.to_json
=> "\"\\\"answertolife\\\"=>\\\"42\\\"\""
I saw How do I convert a String object into a Hash object?, but I still cannot figure it out.
Try this
eval("{ #{deepthought} }")
It wraps the deepthought string with curly brace { }, and then use eval
A bit late but if you need to convert a multiple entries this works great.
def hstore_to_hash(hstore)
values = {}
hstore.gsub(/"/, '').split(",").each do |hstore_entry|
each_element = hstore_entry.split("=>")
values[each_element[0]] = each_element[1]
end
values
end
Rails4 supports hstore out of the box so I'd probably handle the string casting the same way Rails4 does it. If you look inside the Rails4 PostgreSQL-specific casting code, you'll find string_to_hstore:
def string_to_hstore(string)
if string.nil?
nil
elsif String === string
Hash[string.scan(HstorePair).map { |k, v|
v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
[k, v]
}]
else
string
end
end
and a little lower down in the same file, you'll find HstorePair:
HstorePair = begin
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
/(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
end
Stash that somewhere convenient (probably somewhere in lib/) and send your hstore strings through that string_to_hstore to unpack them into Hashes.
This seems to work but feels dirty.
JSON.parse "{ #{deepthought} }".gsub('=>', ':')

Check string is a valid number or not in ruby

I want to check weather variable contains a valid number or not.
I can validate correctly for null and blank but can not validate text as a "Integer"...
I tried:
if(params[:paramA].blank? || (params[:paramA].is_a?(Integer)) )
I have also tried is_numeric, is_numeric(string), is_number? and other ways...
but did not get success...
I saw such patch:
class String
def is_number?
true if Float(self) rescue false
end
end
if (params[:paramA].blank? || !params[:paramA].is_number?)
Or without the patch:
if (params[:paramA].blank? || (false if Float(params[:paramA]) rescue true))
It supports 12, -12, 12.12, 1e-3 and so on.
If your parameter is for an ActiveRecord model, then you should probably use validates_numericality_of. Otherwise...
You only want integers, right? How about:
if (params[:paramA].blank? || params[:paramA] !~ /^[+-]?\d+$/)
That is, check whether the parameter consists of an optional + or -, followed by 1 or more digits, and nothing else.
If the thing you want to do is this:
I want to check weather variable contains a valid number or not.
You can get it with regex. See it here
s = 'abc123'
if s =~ /[-.0-9]+/ # Calling String's =~ method.
puts "The String #{s} has a number in it."
else
puts "The String #{s} does not have a number in it."
end
In rails you can use the numeric? method on a String or Integer or Float which does exactly what you need.
123.numeric?
# => true
123.45.numeric?
# => true
"123".numeric?
# => true
"123.45".numeric?
# => true
"a1213".numeric?
# => false
UPDATE
My bad, I had a dirty environment, the above works if mongoid version 3 and above is loaded.

Gsub for non-numeric characters is removing numeric characters as well

Code:
puts "params[:phone] is " + params[:phone]
ph = params[:phone].gsub!(/\D/,'')
Rails.logger.info("ph is now " + ph + "\r\n")
Rails console:
params[:phone] is 808XXXXXXX <-- (REDACTED FOR PRIVACY)
Completed 500 Internal Server Error in 2383ms
TypeError (no implicit conversion of nil into String):
app/controllers/api_controller.rb:400:in `+'
That means that 'ph' is nil.
Wut?
That same gsub!(/\D/,'') part has been working fine for nearly a year, stripping hyphens, spaces, parentheses, etc.
Now it wants to completely strip everything.
I don't get it.
EDIT:
When I use this:
ph = params[:phone].gsub(/\D/,'')
I get the result that I expect, both with and without digits (eg. "8084445555" or "808-444-5555" I get the result "8084445555")
But I still want to know why. gsub! is going to replace params[:phone], I get that, but it should not be stripping non-numerics, and it is.
Here is the answer:
gsub will return the original string if it doesn't match anything. On the other hand, gsub! will return nil in such case.
If both methods match, they will return the string with the substitutions in place and there is nothing special about it, except that gsub! will modify the receiver object as you should already know.
Here are some examples that illustrate the facts. Pay special attention to the subjects and the returned values. You can try the following in irb if you want.
phone_number = "888-555-0110" #=> "888-555-0110"
stripped_pn = phone_number.gsub(/\D/, '') #=> "8885550110"
phone_number #=> "888-555-0110"
stripped_pn.gsub(/\D/, '') #=> "8885550110"
Here are the same examples with gsub!:
phone_number = "888-555-0110" #=> "888-555-0110"
stripped_pn = phone_number.gsub!(/\D/, '') #=> "8885550110"
phone_number #=> "8885550110"
stripped_pn.gsub!(/\D/, '') #=> nil

formatting JSON object in rails 3

I have a rails app has an api for iphone/android clients. I'm doing Message.find(1).to_json(:include => :user) but that adds \ around " to escape it.
How can I prevent to_json from escaping the double quotes?
>> { "a" => "blah" }.to_json
=> "{\"a\":\"blah\"}"
I would like it to be { "a" : "blah" } or { a : "blah" }
It looks like this is actually what you want.
What you are seeing is the string formatted for display (and human readability). It is delimited by double quotes, so the double quotes inside the string are escaped. In reality, the string contains double quotes, but for literal representation, they are escaped. If you stick this into a JSON parser you will find it returns the object you want.
If you were to print this out, you will find that you get the format you want.
irb(main):001:0> puts { "a" => "blah" }.to_json
{"a":"blah"}
=> nil
For further illustration, you could try parsing it. The string you ended up with returns your original object, because JSON is represented by a string. However, attempting to insert the desired content will give you a nasty syntax error or a TypeError. This is because JSON is not a literal in Ruby, whereas in JavaScript it can be used as a literal object. In Ruby it is a representation in the form of a string.
irb(main):002:0> JSON.parse("{\"a\":\"blah\"}")
=> {"a"=>"blah"}
irb(main):003:0> JSON.parse({ "a" : "blah" })
SyntaxError: (irb):3: syntax error, unexpected ':', expecting tASSOC
JSON.parse({ "a" : "blah" })
^
(irb):3: syntax error, unexpected '}', expecting $end
JSON.parse({ "a" : "blah" })
^
irb(main):004:0> JSON.parse({a:"blah"})
TypeError: can't convert Hash into String

Resources