FactoryGirl - create_list increments parameter for each object - ruby-on-rails

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

Related

Ruby on Rails method returning nothing

I'm new to ruby and just testing out how to define a method with two variable inputs from the user. Currently, it takes the values but it returns nothing.
Is there something I missing here?
puts "Tell me what you want to print."
word = gets.chomp
puts "How many times do you want it printed?"
number = gets.to_i
def print_x_times(word)
i = 0
while i < number
puts(word)
i += 1
end
end
A few nits here:
You've defined a new method, but you haven't invoked it. You're likely using something like IRB to interactively play around, but the fact remains that you've only defined that method.
You're going to confuse yourself if you use word and number as variables inside of that method, since they're not guaranteed to be the same as the ones you've defined outside of it.
If you want to pass two values to the method, then you have to specify two parameters:
def print_x_times(word, number)
# code
end
...then, you actually go about calling it with your variables.
print_x_times(word, number)
You should pass number as an argument to the function print_x_times as you do for word
moreover, this code defines a function but doesn't call it, you should call it too
You have two issues here...
You need to define the method to take two parameters, word and number.
Then you need to call that method with the two arguments you derived from user input.

rails hash of date functions

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.

What does absolute sign mean in Ruby?

For example
func(#param) do |f|
some code here
end
and
#param.each do |sth|
some code here
end
What does the absolute value sign do here? I don't understand these two pieces of code.
It's the local variable within the block, so for the line:
#param.each do |sth|
you're iterating over #param right, well each item in #param is referred to singularly as sth.
So if #param refers to an array containing the numbers
[1,3,5,4]
During the first iteration sth will be 1, then 3, then 5, then 4.
Same goes for :
func(#param) do |f|
except now the local variable is called f! You could call it anything you want, even |ihavenoideawhatimdoing|
It's a local variable, it is saying that for the block of code between do...end, the variable f is defined.
It is a parameter to a block. The block is the part of the code between the do and the end. That block of code can use f or sth, which in your examples would probably have been set by func or each.
A tutorial on Ruby blocks will probably be helpful.
Labmda calculus - more abstract, but it was the context in which I first saw these things.
It signifies instance variables. You often see it interchanged if people are using attr_* methods like attr_accessor, which makes #someattr and self.some_attr equivalent in instance methods.

How to chain try() and scoped to_s() in Rails?

In a Rails view, one can use try to output only if there is a value in the database, e.g
#model.try(:date)
And one can chain trys if, for example, the output is needed as a string
#model.try(:date).try(:to_s)
But what if I need to call a scoped format? I've tried
#model.try(:date).try(:to_s(:long))
#model.try(:date).try(:to_s).try(:long)
What is the correct syntax for this? And what is a good reference for more explanation?
Thanks
From the fine manual:
try(*a, &b)
[...]
try also accepts arguments and/or a block, for the method it is trying
Person.try(:find, 1)
So I think you want:
#model.try(:date).try(:to_s, :long)
This one won't work:
#model.try(:date).try(:to_s(:long))
because you're trying to access the :to_s symbol as a method (:to_s(:long)). This one won't work:
#model.try(:date).try(:to_s).try(:long)
because you're trying to call the long method on what to_s returns and you probably don't have a String#long method defined.
mu is too short's answer shows the correct usage for the try method with parameters:
#model.try(:date).try(:to_s, :long)
However, if you are using Ruby 2.3 or later, you should stop using try and give the safe navigation operator a try (no pun intended):
#model&.date&.to_s(:long)
The following answer is here for historical purposes – adding a rescue nil to the end of statements is considered bad practice, since it suppresses all exceptions:
For long chains that can fail, I'd rather use:
#model.date.to_s(:long) rescue nil
Instead of filling up my view with try(...) calls.
Also, try to use I18n.localize for date formatting, like this:
l #model.date, format: :long rescue nil
See:
http://rails-bestpractices.com/posts/42-use-i18n-localize-for-date-time-formating
In case you often use try chains without blocks, an option is to extend the Object class:
class Object
def try_chain(*args)
args.inject(self) do |result, method|
result.try(method)
end
end
end
And then simply use #model.try_chain(:date, :to_s)

Using the after_save callback to modify the same object without triggering the callback again (recursion)

If I add an after_save callback to an ActiveRecord model, and on that callback I use update_attribute to change the object, the callback is called again, and so a 'stack overflow' occurs (hehe, couldn't resist).
Is it possible to avoid this behavior, maybe disabling the callback during it's execution? Or is there another approach?
Thanks!
One workaround is to set a variable in the class, and check its value in the after_save.
Check it first. (if var)
Assign it to a 'false' value before calling update_attribute.
call update_attribute.
Assign it to a 'true' value.
end
This way, it'll only attempt to save twice. This will likely hit your database twice, which may or may not be desirable.
I have a vague feeling that there's something built in, but this is a fairly foolproof way to prevent a specific point of recursion in just about any application.
I would also recommend looking at the code again, as it's likely that whatever you're doing in the after_save should be done in before_save. There are times that this isn't true, but they're fairly rare.
Could you use the before_save callback instead?
I didn't see this answer, so I thought I'd add it in case it helps anyone searching on this topic. (ScottD's without_callbacks suggestion is close.)
ActiveRecord provides update_without_callbacks for this situation, but it is a private method. Use send to get access to it anyway. Being inside a callback for the object you are saving is exactly the reason to use this.
Also there is another SO thread here that covers this pretty well:
How can I avoid running ActiveRecord callbacks?
Also you can look at the plugin Without_callbacks. It adds a method to AR that lets you skip certain call backs for a given block.
Example:
def your_after_save_func
YourModel.without_callbacks(:your_after_save_func) do
Your updates/changes
end
end
Check out how update_attribute is implemented. Use the send method instead:
send(name.to_s + '=', value)
If you use before_save, you can modify any additional parameters before the save is completed, meaning you won't have to explicitly call save.
This code doesn't even attempt to address threading or concurrency issues, much like Rails proper. If you need that feature, take heed!
Basically, the idea is to keep a count at what level of recursive calls of "save" you are, and only allow after_save when you are exiting the topmost level. You'll want to add in exception handling, too.
def before_save
#attempted_save_level ||= 0
#attempted_save_level += 1
end
def after_save
if (#attempted_save_level == 1)
#fill in logic here
save #fires before_save, incrementing save_level to 2, then after_save, which returns without taking action
#fill in logic here
end
#attempted_save_level -= 1 # reset the "prevent infinite recursion" flag
end
Thanks guys, the problem is that I update other objects too (siblings if you will)... forgot to mention that part...
So before_save is out of the question, because if the save fails all the modifications to the other objects would have to be reverted and that could get messy :)
The trick is just to use #update_column:
Validations are skipped.
Callbacks are skipped.
updated_at/updated_on are not updated.
Additionally, it simply issues a single quick update query to the db.
http://apidock.com/rails/ActiveRecord/Persistence/update_columns
I had this problem too. I need to save an attribute which depends upon the object id. I solved it by using conditional invocation for the callback ...
Class Foo << ActiveRecord::Base
after_save :init_bar_attr, :if => "bar_attr.nil?" # just make sure this is false after the callback runs
def init_bar_attr
self.bar_attr = "my id is: #{self.id}"
# careful now, let's save only if we're sure the triggering condition will fail
self.save if bar_attr
end
Sometimes this is because of not specifying attr_accessible in models. When update_attribute wants to edit the attributes, if finds out they are not accessible and create new objects instead.On saving the new objects, it will get into an unending loop.
I had a need to gsub the path names in a block of text when its record was copied to a different context:
attr_accessor :original_public_path
after_save :replace_public_path, :if => :original_public_path
private
def replace_public_path
self.overview = overview.gsub(original_public_path, public_path)
self.original_public_path = nil
save
end
The key to stop the recursion was to assign the value from the attribute and then set the attribute to nil so that the :if condition isn't met on the subsequent save.
You can use after_save in association with if as follows:
after_save :after_save_callback, if: Proc.new {
//your logic when to call the callback
}
or
after_save :after_save_callback, if: :call_if_condition
def call_if_condition
//condition for when to call the :after_save_callback method
end
call_if_condition is a method. Define the scenario when to call the after_save_callback in that method

Resources