How do i reduce the following code to one line in Ruby?
unless(current_facebook_user.nil?)
unless(current_facebook_user.client.nil?)
unless(current_facebook_user.client.default_params.nil?)
val = current_facebook_user.client.default_params
end
end
end
You can using the try() method from active_support/core_ext/object/try.rb introduced since Rails 2.3.2
val = current_facebook_user.try(:client).try(:default_params)
Method Try
Or directly using Safe navigation operator introduced since Ruby 2.3.0
val = current_facebook_user&.client&.default_params
documented here
more on Safe navigation operator
Use || operator in one unless: val = ... unless cond1 || cond2 || cond3
Or use && on negations of the conditions in if: val = ... if !cond1 && !cond2
You may not need the third check, as if .default_params is nil, val will be evaluated nil too (if it started nil, this is no problem; if it was already populated, you might want to keep the third guard there :-)
val = current_facebook_user.client.default_params if current_facebook_user && current_facebook_user.client && current_facebook_user.client.default_params
val = current_facebook_user.client.default_params unless current_facebook_user.nil? or current_facebook_user.client.nil? or current_facebook_user.client.default_params.nil?
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
How can I refactor this code?
if env["rack.request.form_hash"] && env["rack.request.form_hash"]["authenticity_token"]
env["rack.request.form_hash"]["authenticity_token"]=env["rack.request.form_hash"]["authenticity_token"].gsub("\r\n",'')
end
env["rack.request.form_hash"]["authenticity_token"] = env["rack.request.form_hash"]["authenticity_token"].gsub("\r\n",'') rescue nil
or with in place editing
env["rack.request.form_hash"]["authenticity_token"].gsub!("\r\n",'') rescue nil
if you have the andand gem, you can skip the check and go straight to:
env["rack.request.form_hash"]["authenticity_token"].andand.gsub("\r\n",'')
The hash indexes seem to be reused everywhere, maybe you can start there.
key1 = "rack.request.form_hash"
key2 = "authenticity_token"
env[key1] && env[key1][key2]
Nothing clever, but significantly shortens the line.
Something like this could work:
env[key1][key2].gsub!('\r\n','') if env.has_key?(key1) && env[key1].has_key?(key2)
I would recommend:
if (rrf = env["rack.request.form_hash"]) && rrf_at = rrf["authenticity_token"] then rrf_at.gsub!("\r\n",'') end
or similar but shorter:
rrf_at.gsub!("\r\n",'') if (rrf = env["rack.request.form_hash"]) && rrf_at = rrf["authenticity_token"]
It's DRY, concise and does not use rescue "hacks" ;-D
Rather then using andand or try, I would do:
if env.fetch("rack.request.form_hash", {})["authenticity_token"].to_s.gsub("\r\n",'')
or add to_hash to the inventory of useful NilClass methods (to_a, to_s, to_i, etc):
class NilClass; def to_hash; {} end end
and do:
if env["rack.request.form_hash"].to_hash["authenticity_token"].to_s.gsub("\r\n",'')
I am trying to say this
self.preferred_amount * object.each{|li|li.variant}.collect{|li|li.weight}
The only problem is that certain weights equal nil.
Being that the case, I would like to add that if they do equal nil, make them equal 0.
Is there any way to incorporate this logic in the same line?
Or is there a way I can make this statement even more refactored than it is?
Change li.weight to li.weight || 0
|| is the "short circuit or" operator. If its left hand side is truthy (neither false nor nil), it returns the left hand side, otherwise it returns the right hand side.
There is a feature in MRI >= 1.8.7 that will let you make this terser. Instead of:
each{|li|li.variant}
you can write
each(&:variant)
In versions of Ruby before 1.8.7, require the backports gem to get this feature.
Better than that, move all of the logic into object's class, e.g.
class Whatever
def variant_weights
each(&:variant).collect{ |li| li.weight || 0}
end
end
and to use it:
self.preferred_amount * object.variant_weights
However, note that it is a bug to multiply a scalar amount by an array. If you mean to sum the weights, then:
class Whatever
def total_variant_weights
each(&:variant).collect{ |li| li.weight || 0}.inject(&:+)
end
end
and to use it:
self.preferred_amount * object.total_variant_weights
Note, all the answers above are correct for your purpose, but to answer your question directly:
How do I write a conditional statement in a single line? Rails
You can use ternary operators. They take the following form:
assertion ? value_if_true : value_if_false
# if assertion is true, then value_if_true, otherwise, value_if_false
for example:
puts 4 < 5 ? 'you are on Earth' : 'you are on another planet'
<%= #user.is_admin? ? 'you can access this page' : 'you aren\'t allowed to be here' %>
Like I said, the answers above are actually what you want for this particular operation (not that a ternary operator won't work in this case). I just wanted to give you some more insight into one-liners.
Also note, this is not Ruby-specific. Most programming languages (including Ruby, PHP, CF, AS, JAVA, C, C#...) have ternary operators.
just || 0 the weight:
self.preferred_amount * object.each{|li|li.variant}.collect{|li|li.weight || 0}
Try
.collect{|li|li.weight || 0}
The each seems redundant. What about:
self.preferred_amount * object.collect { |o| o.variant.weight.to_i }
or if you really meant to sum the weights:
self.preferred_amount * object.inject { |sum, o| sum + o.variant.weight.to_i }
I've a strange situation with my rails application (2.3.8)
I want to assign a no nil value to some variable in my controller, so I wrote this code:
#myValue = session[:value] or []
In my view I wrote:
<%= #myvalue.size %>
When I display my page, I have a nil error.
I've tried the nil or [] in the IRB and I get the []. So my question is if anyone know why is working different in rails?
P.S.: The only gem that I'm using is cell version 3.3.3.
Thanks!
The 'or' keyword has too low a precedence for this code to work. Use '||' instead. To demonstrate the point:
ruby-1.9.2-p0 > a = nil or true; a
=> nil
ruby-1.9.2-p0 > a = nil || true; a
=> true
It's an order of operations thing.
#myValue = (session[:value] or []) will work. I believe #myValue = session[:value] or [] is interpreted as (#myValue = session[:value]) or []. But #myValue = session[:value] || [] worked too without parens. || has higher precedence than or.
The idiom is to do session[:value] || [], using ||, not or. You can also try session[:value].to_a. This works because nil.to_a becomes [].
if args.size == 5
value_for,alt_currency_id,amount,exchange_rate_code,tran_dt = args
else
value_for,alt_currency_id,amount,exchange_rate_code,year_no,period_no = args
end
Any Better way to write this condition ??
I would just skip the condition entirely. If you don't have the fifth argument, period_no will simply be nil.
If period_no needed to be set to some default you could follow up with:
period_no ||= sane_default
Definitely is a code smell, specially since the variable is called args. If you're passing all these arguments as optional values, the best approach is make the variable arguments into a hash.
def whatever(value_for, alt_currency_id, amount, options = {})
tran_dt = options[:tran_dt]
year_no = options[:year_no]
period_no = options[:period_no]
...
end
To strictly meet your requirements, I'd do this:
value_for, alt_currency_id, amount, exchange_rate_code = args.shift(4)
tran_dt, year_no, period_no = [nil, nil, nil] # or some sensible defaults
case args.size
when 1 then tran_dt = args.shift
when 2 then year_no, period_no = args.shift(2)
end
But this code has a smell to it. I'd look at redesigning how that method gets called.
Perhaps assign period_no to nil by default, and use that to determine which argument set you are working with:
def process_record(value_for, alt_currency_id, amount, exchange_rate_code, tran_dt, period_no=nil)
year_no = period_no ? tran_dt : nil
puts "tran_dt: #{tran_dt.inspect}"
puts "year_no: #{year_no.inspect}"
puts "period_no: #{period_no.inspect}"
end
process_record(:foo, :bar, :baz, :buz, Time.now)
# Output:
#
# tran_dt: Mon Sep 13 15:52:54 -0400 2010
# year_no: nil
# period_no: nil
process_record(:foo, :bar, :baz, :buz, 2010, 1)
# Output:
#
# tran_dt: 2010
# year_no: 2010
# period_no: 1
Here's one way of DRYing up your code a bit:
value_for, alt_currency_id, amount, exchange_rate_code, year_no, period_no = args
if period_no.nil?
tran_dt = year_no
year_no = nil # May or may not be needed, depending on later code
end
Ruby has two ternary operators as well that I'm aware of
a = true ? 'a' : 'b' #=> "a"
b = false ? 'a' : 'b' #=> "b"
or
a = (true && 'a') || b #=> "a"
b = (false && 'a') || b #=> "b"
Are you processing commandline? Just leave as it is, for me it is most readable at first look :) Otherwise it may smell perlish.
You simply see what is required set for 5 arguments or else.
If these are not command line args I suggest introducing hash.