Ruby shortcut for "if (number in range) then..." - ruby-on-rails

Is there a Ruby shortcut for the following?
if (x > 2) and (x < 10)
do_something_here
end
I thought I saw something to that effect, but cannot find a reference to it. Of course it's hard to lookup when you don't know what operator you're looking for.

if (3..9).include? x
# whatever
end
As a sidenote, you can also use the triple equals operator for ranges:
if (3..9) === x
# whatever
end
This lets you use them in case statements as well:
case x
when 3..9
# Do something
when 10..17
# Do something else
end

do_something if (3..9).include?( x ) # inclusive
do_something if (3...10).include?( x ) # inclusive start, exclusive end
See the Range class; you can read an introduction to them hosted on my website.

Comparable#between?
do_something if x.between?(2, 10)

Something like this?
do_something if (3..9) === x
or
r = 3..9
if r === x
. . .

Related

Construct a dynamic route path in Ruby

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.

RSPEC get ExampleGroup for binding ivars

This one is going to be a tinge interesting. After noting the peculiar behavior of let, I've decided to try and make a direct equivalent of the Lisp Let for RSPEC by binding instance variables to example groups.
Here's the code for this so far:
module RSpec
module Core
module MemoizedHelpers
module ClassMethods
def let_local(name, &block)
raise "#let_local called without a block" if block.nil?
# Binding to string instances, fix
current_example = caller[0]
# Attempt to find ivar, if not, make it.
meth = -> {
current_example.instance_variable_get("##{name}") ||
current_example.instance_variable_set("##{name}", block.call)
}
MemoizedHelpers.module_for(self).send(:define_method, name, meth)
before(:all) { __send__(name) }
end
end
end
end
end
Problem being, while it technically works for nested examples, I'm throwing ivars on a string. I know why it currently works but man is that hackish... How can I get a hold of the current example group that that function would be run inside? (ie
This is more of a thought exercise to see if it can be done.
There are definite performance reasons for something like this though, when used correctly (and frozen.) The use case is if you write tests in a functional manner, this let_local will not get in the way of running tests in parallel like the original let, and will not try and rebuild the object repeatedly (think expensive instantiations.)
Granted that this can already be done with a before :all ivar, but this may be a cleaner way about it.
Example test code using it:
describe 'Single local, multiple nested example, same local name' do
let_local(:a) { Person.new('Doctor', 900) }
it 'will be 900' do
expect(a.age).to eq(900)
end
it 'will be named Doctor' do
expect(a.name).to eq('Doctor')
end
context 'Doc got old' do
let_local(:a) { Person.new('Doctor', 1000) }
it 'should now be 1000' do
expect(a.age).to eq(1000)
end
context 'And older still!' do
let_local(:a) { Person.new('Doctor', 1100) }
it 'will now be 1100' do
expect(a.age).to eq(1100)
end
end
it 'will still be 1000' do
expect(a.age).to eq(1000)
end
end
it 'will still be 900' do
expect(a.age).to eq(900)
end
end
The overall intent is to emulate this type of behavior in Lisp:
(let ((x 1))
(write-line (write-to-string x)) ; prints 1
(let ((x 2))
(write-line (write-to-string x))) ; prints 2
(write-line (write-to-string x))) ; prints 1
Any tips or ideas?
You can already emulate that behavior using standard Ruby. For example,
def let(pr)
pr.call
end
let->(x=1, y=2) {
p [x, y]
let->(x=3, y=4) {
p [x, y]
}
p [x, y]
}
Output:
[1, 2]
[3, 4]
[1, 2]

Object#tap question

Why does
a = [].tap do |x|
x << 1
end
puts "a: #{a}"
work as expected
a: [1]
but
b = [].tap do |x|
x = [1]
end
puts "b: #{b}"
doesn't
b: []
?
The reason why the second snippet does not change the array is the same why this snippet:
def foo(x)
x = [1]
end
a = []
foo(a)
does not change variable a. Variable x in your code is local to the scope of the block, and because of that you can assign anything to it, but the assignment won't be visible outside (Ruby is a pass-by-value language).
Of course, blocks have also closures on the local variables where they were declared, so this will work:
def foo(x)
yield(x)
end
b = []
foo(123) do |x|
b = [1]
end
p b # outputs [1]
The first method put 1 on the end of an empty array. In the same way you cant say that an empty array is equal to 1. Rather you would try and replicate it...
b = [].tap do |x|
x.unshift(1)
end
This is just an example yet have a look at the method call you can use on an Array by typing.
Array.methods.sort
All the best and Good luck
This is slightly unrelated -- but that [].tap idiom is horrible. You should not use it. Even many of the people who used it in rails code now admit it's horrible and no longer use it.
Do not use it.

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 }

Inserting new item to instance variable in Ruby on Rails

#xs stores urls like www.yahoo.com, www.google.com
for x in #xs
y = x... #do something with x
#result += y #i want to do something like that. i want to store them in #result. What do i have to write in here?
end
Sorry for noob question. By the way how do you call #result ? Is it an instance variable or an array ?
You need to initialize #result first.
#result = []
for x in #xs
y = x...
#result << y
end
You should either do this:
#result << y
or this:
#result += [y]
The + operator expects two arrays, the << operator appends an object onto an array.
From what I can make out from the question, you want to mutate the contents of the already existing array
#mutated_xs = #xs.collect do |x|
y = x.do_something # some code for to do something to x returning y
x += y # mutate existing x here
end
puts #mutated_xs.inspect
If you want to take every element in an array and change it, the idiomatic Ruby way is to use map or collect:
#new_urls = #urls.map do |url|
# change url to its new value here
end
You don't need to manually assign it to #new_urls, just write a statement that returns the desired value, like url.upcase or whatever you want to do.

Resources