Construct a dynamic route path in Ruby - ruby-on-rails

I want to construct a dynamic route path in ruby, something like this
route = (a == a) ? "foo" : (b == b) ? "bar" : "default"
link_to(event.try(:name), admin_"#{route}"_path('params goes here')
I very well know what I have tried is wrong. It should be done with dynamic method creations using class_eval or define_method I am not sure about that. Also, I am not familiar with those concepts. I can google but it would take much time to get a solution.
Anyone, please help me solve this quickly. Thanks in advance.

This is pretty straightforward:
send("admin_#{route}_path", params)
You may want to wrap that up in a helper method to clean things up:
def admin_path_for_ab(a, b, params = nil)
route =
if (a == 'a')
"foo"
elsif (b == 'b')
"bar"
else
"default"
end
send("admin_#{route}_path", params)
end
As a note, nesting ternaries (x ? y : z) is usually a bad idea, and a == a is always true.

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.

How to pass a hash as a query with "OR-join"?

I have a hash with booleans
({"u25" => true, "f26t49" => true, "o50" => true, ..});
all in all there are 19 booleans and I want to check these with a table in my db and get those data sets, how have at least one match. I had try it with
"Model.all(:conditions => hash)" or "Model.where(hash)"
but there I get the query
"..u25 == true AND f26t49 == true AND o50 == true..."
but I need something like this:
"..u25 == true OR f26t49 == true..."
I hope you could help me!
ADDITION:
I tryed to make a workaround and generate a query string out of the hash. If i write this string directly (m = Model.where("u25 == 't'")) it works but if I pass a varaible
#query = '"u25 == ' + "'t'" + '"'
m = Model.where(#query)
than m is nil!
I think that your workaround put you in the right direction. Here's what I would do:
# model.rb
def self.search_with_conditions(hash)
query = hash.map{|k,v| "#{k} == #{v}"}.join(' OR ')
where(query)
end
And then in your controller, simply call:
m = Model.search_with_conditions(hash)
Sidenote: This might work for some DBs and not for others as some use 1 as the value for true. Also, I didn't have time to try this so I may be missing some quotation marks in that condition. I think it could be '#{v}'.
EDIT: After learning a little bit more about Rails, I must add a disclaimer here:
If the strings k or v are part of some user input, this approach is susceptible to SQL injection. Use this method with care.

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.

How do you compare multiple RHS values in RSPec?

I'm new to RSpec, and I'm trying to run "should == A || B", but it's ignoring the 'B' and is only comparing with 'A' (and thus failing when val is 'B'):
Sample.find(:all).map(&:param).each{|val| val.should == 'A'||'B'}
Does anyone know how I include the 'B' in the comparison?
['A', 'B'].should include(val)
That might get your spec passing, but is it what you want to test? That the return value is a member of a set? If so, then perhaps this is a good solution.
You can also do:
(x == A || x == B).should be_true
Sample.find(:all).map(&:param).each{ |val| ['A', 'B'].should.include?(value) }
However, this does seem like a bit of an odd test to write.
Assuming you are trying to perform an existence check, I would rewrite your code as follows:
Sample.exists?(:conditions => {:params => %w(A B)}).should_be_true

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