I'm trying to validate redemption codes
#redemption_codes = Account.where(:redeemed == false).map(&:redemption_code)
validates :code, inclusion: { in: #redemption_codes }
before_create :remove_code
def remove_code
#redeemed = Account.where(:redeemed == true).map(&:redemption_code)
#redemption_codes.delete_if{|code|#redeemed.include?(code)}
end
If a code has already been redeemed, I want to remove it from the array, so it's no longer included.
Here I get
Undefined method delete_if for nil:NilClass
The value of #redemption_codes is nil
If I type in a value that is not included in the array, I get an error flash message.
When I type in a value included in the array, it works. In other words, it works if I comment out the remove_code method.
Problem is #redemption_codes is set in stone. If a value is updated from :redeemed = false to :redeemed = true, it won't disappear from the array.
That's why I tried to create a custom validation message, but it didn't work.
Help is appreciated.
That is the incorrect syntax.
Account.where(:redeemed == false)
should be
Account.where(redeemed: false)
You should try something like this
def validate_code
#redemption_codes = Account.where(redeemed: false).map(&:redemption_code)
errors[:base] << "Invalid Redemption Code" unless #redemption_codes.include?(self.code)
end
You could also use the uniqueness validator
validates :code, inclusion: { in: #redemption_codes }, uniqueness: true
This ensures that a redeemed account will not be valid
Related
Can't figure this out.. Not sure why the record isn't being saved.. the method is being called properly, and all the fields are present, and the logic is correct.. Here is my model code:
class Mine < ActiveRecord::Base
belongs_to :shop
validates :merchant_id, presence: true
validates :auth_token, presence: true
before_save :assign_three_speed
private
def assign_three_speed
if CreateFulfillmentService::NON_US_MARKETPLACES.include?
(self.marketplace)
self.three_speed = false
else
self.three_speed = true
end
end
end
Well this is super crazy.. I put in some loggers and now it DOES save?? This is my code now:
def assign_three_speed
Rails.logger.info "What is self?? #{self.inspect}"
if CreateFulfillmentService::NON_US_MARKETPLACES.include?
(self.marketplace)
self.three_speed = false
else
self.three_speed = true
end
Rails.logger.info "Now what is self?? #{self.inspect}"
end
In versions of Rails prior to 5.0.0, returning false from a callback method will cancel the save. From the Rails 4.2.7 documentation:
If a before_* callback returns false, all the later callbacks and the
associated action are cancelled. Callbacks are generally run in the
order they are defined, with the exception of callbacks defined as
methods on the model, which are called last.
When setting self.three_speed = false, it is the last statement that is run in the method, so that false is the return value of the assign_three_speed method. That's why adding the logger to the last line fixed it. Have the method return some other value instead.
Return true as the last line if you never want to cancel the callback:
def assign_three_speed
if CreateFulfillmentService::NON_US_MARKETPLACES.include(self.marketplace)
self.three_speed = false
else
self.three_speed = true
end
true
end
In my model file, I am trying to make a conditional validation depending on a hidden form value. It seems like the :form_type_main? method never gets called, or just does not work. Want am I doing wrong?
attr_accessor(:form_type,:field1,:field2,:field3,:field4)
required_main = ["field1", "field2"]
required_second = ["field3", "field4"]
if :form_type_main?
required = required_main
else
required = required_second
end
required.each do |i|
validates_presence_of i
end
def form_type_main?
form_type == "main"
end
You may simply define the validations like this:
REQUIRED_MAIN = [:field1, :field2]
REQUIRED_SECOND = [:field3, :field4]
validates_presence_of *REQUIRED_MAIN, if: :form_type_main?
validates_presence_of *REQUIRED_SECOND, unless: :form_type_main?
def form_type_main?
form_type == "main"
end
Validation for uniqueness applies to a combination of two fields. My problem is that patching a new record that does validate, throws the error ActiveRecord::RecordNotUnique PG::UniqueViolation: ERROR: duplicate key value violates unique constraint, rather than that it executes the else part of the method below. Why does it throw an error instead of execute the else part? How to change this?
def create
first_node = Node.find_by(id: params[:first_node_id])
second_node = Node.find_by(id: params[:second_node_id])
link = first_node.where_first_links.build(create_params)
if link.save
render json: link, status: :created
else
render json: link, message: "unable", status: :bad_request
end
end
In the migration file:
add_index :links, [:first_node_id, :second_node_id], unique: true
The model validation:
before_save :order_nodes
validates :first_node_id, presence: true
validates :second_node_id, presence: true
validates :first_node_id, uniqueness: { scope: :second_node_id }
def order_nodes
if first_node_id > second_node_id
first = first_node_id
second = second_node_id
self.first_node_id = second
self.second_node_id = first
if direction == '0'
self.direction = 1
elsif direction == '1'
self.direction = 0
end
end
end
It most probably means that:
Your object passed the validation.
THEN your before_save callback reordered the fields
Subsequent attempt to save the record to the database violated the database unique constraint
Try changing your callback from:
before_save :order_nodes
to:
before_validation :order_nodes
NOTE: In this case, you'll have to assume that your fields may be invalid and rewrite your callback accordingly.
I'm wanting to validate that my height attribute is within a bunch of different ranges. So my attempt was something like what I did below... however this is incorrect. How should this be done? Thanks!
validates :height, :numericality => { in: { 5020..5028, 5030..5038, 5040..5048, 5050..5058, 5060..5068, 5070..5078, 5080..5088, 5090..5098, 5100..5108, 5110..5118,
6000..6008, 6010..6018, 6020..6028, 6030..6038, 6040..6048, 6050..6058, 6060..6068, 6070..6078, 6080..6088, 6090..6098, 6100..6108, 6110..6118,
7000..7008, 7010..7018, 7020..7028, 7030..7038, 7040..7048, 7050..7058, 7060..7068, 7070..7078, 7080..7088, 7090..7098, 7100..7108, 7110..7118 } }
You can put that in a custom validate method:
class YourModel < ActiveRecord::Base
VALID_HEIGHT_RANGES = [5020..5028, 5030..5038, 5040..5048, 5050..5058, 5060..5068, 5070..5078, 5080..5088, 5090..5098, 5100..5108, 5110..5118, 6000..6008, 6010..6018, 6020..6028, 6030..6038, 6040..6048, 6050..6058, 6060..6068, 6070..6078, 6080..6088, 6090..6098, 6100..6108, 6110..6118, 7000..7008, 7010..7018, 7020..7028, 7030..7038, 7040..7048, 7050..7058, 7060..7068, 7070..7078, 7080..7088, 7090..7098, 7100..7108, 7110..7118]
validate :height_in_valid_range
private
def height_in_valid_range
VALID_HEIGHT_RANGES.each do |range|
unless range.include? height
errors.add :height, "not in valid range"
break
end
end
end
end
I create a Subscription for a User via the method .create_subscription(plan_title).
That method checks that it's possible to subscribe (not oversubscribed plan or archived subscription) via the method .plan_subscribable?(plan).
This method does either return true or false, but I would like it to return an error message as well that could be passed to the user if false.
How and where do I implement these validation errors?
class User
def plan_subscribable?(plan)
users_subscribed = Subscription.where(plan_id: plan.id).size
return false unless users_subscribed <= plan.quantity
return false unless plan.archived == false
return true
end
def create_subscription(plan_title)
plan = Plan.where(title: plan_title).first
if plan_subscribable?(plan)
Subscription.create(...)
end
end
end
You could modify plan_subscribable? to return a boolean true or a string containing the specific error message:
def plan_subscribable?(plan)
return 'The number of users cannot be higher than the plan quantity' unless users_subscribed <= plan.quantity
return 'Archived plans are not subscribable' unless plan.archived == false
return true
end
Then, evaluate whether the returned value from plan_subscribable? is true. If it is not, the statement is implicitly false and you can use the returned value as the error message:
def create_subscription(plan_title)
plan = Plan.where(title: plan_title).first
subscribable_or_error = plan_subscribable?(plan)
if subscribable_or_error === true
Subscription.create(...)
else
error_message = subscribable_or_error
end
end