rails hash of date functions - ruby-on-rails

I have some reocure's model, that has frequencies and should create several records on it's own creation.
I'm trying to convert the integer frequency (1,7,30) to a function of (days, weeks, months) Respectively, so I can add this to the new records.
I tried doing it by using a hash of date functions, in order to use it like date+i.months():
date_hash={1=>days(), 7=>weeks(), 30=>months()}
but I'm getting an error.
It should be used inside of a loop:
some_number.times do |i|
Record.create({...., :date => start_date+i.(date_hash[frequency]),....})
end
I'm getting this error:
undefined method `days' for # <MyController:0x111111>
Thanks for your assist.

You have a lot of not-Ruby here. Ruby doesn't provide first-class functions like you seem to expect (date() would assign the value of that key to the result of the invocation of some function named date available in the local scope), and you can't call a variable method name like that. In Ruby, method calls on objects (receivers) are messages sent to the receiver, with optional arguments. The typical way you would call something like this is by using Object#send with the desired method name:
methods = {1 => :days, 7 => :weeks, 30 => :months}
x.send(methods[1]) # Calls x#days
However, the intent of your code is rather unclear, and there is likely a much better-factored way of doing what you want.

Related

FactoryGirl - create_list increments parameter for each object

I'm trying to use RSpec's create_list in a way that, for a specific column, each object receives the value of the previous object + 1.
If Rails accepted ++ notation, it would be something like this:
create_list :entity, 10, priority: priority++
I tried using sequence, but the problem is the sequence never resets. I need priority to always start from 1.
How could I achieve that?
Thanks in advance.
I do not think this is possible with create_list. Due to the way method calling works in Ruby, the arguments you pass to create_list are evaluated immediately and are not re-evaluated for each new Entity that is being created.
How about this instead?
(1..10).map do |priority|
create :entity, priority: priority
end

How do I mass update a field for models in an array?

I’m using Rails 4.2.7. How do I mass update a field of an array of my models without actually saving that information to the database? I tried
my_objcts_arr.update_all(my_object: my_object)
but this results in the error
NoMethodError: undefined method `update_all' for #<Array:0x007f80a81cae50>
I realize I could iteraet over the array and update each object individually, but I figure there's a slicker, one-line way in Ruby taht I'm not aware of.
update_all needs to be called on a class level active record model/relation, ie User or TaxReturn. Here is one somewhat related SO post showing some examples, and here is the api doc for update_all. It will send the UPDATE directly to the db (it is an active record method, after all), so it is not what you want.
You're best off iterating and updating the value yourself with collect or something similar, which is only one line.
foo = [{:a=>"a", :b=>"b"}, {:a=>"A", :b=>"B:}]
// => [{:a=>"a", :b=>"b"}, {:a=>"A", :b=>"B"}]
foo.collect{|x| x[:a]="C"}
// => ["C", "C"]
foo
// => [{:a=>"C", :b=>"b"}, {:a=>"C", :b=>"B"}]

Some questions about symbol and instance method in Ruby

I'm currently try to learn ruby on 'Learn Ruby The Hard Way'
Here's my question...
The following code are from exercise 40:
cities = {'CA'=> 'San Francisco', 'MI'=> 'Detroit', 'FL'=> 'Jacksonville'}
cities['NY'] = 'New York'
cities['OR'] = 'Portland'
def find_city(map, state)
if map.include? state
return map[state]
else
return 'Not found.'
end
end
cities[:find] = method(:find_city)
while true
print 'State? (ENTER to quit) '
state = gets.chomp
break if state.empty?
puts cities[:find].call(cities, state)
end
I played around with the code, and finally understand how it works.
But I still don't understand about two things:
first...
In about middle of the code,
it defined a variable
cities[:find] = method(:find_city)
As what I know for now, the :(colon) declare a symbol.
I want to know is it a better practice to name a variable as cities[:find]
instead of using cities_find in this case?
I'm not quite sure what's the differences, or maybe it's much readable for most rubyist?
And the second one is also about the same line.
method(:find_city)
I know it allows me to call the find_city method.
But again, why I have to put a colon before find_city?
Does this code means parse the arguments I put in to symbols?
I have to say that Learn Ruby The Hard Way gives us a really Really REALLY GOOD example of what we should NOT do. No rubyist will ever type such code in his/her projects. This piece of code is confusing, unreadable and is an abuse of metaprogramming.
Anyway, I dissect that code for you.
The confusing part starts with this line:
cities[:find] = method(:find_city)
Let's look at the right side of the =. It calls a method whose name is method, as you may guess, the return value of the method call is the method find_city, more precisely, a Method object that wraps the method find_city with its scope. Then that method is stored in the hash cities, with a symbol :find as the key. So the value of cities now become
{
'CA'=> 'San Francisco',
'MI'=> 'Detroit',
'FL'=> 'Jacksonville',
'NY' => 'New York',
'OR' => 'Portland',
:find => #<Method:main.find_city>
}
You can see that the last key-value pair is really really weird, and it shouldn't be there because the hash cities should only store states and their capitals. Heck!
Then here comes this even weirder expression cities[:find].call(cities, state). Let's see how this work.
cities[:find] simply retrieve the Method object from the hash (still remember what method it wraps?)
cities[:find].call(cities, state) invokes the method it wraps, which is find_city, in the scope that the Method object wraps, which is the top level object (a.k.a. main), Method#call returns whatever the method wrapped in returns. So this expression is just find_city(cities, state), written in an alien style.
cities[:find] = method(:find_city)
Here, cities is a hash and the method object returned by method(:find_city) is assigned to the hash key find which is a symbol.
I think it depends upon you and the context of program where you are writing this.
A simple method_var = method(:find_city) would work here as well.
method(:find_city)
I know it allows me to call the find_city method. But again, why I have to put a colon before find_city? Does this code means parse the arguments I put in to symbols?
Here, you are passing the method name as an argument, you have to either pass it as a symbol or string.
In Ruby, the method method creates a Method Object. This allow you to pass it around in your code and call it later using the .call method on your Method object.
Since calling method(my_method) would evaluate my_method and pass the result to method(...), you need a way to tell the method method which method to use. That's why you basically pass in the method name as a Symbol into the method method :D
So it actually defined a proc, and make :find and find_city sort like key and value...Probably...

How can you test if a certain value is in an instance variable in Ruby on Rails?

I need to test if an instance variable in my controller contains a specific value. I think .include? would be the way to do it but that doesn't seem to work.
My code looks something like this:
#names=Model.find_by_sql("select name from ...")
if #names.include?(params[:name])
...
end
The if statement somehow allways evaluates to true.
Thanks
Firstly, find_by_sql is not a good way to do. find_by_sql will return you an object of class Model. Whereas params[:name] is most likely a string. The following should work:
Model.find(:all, :conditions => 'specify conditions here').map(&:name).include?(params[:name])
The results of find_by_sql will be (from the docs):
an array with columns requested encapsulated as attributes of the model you call this method from.
You need to search within the results.

List all methods that an object respond_to?

I have two models,
User
Membership
The two have the following relationship with one another
user has_many :memberships
I've been trying to figure out where the build method resides, and how do i get it in a list of methods for the instance. Here is the output of the debugger that shows my delima
(rdb:63) #user.memberships.respond_to?"build"
true
While the following is returning false, shouldnt it return true??
(rdb:63) #user.memberships.instance_methods.include?"build"
false
One point is that instance_methods takes an optional boolean parameter, indicating whether you want to see methods of the instances ancestors. In your case I think you want instance_methods(true).
However, it appears that "build" is an autogenerated method, according to the documentation. Typically the autogenerated methods in ActiveRecord are implemented by overriding method_missing and handling calls to "methods" that don't actually exist. responds_to is also overridden so that the class will indicate that it responds to the correct calls. However, since those "methods" aren't actually defined, they won't show up in the instance_methods list.
Since the list of commands that a class can respond_to using method_missing is essentially infinite, I'm pretty sure there's no way to get the list. For example, an ActiveRecord model that has attributes a,b,c, and d will automatically respond to calls such as find_by_a_and_b and find_by_a_b_and_c and find_by_b_and_d and so forth, ad infinitum. There's no way to get a list of all of those possibilities.
Please note that instance_methods returns an array of String or Symbol depending on the Ruby version.
Ruby 1.8 returns an Array of String, Ruby 1.9 an Array of Symbol.
In Ruby 1.8
"".respond_to?(:upcase)
# => true
"".class.instance_methods.include?("upcase")
# => false
"".class.instance_methods.include?(:upcase)
# => false
In Ruby 1.9
"".respond_to?(:upcase)
# => true
"".class.instance_methods.include?("upcase")
# => false
"".class.instance_methods.include?(:upcase)
# => true
Also, instance_methods must be called on the class, not on the instance.
You could try:
#user = User.first
#user.methods.grep /method_name/
However, I don't think you'll see 'build' or 'create' in a list. Most likely these methods are generated dynamically

Resources