Rails ternary operator and 'this' - ruby-on-rails

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.

Related

Rails/Ruby one-liner unless zero/nil?

Is there a way to make this situation more compact in rails views?
Eg I have haml
= object.count unless object.count ==0
I sort of don't like that has I'm repeating the function there, I would much rather have something like
= object.count unless ==0
Eg if I had more complex statements
= object.relations.where(attribute: "something").count unless zero?
I could split that into two lines say
- cnt = object.relations.where(attribute: "something").count
= cnt unless cnt==0
But for each situation I would have multiple lines, and storing a variable to use once sucks.
EDIT: just to elaborate I want to check if the number is 0, and if so not display anything. It looks nicer in the view that way.
UPDATE:
One of the answers made come up with a solution along these lines
class Object
def unless
self unless yield(self)
end
end
So I can call whatever object I have with a block eg. .unless{|c| c<1}
This lets me tack the conditionals on, and keeps it pretty clear what is going on :), bonus is as it's block driven I can use this on any object :P.
Thanks everyone :)
UPDATE EVEN MORE
Having |c| in the block sucked. So I looked up the api and changed it too
class Object
def unless(&block)
self unless instance_eval(&block)
end
end
So now I can use .count.unless{zero?} to accomplish this :P. Or if I have a complicated condition I can add that in with |c| etc.
If object is an array you can use object.empty? (or object.any? for the reverse case)
Just create a view helper:
def display_count_or_nothing(array)
array.count unless array.count == 0
end
In the view you can use it like this:
<%= display_count_or_nothing(array) %>
i think the following is nice and clear, although i hate the variable "object",
it would be much nicer if the name of the variable described the contents of the array (as plural)
= object.count unless object.empty?
If this is only about count, you can monkey patch Enumerable:
module Enumerable
def count_or_empty_string
self.any? ? self.count : ''
end
end
If object is an enumerable, you can do this:
= object.count_or_empty_string
This will return an "" if object.count == 0 else it will return an integer. So there is no need for unless or if in your HAML anymore.

Append query string to url

I have a callback url string params[:callback] and I need to append a query string "&result=true" and redirect the user. The better way I found of doing this is using addressable but I think the code is too big for task like this especially when we are talking about ruby:
callback = Addressable::URI.parse(params[:callback])
query = callback.query_values
query[:result] = 'true'
callback.query_values = query
redirect_to callback.to_s
Is there a more elegant way of getting the same result as this snippet?
I wan't to bring update to this topic, because any of the solutions didn't work me.
The reason being, that it seems that callback.query_values returns Nil if the actual url doesn't have existing query values.
Therefore if you have urls such as: http://www.foo.com and http://www.foo.com?bar=1 you should use the following code:
url = "http://www.foo.com" # or params[:callback] obviously. :)
callback = Addressable::URI.parse(url)
callback.query_values = (callback.query_values || {}).merge({
result: true
})
redirect_to callback.to_s
Cheers.
if you don't need any URL validations you can do this (looks a little bit dirty):
url = params[:callback]
redirect_to url + (url.include?('?') ? '&' : '?') + 'result=true'
otherwise you have to use either some external library (like Addressable) or URI module from standard library
callback.query_values = callback.query_values.merge({"result" => "true"})
I think you're pretty close to optimal. you could crush out a line or two,
but it doesn't really gain you anything.
callback = Addressable::URI.parse(params[:callback])
callback.query_values = callback.query_values.merge {:results => 'true' }
redirect_to callback.to_s
If the callback is always inside your application, you might have some other options with varying degrees of coolness, but you didn't specify if that was the case or not.
years later, I find a better solution of this problem.
Get the value from the super first, then do any tweaks we need using Addressable
def url_for(options={})
val = super
if params[:locale].present?
parsed = Addressable::URI.parse(val)
query_array = parsed.query_values(Array) || []
query_array << ['locale', params[:locale]]
parsed.query_values = query_array
val = parsed.to_s
end
val
end
Let me offer this one modestly. I suggest using only strings for query parameters keys and values (like Arye noted) . Also, NilClass instances have a to_h method, which allows to remove some brackets:
callback = Addressable::URI.parse(params[:callback])
callback.query_values = callback.query_values.to_h.merge("result" => "true")
redirect_to callback.to_s
You can try with merge
request.parameters.merge({:result => true})
this will add your parameter to the ones already defined.

How can I refactor this Ruby and Rails code?

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

If string is empty then return some default value

Often I need to check if some value is blank and write that "No data present" like that:
#user.address.blank? ? "We don't know user's address" : #user.address
And when we have got about 20-30 fields that we need to process this way it becomes ugly.
What I've made is extended String class with or method
class String
def or(what)
self.strip.blank? ? what : self
end
end
#user.address.or("We don't know user's address")
Now it is looking better. But it is still raw and rough
How it would be better to solve my problem. Maybe it would be better to extend ActiveSupport class or use helper method or mixins or anything else. What ruby idealogy, your experience and best practices can tell to me.
ActiveSupport adds a presence method to all objects that returns its receiver if present? (the opposite of blank?), and nil otherwise.
Example:
host = config[:host].presence || 'localhost'
Phrogz sort of gave me the idea in PofMagicfingers comment, but what about overriding | instead?
class String
def |(what)
self.strip.blank? ? what : self
end
end
#user.address | "We don't know user's address"
Since you're doing this in Ruby on Rails, it looks like you're working with a model. If you wanted a reasonable default value everywhere in your app, you could (for example) override the address method for your User model.
I don't know ActiveRecord well enough to provide good code for this; in Sequel it would be something like:
class User < Sequel::Model
def address
if (val=self[:address]).empty?
"We don't know user's address"
else
val
end
end
end
...but for the example above this seems like you'd be mixing view logic into your model, which is not a good idea.
Your or method might have some unwanted side-effects, since the alternative (default) value is always evaluated, even if the string is not empty.
For example
#user.address.or User.make_a_long_and_painful_SQL_query_here
would make extra work even if address is not empty. Maybe you could update that a bit (sorry about confusing one-liner, trying to keep it short):
class String
def or what = ""
self.strip.empty? ? block_given? ? yield : what : self
end
end
#user.address.or "We don't know user's address"
#user.address.or { User.make_a_long_and_painful_SQL_query_here }
It is probably better to extend ActiveRecord or individual models instead of String.
In your view, you might prefer a more explicit pattern like
#user.attr_or_default :address, "We don't know the user's address"
Ruby:
unless my_str.empty? then my_str else 'default' end
RoR:
unless my_str.blank? then my_str else 'default' end
I recommend to use options.fetch(:myOption, defaultValue) because it works great with boolean flags like the ones mentioned above and therefore seems better to use in general.
Examples
value = {}
puts !!(value.fetch(:condition, true)) # Print true
value = {}
value[:condition] = false
puts !!(value.fetch(:condition, true)) # Print false
value = {}
value[:condition] = true
puts !!(value.fetch(:condition, true)) # Print true
value = {}
value[:condition] = nil
puts !!(value.fetch(:condition, true)) # Print false

How do write conditional statement in a single line? rails

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 }

Resources