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.
Related
I would like to compare two hashes and forces them to be equal:
one with Symbols on keys and values
the second with only strings.
e.g:
sym_hash = {:id=>58, :locale=>:"en-US"}
string_hash = {"id"=>58, "locale"=>"en-US"}
Try like this does not work:
> sym_hash == string_hash
=> false
I first tried to symbolized the string_hash:
> string_hash.deep_symbolize_keys
=> {:id=>58, :locale=>"en-US"}
But it is still false because sym_hash still has : in front of locale var.
Then I tried to stringified the sym_hash:
> sym_hash.with_indifferent_access
=> {"id"=>58, "locale"=>:"en-US"}
But when I test for equality it is still false for the same reasons.
EDIT
To answer many comments abouy why I wanted those hashes to be equal here, I'll explain what I'm trying to do.
I'm using Reque to manage my jobs. Now I wanted to do a class to avoid having the same* job running, or being enqueued twice in the same time.
(same: for me the same job is a job having the same parameters, I would like to be able to enqueu twice the same jobs having differents ids for instance.)
For that I'm a using the plugin resque-status, so far I'm able to know when a job is running or not. Beside, when I save the params using set I notice that the message written to Redis(because resque-status is using Redis to keep track of the job's status) is not properly saved with symbols.
Here is my class:
# This class is used to run thread-lock jobs with Resque.
#
# It will check if the job with the exact same params is already running or in the queue.
# If the job is not finished, it will returns false,
# otherwise, it will run and returns a the uuid of the job.
#
class JobLock
def self.run(obj, params = {})
# Get rid of completed jobs.
Resque::Plugins::Status::Hash.clear_completed
# Check if your job is currently running or is in the queue.
if !detect_processing_job(obj, params)
job_uuid = obj.create(params)
Resque::Plugins::Status::Hash.set(job_uuid,
job_name: obj.to_s,
params: params)
job_uuid
else
false
end
end
def self.detect_processing_job(obj, params = {})
Resque::Plugins::Status::Hash.statuses.detect do |job|
job['job_name'] == obj.to_s && compare_hashes(job['params'], params)
end
end
def self.compare_hashes(string_hash, sym_hash)
[sym_hash, string_hash].map do |h|
h.map { |kv| kv.map(&:to_s) }.sort
end.reduce :==
end
end
And here how I can use it:
JobLock.run(MyAwesomeJob, id: 58, locale: :"en-US")
As you can see I used #mudasobwa's answer but I hope there is a easier way to achieve what I am trying to do!
How about this?
require 'set'
def sorta_equal?(sym_hash, str_hash)
return false unless sym_hash.size == str_hash.size
sym_hash.to_a.to_set == str_hash.map { |pair|
pair.map { |o| o.is_a?(String) ? o.to_sym : o } }.to_set
end
sym_hash= {:id=>58, :locale=>:"en-US"}
sorta_equal?(sym_hash, {"id"=>58, "locale"=>"en-US"}) #=> true
sorta_equal?(sym_hash, {"locale"=>"en-US", "id"=>58 }) #=> true
sorta_equal?(sym_hash, {"id"=>58, "local"=>"en-US", "a"=>"b" }) #=> false
sorta_equal?(sym_hash, {"id"=>58, "lacole"=>"en-US"}) #=> false
sorta_equal?(sym_hash, {"id"=>58, [1,2,3]=>"en-US"}) #=> false
sorta_equal?({}, {}) #=> true
class A; end
a = A.new
sorta_equal?({:id=>a, :local=>:b}, {"id"=>a, "local"=>"b"}) #=> true
You could try to convert both hashes to JSON, and then compare them:
require 'json'
# => true
sym_hash = {:id=>58, :locale=>:"en-US"}
# => {:id=>58, :locale=>:"en-US"}
string_hash = {"id"=>58, "locale"=>"en-US"}
# => {"id"=>58, "locale"=>"en-US"}
sym_hash.to_json == string_hash.to_json
# => true
The version below works as PHP force-coercion equality:
[sym_hash, string_hash].map do |h|
h.map { |kv| kv.map(&:to_s) }.sort
end.reduce :==
BTW, it’s not a one-liner only because I respect people with smartphones. On terminals of width 80 it’s a perfect oneliner.
To coerce only symbols to strings, preserving numerics to be distinguished from their string representations:
[sym_hash, string_hash].map do |h|
h.map { |kv| kv.map { |e| e.is_a?(Symbol) ? e.to_s : e } }.sort
end.reduce :==
The value of locale in sym_hash is a Symbol :"en-US",
while the value of locale in string_hash is a String.
So they are not equal.
Now if you do:
sym_hash = {:id=>58, :locale=>"en-US"}
string_hash = {"id"=>58, "locale"=>"en-US"}
string_hash.symbolize_keys!
sym_hash == string_hash
=> true
Finaly, to answer my problem I didn't need to force comparaison between hashes. I use Marshal to avoid the problem
class JobLock
def self.run(obj, params = {})
# Get rid of completed jobs.
Resque::Plugins::Status::Hash.clear_completed
# Check if your job is currently running or is in the queue.
if !detect_processing_job(obj, params)
job_uuid = obj.create(params)
Resque::Plugins::Status::Hash.set(job_uuid,
job_name: obj.to_s,
params: Marshal.dump(params))
job_uuid
else
false
end
end
def self.detect_processing_job(obj, params = {})
Resque::Plugins::Status::Hash.statuses.detect do |job|
job['job_name'] == obj.to_s && Marshal.load(job['params']) == params
end
end
end
Anyway, I let this question here because maybe it will help some people in the future...
This is a slight deviation from the original question and an adaptation of some of the suggestions above. If the values can also be String / Symbol agnostic, then may I suggest:
def flat_hash_to_sorted_string_hash(hash)
hash.map { |key_value| key_value.map(&:to_s) }.sort.to_h.to_json
end
this helper function can then be used to assert two hashes have effectively the same values without being type sensitive
assert_equal flat_hash_to_sorted_string_hash({ 'b' => 2, a: '1' }), flat_hash_to_sorted_string_hash({ b: '2', 'a' => 1 }) #=> true
Breakdown:
by mapping a hash, the result is an array
by making the keys / values a consistent type, we can leverage the Array#sort method without raising an error: ArgumentError: comparison of Array with Array failed
sorting gets the keys in a common order
to_h return the object back to a hash
NOTE: this will not work for complex hashes with nested objects or for Float / Int, but as you can see the Int / String comparison works as well. This was inspired by the JSON approach already discussed, but without needing to use JSON, and felt like more than a comment was warranted here as this was the post I found the inspiration for the solution I was seeking.
I frequently find myself writing Ruby code where I check for the presence of a value and subsequently do something with that value if it is present. E.g.
if some_object.some_attribute.present?
call_something(some_object.some_attribute)
end
I think it would be cool, if it could be written as
some_object.some_attribute.presence { |val| call_something(val) }
=> the return value of call_something
Anyone know if there's such a feature in Ruby or though activesupport?
I opened a pull request for this feature.
You can use a combination of presence and try:
If try is called without arguments it yields the receiver to a given block unless it is nil:
'foo'.presence.try(&:upcase)
#=> "FOO"
' '.presence.try(&:upcase)
#=> nil
nil.presence.try(&:upcase)
#=> nil
You could try
do_thing(object.attribute) if object.attribute
This is usually fine, unless the attribute is a boolean. In which case it will not call if the value is false.
If your attribute can be false, use .nil? instead.
do_thing(object.attribute) unless object.attribute.nil?
Though there is no such functionality out of the box, one could do:
some_object.some_attribute.tap do |attr|
attr.present? && call_smth(attr)
end
On the other hand, Rails provides so many monkeypatches, that one could append one to this circus:
class Object
def presense_with_rails
raise 'Block required' unless block_given?
yield self if self.present? # requires rails
end
def presense_without_rails
raise 'Block required' unless block_given?
skip = case self
when NilClass, FalseClass then true
when String, Array then empty?
else false
end
yield self unless skip
end
end
I'm trying to substitute an expression unless the expression is one of two values.
def substitute_string (string)
string.gsub('abc', 'xyz') unless string == ('dabc' || 'eabc')
end
substitute_string('jjjjjabc')
=> 'jjjjjxyz'
substitute_string('dabc')
=> 'dabc'
substitute_string('eabc')
=> 'exyz'
I expected substitute_string('eabc') to return ('eabc') since I stated that in the unless block, which I passed two values.
I don't understand why this doesn't work, and what I can do to make 'eabc' return 'eabc'.
('dabc' || 'eabc') is a boolean expression that evaluates to true and returns 'dabc'.
Use two or's:
unless string == 'dabc' || string == 'eabc'
Or use =~ (regex pattern match)
unless string =~ /(dabc|eabc)/
Since you indicated you're using Rails, you can also use in? like this:
unless string.in? ['dabc', 'eabc']
It is because (1) 'dabc' || 'eabc' is equivalent to 'dabc', and nowhere in your code does 'eabc' appear in a meaningful way, and because (2) it only returns nil when the condition is met according to the way you used unless.
def substitute_string(string)
case string
when 'dabc', 'eabc' then string
else string.gsub('abc', 'xyz')
end
end
Apart from the fun of obscure technicalities about what is returned when and in what situations, I don't see a lot of merit in not being more explicit with the return. The very fact that this issue was brought and subsequently debated on SO is exactly why writing code (working code to be sure) in this obscure fashion will lead to confusion for developers interpreting this code, and leads to buggy software.
The only benefit I see to this is that it's on one line.
def substitute_string(string)
string.gsub('abc', 'xyz') unless ['dabc', 'eabc'].include?(string)
end
I personally would prefer the following as it makes it clear what your intentions are:
def substitute_string(string)
return string if ['dabc', 'eabc'].include?(string)
string.gsub('abc', 'xyz')
end
'dabc' || 'eabc' will always equal true since it just means condition or condition where condition is a string. Since a string is not nil or false it evaluates to true. You could check whether the string is in an array values instead:
def substitute_string(string)
string.gsub('abc', 'xyz') unless ['dabc', 'eabc'].include?(string)
end
I'm submitting a parameter show_all with the value true. This value isn't associated with a model.
My controller is assigning this parameter to an instance variable:
#show_all = params[:show_all]
However, #show_all.is_a? String, and if #show_all == true always fails.
What values does Rails parse as booleans? How can I explicitly specify that my parameter is a boolean, and not a string?
UPDATE: Rails 5:
ActiveRecord::Type::Boolean.new.deserialize('0')
UPDATE: Rails 4.2 has public API for this:
ActiveRecord::Type::Boolean.new.type_cast_from_user("0") # false
PREVIOUS ANSWER:
ActiveRecord maintains a list of representations for true/false in https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb
2.0.0-p247 :005 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean("ON")
2.0.0-p247 :006 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean("F")
This is not part of Rails' public API, so I wrapped it into a helper method:
class ApplicationController < ActionController::Base
private
def parse_boolean(value)
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
end
and added a basic test:
class ApplicationControllerTest < ActionController::TestCase
test "parses boolean params" do
refute ApplicationController.new.send(:parse_boolean, "OFF")
assert ApplicationController.new.send(:parse_boolean, "T")
end
end
I wanted to comment on zetetic answer but as I can't do that yet I'll post this as an answer.
If you use
#show_all = params[:show_all] == "1"
then you can drop ? true : false because params[:show_all] == "1" statement itself will evaluate to true or false and thus ternary operator is not needed.
This question is rather old, but since I came across this issue a couple of times, and didn't like any of the solutions proposed, I hacked something myself which allows to use multiple strings for true such as 'yes', 'on', 't' and the opposite for false.
Monkey patch the class String, and add a method to convert them to boolean, and put this file in /config/initializers as suggested here: Monkey Patching in Rails 3
class String
def to_bool
return true if ['true', '1', 'yes', 'on', 't'].include? self
return false if ['false', '0', 'no', 'off', 'f'].include? self
return nil
end
end
Notice that if the value is none of the valid ones either for true or false, then it returns nil. It's not the same to search for ?paid=false (return all records not paid) than ?paid= (I don't specify if it has to be paid or not -- so discard this).
Then, following this example, the logic in your controller would look like this:
Something.where(:paid => params[:paid].to_bool) unless params[:paid].try(:to_bool).nil?
It's pretty neat, and helps to keep controllers/models clean.
#show_all = params[:show_all] == "1" ? true : false
This should work nicely if you're passing the value in from a checkbox -- a missing key in a hash generates nil, which evaluates to false in a conditional.
EDIT
As pointed out here, the ternary operator is not necessary, so this can just be:
#show_all = params[:show_all] == "1"
You could change your equality statement to:
#show_all == "true"
If you want it to be a boolean you could create a method on the string class to convert a string to a boolean.
I think the simplest solution is to test "boolean" parameters against their String representation.
#show_all = params[:show_all]
if #show_all.to_s == "true"
# do stuff
end
Regardless of whether Rails delivers the parameter as the String "true" or "false" or an actual TrueClass or FalseClass, this test will always work.
You could just do
#show_all = params[:show_all].downcase == 'true'
It's worth noting that if you're passing down a value to an ActiveModel in Rails > 5.2, the simpler solution is to use attribute,
class Model
include ActiveModel::Attributes
attribute :show_all, :boolean
end
Model.new(show_all: '0').show_all # => false
As can be seen here.
Before 5.2 I use:
class Model
include ActiveModel::Attributes
attribute_reader :show_all
def show_all=(value)
#show_all = ActiveModel::Type::Boolean.new.cast(value)
end
end
Model.new(show_all: '0').show_all # => false
Another approach is to pass only the key without a value. Although using ActiveRecord::Type::Boolean.new.type_cast_from_user(value) is pretty neat, there might be a situation when assigning a value to the param key is redundant.
Consider the following:
On my products index view by default I want to show only scoped collection of products (e.g. those that are in the stock). That is if I want to return all the products, I may send myapp.com/products?show_all=true and typecast the show_all parameter for a boolean value.
However the opposite option - myapp.com/products?show_all=false just makes no sense since it will return the same product collection as myapp.com/products would have returned.
An alternative:
if I want to return the whole unscoped collection, then I send myapp.com/products?all and in my controller define
private
def show_all?
params.key?(:all)
end
If the key is present in params, then regardless of its value, I will know that I need to return all products, no need to typecast value.
You can add the following to your model:
def show_all= value
#show_all = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
You could convert all your boolean params to real booleans like this:
%w(show_all, show_featured).each do |bool_param|
params[bool_param.to_sym] = params[bool_param.to_sym] == "true"
end
In this solution, nil parameters would become false.
While not explicitly what the question is about I feel this is appropriately related; If you're trying to pass true boolean variables in a rails test then you're going to want the following syntax.
post :update, params: { id: user.id }, body: { attribute: true }.to_json, as: :json
I arrived at this thread looking for exactly this syntax, so I hope it helps someone looking for this as well. Credit to Lukom
I.e., is
Post.title?
equivalent to
Post.title.present?
No.
Object#present? is the same thing as calling !obj.blank?.
The "attribute?" method might end up calling the same code, but it might not, and it depends on the column type that you're dealing with.
The easiest way to see these not return the same value is to access a numeric column. Say you had foo.score as a decimal column in your db, and you set it to zero. You'd see the following behavior.
foo.score = 0
foo.score? # false
foo.score.present? # true
The code for the "?" method is in ActiveRecord::AttributeMethods.
def query_attribute(attr_name)
unless value = read_attribute(attr_name)
false
else
column = self.class.columns_hash[attr_name]
if column.nil?
if Numeric === value || value !~ /[^0-9]/
!value.to_i.zero?
else
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
!value.blank?
end
elsif column.number?
!value.zero?
else
!value.blank?
end
end
end