How can I refactor this Ruby and Rails code? - ruby-on-rails

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",'')

Related

Better way to check 2 conditions based on 1 object

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

Rails ternary operator and 'this'

Does Rails have a this like javascript/Jquery does?
Take this example:
User.find_by_email(params[:candidate][:email].present? ? (u = this.id) : (u = 'not here')
or:
if User.find_by_email(params[:candidate][:email].present?
a += 1
user = this
end
I'm aware that this code might be rewritten in more efficient ways in this case, but my question is about being able to use this. Does Ruby have something like this?
In the context of a class you use self.
In these cases though this code is not in User context so you have to make an assignment.
u = User.find_by_email(params[:candidate][:email])
user_name = u.any? ? u.name : 'not here'
I prefer .any? to .present? in this context as it reads better.
Ruby uses self to denote this. I am not quite sure if you need to use self for your problems.
First scenario can be rewritten as:
u = User.find_by_email(params[:candidate][:email]).try(:id) || 'not here'
Second scenario can be rewritten as:
user = User.find_by_email(params[:candidate][:email])
a += 1 if user.present?
I'm guessing the more idiomatic ruby approach for your case would be something like the following:
User.where("email in (?)", email_arr).each do |user|
a += 1
user.foo = bar
end
but it's hard to say without seeing the all code.

Ruby: Nils in an IF statement [duplicate]

This question already has answers here:
How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?
(16 answers)
Closed 7 years ago.
I have the following very ugly ruby code in a rails app I'm working on:
if params.present?
if params[:search].present?
if params[:search][:tags_name_in].present?
...
end
end
end
All I'm trying to ask is whether params[:search][:tags_name_in] has been defined, but because params, and params[:search], and params[:search][:tags_name_in] might all be nil, if I use...
if params[:search][:tags_name_in].present?
... I get an error if there are no params or no search params.
Surely there must be a better way to do this... suggestions??
if you are just trying to see if its defined why not keep it simple and use the defined? function?
if defined?(params[:search][:tags_name_in])
Params will always be defined, so you can remove that.
To reduce the amount of code you can do
if params[:search] && params[:search][:tags_name_in]
#code
end
If params[:search] is not defined, the condition will short circuit and return nil.
You can use andand for this. It handles this exact situation:
if params[:search].andand[:tags_name_in].andand.present?
You have many choices that will return the value of params[:search][:tags_name_in] or nil if params[:search] is nil.
Clear but lengthy:
params[:search] && params[:search][:tags_name_in]
Using try (from active_support):
params[:search].try(:[], :tags_name_in)
Using rescue:
params[:search][:tags_name_in] rescue nil
Using fetch:
params.fetch(:search, {})[:tags_name_in]
Note that fetch can sometime be used to avoid the if altogether, in particular if there is nothing to do when the param is not specified:
def deal_with_tags
MyModel.where :tags => params.fetch(:search){ return }[:tags_name_in]
end
Haha, if you want to be terrible and monkeypatch nil:
class NilClass
def [](key)
nil
end
end
I would recommend a short-circuited if like the other answers suggest, though.
I usually end up doing something like this:
if params.has_key?(:search) && params[:search].has_key?(:tags_name_in)
...
end
Although if you don't mind testing against nils in your if statement you could also do this:
if params[:search] && params[:search][:tags_name_in] ...
This will not throw an error because ruby short-circuits the && operator.

Ruby Syntax: How to write "a = h[:v] if !h[:v].nil?"

I find myself often writing statements equivalent to:
deleted_at = Time.at(data[:deleted_at]) if !data[:deleted_at].nil?
i'd like to be able to write this in a more concise way. Any suggestions?
Sometimes I write this as:
deleted_at = Time.at(i) if !(i = data[:deleted_at]).nil?
But I think this makes the code harder to read so I hope there is some nicer way to do this.
I use 'unless' since I find it more readable:
deleted_at = Time.at(data[:deleted_at]) unless data[:deleted_at].nil?
or you could even use:
deleted_at = Time.at(data[:deleted_at]) if data[:deleted_at]
You could wrap that generically into a lambda block:
class Object
def unless_nil?
yield self unless self.nil?
end
end
data[:deleted_at].unless_nil? {|i| deleted_at = i }
I'd recommend to not monkey patch the Object class but better create a module and include that in the classes you need this functionality for.
data_deleted = data[:deleted_at]
deleted_at = Time.at(data_deleted) unless data_deleted.nil?
makes it more readable IMO.
Not having deleted_at declared like that is not my cup of tea. It would at least be set to nil with something like this:
deleted_at = Time.at(data[:deleted_at]) rescue nil
or
deleted_at = data[:deleted_at].nil? ? nil : Time.at(data[:deleted_at])
You can take advantage of the nil version evaluating to false to do this:
deleted_at = data[:deleted_at] and Time.at(data[:deleted_at])
Although now having written this i think i prefer the if version.
Code is written far less than its read - concise doesn't always equate to readable.
You can also do
data[:deleted_at] and deleted_at = Time.at(data[:deleted_at])
although whether that's better is a matter of taste.
If it's only Time.at that's giving you problems, you could write
class Time
def nil_friendly_at(time)
return nil if time.nil? # Or "unless time"
at(time)
end
end
Or you could monkey-patch Time.at itself if you want (I'm not recommending this though!)

Another way to write: if some_variable && some_valiable.size == 2

In Ruby & in RoR I oft find myself testing whether an object exists, then whether an object's properties match some criteria. Like so:
if params[:id] && params[:id].size == 40
...do stuff
end
Is there a more efficient way to do this? Something like:
if params[:id].size == 40 rescue false
but without using the rescue?
With Rails 2.3 you can use Object#try method:
if params[:id].try(:size) == 40
# do stuff
end
try will return nil when called on nil (with any arguments). Hope that makes sense.
You can do it witout additional gems.
if params[:id].to_a.size == 40
... do stuff
end
Try the andand gem:
require 'andand'
if params.andand.size == 40
...do stuff
end

Resources