Currently I have the following 2 lines of code
errors.add_to_base I18n.t :error_message if value != 1
return false if !errors.blank?
Is it possible to condense this into 1 line of code? I need to do this in multiple places with different error message and condition. Also, "return false" is to stop the flow of an ActiveRecord lifecycle.
Hmm. If you know errors.blank? will be true unless the first condition fires then:
(errors.add_to_base I18n.t :error_message; return) if value != 1
Update: Aha, you are willing to define a method. How about a Proc object? It's better than a method here in that if the Proc block returns then the invocation will return from the surrounding method.
test = Proc.new do |cond, msg|
errors.add_to_base I18n.t msg if cond
return unless errors.blank?
end
# ...
test.call value != 1, :error_message
Note that you don't need to return false as a plain return will return nil and that will be good enough unless some sadist is doing something like f().class == NilClass. :-)
You can take advantage of how the boolean logic operators work and do something like this:
value != 1 && errors.add_to_base I18n.t :error_message && return false
Not very clear, I wouldn't recommend using it. Also if errors.add_to_base returns something that's "falseish" the return false wouldn't happen.
Remember: "Always code as if the person who will maintain your code is a violent psychopath who knows where you live”
Related
I commonly need to use this kind of methods where I have to check if an object exists and if this object returns a specific value or a behavior. Is that a better way to write this code?
def set_current_theme
if current_tenant && current_tenant.has_custom_domain?
#theme = Theme.last || Theme.create
end
end
At a first glance, I would just add one conditional: if current_tenant.has_custom_domain? and that should be enough. But the result is generally that there is no such method (in this case has_custom_domain?) for nil class.
Shorter (and i think better) way is to use &. (it's shorthand for try!) like this.
if current_tenant&.has_custom_domain?
#theme = Theme.last || Theme.create
end
What does &. (ampersand dot) mean in Ruby?
I would suggest early return (so called guard clause) instead of :if statement, because you don't have :else clause:
def set_current_theme
return unless current_tenant&.has_custom_domain?
#theme = Theme.last || Theme.create
end
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
def role?(role)
return !!self.roles.find_by_name(role.to_s.camelize)
end
Can you help me understand what's happening in the code above? I'm new to Rails/Ruby.
Thanks
It's negation (!) operator repeated twice.
Note that only ruby objects evaluating to false (in boolean expression) are nil and false itself.
Therefore,
some_role will be true, !some_role is false and !!some_role is true again.
nil is false, !nil is true and !!nil is false.
So, this is a "clever" way to check whether role returned from find_by_name is nil or not. (And therefore whether role with such name exists or not)
I guess, I don't have to tell you that doing this is bad for readability. You can always check if result is nil by normal means, like result.nil? or result == nil.
This is more readable. No need for the 'self' or 'return'. 'present?' is the opposite of 'nil?' so no negation is required.
def role?(role)
roles.find_by_name(role.to_s.camelize).present?
end
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