Is there a way to make a before_save conditional? - ruby-on-rails

I am trying to make a before_save in a rails app conditional, but it doesn't seem to be working.
before_save method_call_to_run if self.related_model.some_method_that_returns_t_or_f?
If the 'some_method_that_returns_t_or_f' returns true, I want it to run the method before it saves the object otherwise I just want it to ignore the before_save.

you can use :if
before_save do_something, :if => Proc.new {|model| model.some_boolean_attr_or_method }
or simply
before_save do_something, :if => some_condition
EDIT:
for a quick reference, there's an excellent guide about this:
http://guides.rubyonrails.org/active_record_callbacks.html#conditional-callbacks

Rails 5
I've had success defining a private method which contains the boolean logic and then passing it as a symbol (that last part seems like a requirement):
before_save do_something, if: :private_boolean_method?
I also recently found out you can simply pass a block (took me a while to figure out the syntax):
before_save do_something, if: -> { condition == "met" }

Related

ActiveRecord: perform a validation on method call only?

I'm trying to call validation on an ActiveRecord after a certain method in another Ruby file is called. Is there some way I can tie this into ActiveRecord's validation scheme, i.e.:
validate :cars_have_wheels?, on: after_cache_reset
Note: cars_have_wheels? is a method located in the ActiveRecord object, after_cache_reset is the method in the other file.
Thank you!
Check out this link (Ruby on Rails guides):
5 Conditional Validation
Sometimes it will make sense to validate an object only when a given predicate is satisfied. You can do that by using the :if and :unless options, which can take a symbol, a string, a Proc or an Array. You may use the :if option when you want to specify when the validation should happen. If you want to specify when the validation should not happen, then you may use the :unless option.
5.1 Using a Symbol with :if and :unless
You can associate the :if and :unless options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.
class Order < ActiveRecord::Base
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
end

Rails 3.2 After save callback only when a attribute is updated or added

I have an after save callback:
after_save :do_an_expensive_task_on_basis_of_flag_value
I later realized that this is a very expensive task and it does not make sense to call it when the value of flag is not set(on create) OR value of flag is not updated on update.
So one thing , i can do is to replace it with:
after_create :do_an_expensive_task_on_basis_of_flag_value , :if => Proc.new {|a| a.flag.present?}
after_update :do_an_expensive_task_on_basis_of_flag_value , :if => Proc.new {|a| a.flag_changed?}
But i am looking for a one liner, with a proc such that this task is called only when Flag is set on create and flag is updated on update with :after_save.
Note: This is not correct way:
after_save :do_an_expensive_task_on_basis_of_flag_value , :if => Proc.new {|a| a.flag.present? or a.flag_changed?}
As it still calls callback on update , even when flag is not changed.
a.flag_changed? works for both cases. But just for your knowledge, you could do it like this:
after_save :do_an_expensive_task_on_basis_of_flag_value , :if => Proc.new {|a| (new_record? and a.flag.present?) or a.flag_changed?}
The logic in your proc is slightly wrong. It will always run if the flag is present, regardless of if it has been changed on this save or not.
Surely a.flag_changed? is enough for the logic you need? If the flag is added, it will be changed from (its default value, probably nil) to (whatever you set it to).

not sure why a Proc would be used here - not getting something simple

I understand the concept of a proc but sometime I see code like this (take from rails guide for validation http://guides.rubyonrails.org/active_record_validations_callbacks.html#using-if-and-unless-with-a-proc):
class Order < ActiveRecord::Base
before_save :normalize_card_number,
:if => Proc.new { |order| order.paid_with_card? }
end
it seems this could be more simply written as:
class Order < ActiveRecord::Base
before_save :normalize_card_number, :if => :paid_with_card?
end
What am I not getting about the advantage of using a Proc here?
thx in advance
In simple cases they are equivalent, but procs allow far more versatility without requiring a method to be defined simply for a validation if check.
Imagine this:
before_save :nuke, :if => Proc.new { |model| !model.nuked? && model.nukable? && model.region.nukable? }
You can always write that check in an instance method and reference it with a symbol, but for cases where the specific logic is only in the :if, it is valid to keep it in a proc.
They are equivalent if the receiver of the method is the object being validated. This isn't exactly how the ActiveModel validators work, but the concept is similar:
Calling to_proc on a symbol :sym gives you the functional equivalent of ->(x){ x.sym } - the symbol is sent as a message to the argument of the proc. Calling to_proc on a proc simply returns itself, so you can pass either a symbol or proc into a method and guarantee a proc:
def return_a_proc(symbol_or_proc)
symbol_or_proc.to_proc
end
In cases where the model instance is not the receiver, e.g. the validation method takes the model as an argument, or as in Daniel Evans' example, you need to explicitly construct the proc to specify what should be done with the proc's argument.

Rails validation :if => Proc.new or lambda?

I have found that in all examples (include rails documentation) that I have seen for the :if option of validation methods uses Proc.new instead of lambda, for example
class Foo < ActiveRecord::Base
validates_presence_of :name, :if => Proc.new{|f| .... } # why not lambda here?
end
is there any reason for this?
As far as I know, lambda
Is more strict with arguments.
Also return statement in lambda block returns from the block, not from calling function.
Both seems to be desirable behavior for :if option mentioned above, is there anything I am missing?
Both seems to be desirable behavior for :if option mentioned above, is there anything I am missing?
I'm guessing that:
It's more desirable to allow Procs as they don't care about the number of arguments. So I could easily write any of the below:
validates_presence_of :name, :if => Proc.new{|f| f.display_name.blank? } # I care about 'f' here as I need it to check something.
... and:
validates_presence_of :secret_sauce, :if => Proc.new{ MyApp::REQUIRE_SECRET_SAUCE } # I don't care about any arguments being passed in.
This may seem like a minor thing, but I guess it adds to the flexibility.

Why use Proc.new to call a method in a Rails callback?

in all the tutorials for RoR I see instances where the coder chose to use Proc.new when seemingly it is both unnecessary and rather unattractive.
Example, here is a callback for placed in a model, one using Proc.new the other presumably doing the same thing:
class Order < ActiveRecord::Base
before_save :normalize_card_number,
:if => Proc.new { |order| order.paid_with_card? }
end
class Order < ActiveRecord::Base
before_save :normalize_card_number, :if => "paid_with_card?"
end
So what's the difference? Why use the Proc? Don't they both call the "paid_with_card?" method?
Thanks in advance
In the example above, using a symbol for the conditional method would probably be the best choice.
class Order < ActiveRecord::Base
before_save :normalize_card_number, :if => :paid_with_card?
end
The string option uses eval to evaluate the Ruby code in the string. So personally, I would prefer to use a symbol if calling a method or a Proc if writing a short inline condition.
Per the RailsGuides documentation:
Using a Proc object gives you the ability to write an inline condition instead of a separate method. This option is best suited for one-liners.
I think using a Proc might be better illustrated this way:
class Order < ActiveRecord::Base
before_save :normalize_card_number,
:if => Proc.new { |order| order.payment_type == "card" }
end
This would possibly eliminate the need for the paid_with_card? method.
I'd say it was the case that in older versions of Rails that's how we used to do things, and someone added the feature whereby you could pass a string to be evaluated as an instance method on the current model.
In simple scenarios it makes the older style redundant, but allows for the use of a Proc for more complex 'if' statements which would be impossible to achieve using a method on only the current instance.

Resources