Rails with_options :if => custom_method(parameterOne, parameterTwo) - ruby-on-rails

I was wondering if it's possible to pass in a parameter to methods when you're using the :if option with the 'with_options' method.
For example, can I call something like this?
with_options :if => :is_user_this_level?(threshold_level) do |some_object|
some_object.validates_with ObjectValidator
end
I'm wondering if it's possible to pass in the variable 'threshold_level' to the :if option method. Basically I want to do this because I don't want to have methods like :is_user_level_two?, :is_user_level_three? and so on and so forth.
Also if there's a RAILS way of doing this and I'm barking up the wrong tree please let me know.
Thanks!

The usual :if option for ActiveRecord callbacks can take a lambda as its argument so maybe this will work:
with_options :if => ->(o) { o.is_user_this_level? threshold_level } do |some_object|
some_object.validates_with ObjectValidator
end

Related

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.

Specifying two conditions with :if

I have a model which validates presence of an attribute if a check box is checked in the view.
The code is something like this:
validates_presence_of :shipping_first_name, :if => :receive_by_email_is_unchecked
I am looking to have another condition of this validation.So how do I go about doing this ??
My assumption is that something like this would do:
validates_presence_of :shipping_first_name, :if => {:receive_by_email_is_unchecked,:form_first_step_validation}
I am not sure if this is the write way of doing it or not ??
Any suggestions would be appreciated.
You can pass method names in an array:
validates_presence_of :shipping_first_name, :if => [:receive_by_email_is_unchecked, :form_first_step_validation]
Alternatively you can use proc if you don't want to define separate methods just for conditioning validations:
validates_presence_of :shipping_first_name, :if => proc { !receive_by_email? && form_first_step_validation }
I don't think that will work, but have a look at the source code for validates_presence_of https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations/presence.rb
You can build your own validator to do exactly that
Ryan Bates covered this in one of his first Rails casts
http://railscasts.com/episodes/41-conditional-validations
It's still valid although syntax may be slightly different for Rails v 3 +
I assume you are working on a Rails 2.x app as the syntax you use is not Rails 3 syntax
Rails 3.x syntax would be
validates :field_1, :field_2, :presence_of => true, :if => # Use a proc, or an array of conditions here. see the valid examples and comments that you have already received for this question from #jimworm and #MichaƂ Szajbe

Is there a way to make a before_save conditional?

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" }

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