How to trigger Rails ActiveRecord Validations manually ? (ActiveAdmin) - ruby-on-rails

I'm currently working on a project using Rails 5.2.6. (that's a pretty big project, and i wont be able to update rails version)
We're using ActiveAdmin to handle the admin part, and I have a model in which i save a logo using ActiveStorage.
Recently, i needed to perform validations over that logo properties. (File format, Size and ratio). For that matter, I've been searching through multiple solutions, including the ActiveStorageValidations gem.
This one provided me half of a solution because the validators worked as expected, but the logo would be stored and associated to the model even when the validators would fail.
(I'm redirected to the edit form with the different error displayed on over the logo field, but still the logo would be updated). This is apparently a known issue, from ActiveStorage that is supposedly fixed in Rails 6, but I'm not able to update the project. (And ActiveStorageValidations does not want to do anything about it as well according to an issue i found on the GitHub)
In the end I managed to make a working solution "by hand", using some before_actions that would make the required checks on the image, and render the edit form again before anything could happen if some of the checks fail.
I'm also adding some errors to my model in the process so that when the edit view from active admin is rendered, the errors display correctly on top of the form and the logo field.
Here is the code behind (admin/mymodel.rb)
controller do
before_action :prevent_save_if_invalid_logo, only: [:create, :update]
private
# Active Storage Validations display error messages but still attaches the file and persist the model
# That's a known issue, which is solved in Rails 6
# This is a workaround to make it work for our use case
def prevent_save_if_invalid_logo
return unless params[:my_model][:logo]
file = params[:my_model][:logo]
return if valid_logo_file_format(file) && valid_logo_aspect_ratio(file) && valid_logo_file_size(file)
if #my_model.errors.any?
render 'edit'
end
end
def valid_logo_aspect_ratio(file)
width, height = IO.read(file.tempfile.path)[0x10..0x18].unpack('NN')
valid = (2.to_f / 1).round(3) == (width.to_f / height).round(3) ? true : false
#my_model.errors[:logo] << "Aspect ratio must be 2 x 1" unless valid
valid
end
def valid_logo_file_size(file)
valid = File.size(file.tempfile) < 200.kilobytes ? true : false
#my_model.errors[:logo] << "File size must be < 200 kb" unless valid
valid
end
def valid_logo_file_format(file)
content_type = file.present? && file.content_type
#my_model.errors[:logo] << "File must be a PNG" unless content_type
content_type == "image/png" ? true : content_type
end
end
This works pretty well, but now my problem is that if any other error occurs on the form at the same time than a logo error (mandatory fields or other stuff), then it's not getting validated, and the errors are not displayed as this renders the edit view before other validations can occur.
My question is, do i have any mean to manually trigger the validations on my model at this level, so that every other field is getting validated and #my_model.errors is populated with the right errors, resulting in the form being able to display every form errors wether logo is involved or not.
Like so :
...
def prevent_save_if_invalid_logo
return unless params[:my_model][:logo]
file = params[:my_model][:logo]
return if valid_logo_file_format(file) && valid_logo_aspect_ratio(file) && valid_logo_file_size(file)
if #my_model.errors.any?
# VALIDATE WHOLE FORM SO OTHER ERRORS ARE CHECKED
render 'edit'
end
end
...
If anybody has an idea about how to do this, or a clue on how to do things in a better way, any clue would be much appreciated!

Approach 1:
Instead of explicitly rendering 'edit' which halts the default ActiveAdmin flow you could simply remove the file form parameters and let things go the standard way:
before_action :prevent_logo_assignment_if_invalid, only: [:create, :update]
def prevent_logo_assignment_if_invalid
return unless params[:my_model][:logo]
file = params[:my_model][:logo]
return if valid_logo_file_format?(file) && valid_logo_aspect_ratio?(file) && valid_logo_file_size?(file)
params[:my_model][:logo] = nil
# or params[:my_model].delete(:logo)
end
Approach 2:
The idea is the same, but you can also do it on model level.
You can prevent file assignment by overriding the ActiveStorage's setter method:
class MyModel < ApplicationModel
def logo=(file)
return unless valid_logo?(file)
super
end
By the way, your valid_logo_file_format method will always return true.

Related

Rails permit some_params differs depending on user

In a Rails project I have a model with a "status" column/property
And different users can set different status values.
For example
not logged in you can create new record, and the status should always be set to 1
when logged in as user_type1 you can set the status to 2
when logged in as user_type2 you are allowed to update other attributes of the record, but not the status.
Currently I handle this by having multiple update methods, and different record_params permitting different update attribute lists. But I wonder if this is the right way. Having multiple update methods in the controller is a bit strange. But also permitting all the params all the time, and checking them within the method, I don't like either. How do you handle such situation
I think you can change the permitted params based on the type of the user somthing like the below,
def update_params
params.require(:record).permit(whitelisted_params)
end
def whitelisted_params
if %w[site_visitor user_type1].include?(user.user_type)
[:status]
elsif user.user_type == 'user_type2'
[:permitted_column_1, :permitted_column_2, :permitted_column_3]
end
end
And you said as the site visitor can always set the status as 1 means,
1 --> Merge the status value in the permitted params.
record_params.merge(status: 1)
2 --> Validating the status in the before_action
Calling this validator in the before_action and respond with error response.
before_action: validate_record_update
def validate_record_update
if %w[site_visitor user_type1].include?(user.user_type)
valid_status = params[:user][:status] == 1
elsif user.user_type == 'user_type2'
valid_status = params[:user][:status] == 2
end
unless valid_status
# Error response
end
end
3 --> Model level validator
If your application is getting large enough that you're dealing with this problem more often, it may be time to implement a Policy pattern to get more maintainable control of authorization in general, not just strong params.
Take a look through the documentation for the pundit gem, which makes policy implementation quite easy and allows you to maintain logic for different parameter permissioning: https://github.com/varvet/pundit#strong-parameters
The link has some great examples for implementing a solution to exactly this situation!

Before save validation not working properly

I have a concern that checks addresses and zip codes with the intention of returning an error if the zip code does not match the state that is inputed. I also don't want the zip code to save unless the problem gets fixed.
The problem that I am having is that it appears that the if I submit the form, the error message in create pops up and I am not able to go through to the next page, but then somehow the default zip code is still saved. This only happens on edit. The validations are working on new.
I don't know if I need to share my controller, if I do let me know and I certainly will.
In my model I just have a
include StateMatchesZipCodeConcern
before_save :verify_zip_matches_state
Here is my concern
module StateMatchesZipCodeConcern
extend ActiveSupport::Concern
def verify_zip_matches_state
return unless zip.present? && state.present?
state_search_result = query_zip_code
unless state_search_result.nil?
return if state_search_result.upcase == state.upcase
return if validate_against_multi_state_zip_codes
end
errors[:base] << "Please verify the address you've submitted. The postal code #{zip.upcase} is not valid for the state of #{state.upcase}"
false
end
private
def query_zip_code
tries ||= 3
Geocoder.search(zip).map(&:state_code).keep_if { |x| Address::STATES.values.include?(x) }.first
rescue Geocoder::OverQueryLimitError, Timeout::Error
retry unless (tries -= 1).zero?
end
def validate_against_multi_state_zip_codes
::Address::MULTI_STATE_ZIP_CODES[zip].try(:include?, state)
end
end

Rails model's to_param sometimes not calling

I working on project using Rails 4.1.6 now. And I have strange problem. Method to_param for my models (Product, Category) sometimes not calling. I use it for SEO-friendly urls.
Here is my to_param method for Category model:
def to_param
puts 'I am in Category to_param'
"#{id}-#{title.to_slug.normalize.to_s}"
end
I use puts for find out is this method working or no. So, when my urls looks good (/categories/39-средства-дезинфекции) I can see the string 'I am in Category to_param' on my server console. This is correct case and all it's great.
But sometimes I have urls like /categories/39 for the same objects. When I look into console for this case, I don't see any prints from my to_param method form Category model.
These two cases I have on the same pages, same views and using the same helpers for category url (category_path).
Most complicated for this situation is that I can't reproduce this bug and don't see any regularity. For the same objects I have correct urls most of times, but sometimes it's not. If I restart rails server and refresh browser with clear cache – problem may out and urls will be correct again.
During my debug and research I found source code for base class. But I can't see there any reasons for the situation described above.
def to_param(method_name = nil)
if method_name.nil?
super()
else
define_method :to_param do
if (default = super()) &&
(result = send(method_name).to_s).present? &&
(param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
"#{default}-#{param}"
else
default
end
end
end
end
Also I can tell that this problem was appear, when I used FriendlyID before, using regex for clear and build slugs, and now for babosa gem. So, I think the problem is my to_param sometimes not calling for my model.
So, I found the reason of this behaviour. Now it's resolved!
The reason was I have redefined to_param for Product and Category in my ActiveAdmin files:
before_filter do
Product.class_eval do
def to_param
puts "I am in admin Product to_param"
id.to_s
end
end
Category.class_eval do
def to_param
puts "I am in admin Category to_param"
id.to_s
end
end
end
So, when I was log in Admin panel and go to Product page – "bug" will appear on front-end views.
So, I need to remove Product.class_eval and Category.class_eval blocks from my admin classes.

Can a mobile mime type fall back to "html" in Rails?

I'm using this code (taken from here) in ApplicationController to detect iPhone, iPod Touch and iPad requests:
before_filter :detect_mobile_request, :detect_tablet_request
protected
def detect_mobile_request
request.format = :mobile if mobile_request?
end
def mobile_request?
#request.subdomains.first == 'm'
request.user_agent =~ /iPhone/ || request.user_agent =~ /iPod/
end
def detect_tablet_request
request.format = :tablet if tablet_request?
end
def tablet_request?
#request.subdomains.first == 't'
request.user_agent =~ /iPad/
end
This allows me to have templates like show.html.erb, show.mobile.erb, and show.tablet.erb, which is great, but there's a problem: It seems I must define every template for each mime type. For example, requesting the "show" action from an iPhone without defining show.mobile.erb will throw an error even if show.html.erb is defined. If a mobile or tablet template is missing, I'd like to simply fall back on the html one. It doesn't seem too far fetched since "mobile" is defined as an alias to "text/html" in mime_types.rb.
So, a few questions:
Am I doing this wrong? Or, is there a better way to do this?
If not, can I get the mobile and tablet mime types to fall back on html if a mobile or tablet file is not present?
If it matters, I'm using Rails 3.0.1. Thanks in advance for any pointers.
EDIT: Something I forgot to mention: I'll eventually be moving to separate sub-domains (as you can see commented out in my example) so the template loading really needs to happen automatically regardless of which before_filter has run.
Possible Duplicate of Changing view formats in rails 3.1 (delivering mobile html formats, fallback on normal html)
However, I struggled with this exact same problem and came up with a fairly elegant solution that met my needs perfectly. Here is my answer from the other post.
I think I've found the best way to do this. I was attempting the same thing that you were, but then I remembered that in rails 3.1 introduced template inheritance, which is exactly what we need for something like this to work. I really can't take much credit for this implementation as its all laid out there in that railscasts link by Ryan Bates.
So this is basically how it goes.
Create a subdirectory in app/views. I labeled mine mobile.
Nest all view templates you want to override in the same structure format that they would be in the views directory. views/posts/index.html.erb -> views/mobile/posts/index.html.erb
Create a before_filter in your Application_Controller and do something to this effect.
before_filter :prep_mobile
def is_mobile?
request.user_agent =~ /Mobile|webOS|iPhone/
end
def prep_mobile
prepend_view_path "app/views/mobile" if is_mobile?
end
Once thats done, your files will default to the mobile views if they are on a mobile device and fallback to the regular templates if a mobile one is not present.
You need to do several things to wire this up, but the good news is that Rails 3 actually makes this a lot simpler than it used to be, and you can let the router do most of the hard work for you.
First off, you need to make a special route that sets up the correct mime type for you:
# In routes.rb:
resources :things, :user_agent => /iPhone/, :format => :iphone
resources :things
Now you have things accessed by an iphone user agent being marked with the iphone mime type. Rails will explode at you for a missing mime type though, so head over to config/initializers/mime_types.rb and uncomment the iphone one:
Mime::Type.register_alias "text/html", :iphone
Now you're mime type is ready for use, but your controller probably doesn't yet know about your new mime type, and as such you'll see 406 responses. To solve this, just add a mime-type allowance at the top of the controller, using repsond_to:
class ThingsController < ApplicationController
respond_to :html, :xml, :iphone
Now you can just use respond_to blocks or respond_with as normal.
There currently is no API to easily perform the automatic fallback other than the monkeypatch or non-mime template approaches already discussed. You might be able to wire up an override more cleanly using a specialized responder class.
Other recommended reading includes:
https://github.com/plataformatec/responders
http://www.railsdispatch.com/posts/rails-3-makes-life-better
Trying removing the .html from the .html.erb and both iPhone and browser will fallback to the common file.
I have added a new answer for version 3.2.X. This answer is valid for <~ 3.0.1.
I came to this question while looking to be able to have multiple fallbacks on the view. For example if my product can be white-labeled and in turn if my white-label partner is able to sell sponsorship, then I need a cascade of views on every page like this:
Sponsor View: .sponsor_html
Partner View: .partner_html
Default View: .html
The answer by Joe, of just removing .html works (really well) if you only have one level above the default, but in actual application I needed 5 levels in some cases.
There did not seem to be anyway to implement this short of some monkey patching in the same vein as Jeremy.
The Rails core makes some fairly wide ranging assumptions that you only want one format and that it maps to a single extension (with the default of NO extension).
I needed a single solution that would work for all view elements -- layouts, templates, and partials.
Attempting to make this more along the lines of convention I came up with the following.
# app/config/initializers/resolver.rb
module ActionView
class Base
cattr_accessor :extension_fallbacks
##extension_fallbacks = nil
end
class PathResolver < Resolver
private
def find_templates_with_fallbacks(name, prefix, partial, details)
fallbacks = Rails.application.config.action_view.extension_fallbacks
format = details[:formats].first
unless fallbacks && fallbacks[format]
return find_templates_without_fallbacks(name, prefix, partial, details)
end
deets = details.dup
deets[:formats] = fallbacks[format]
path = build_path(name, prefix, partial, deets)
query(path, EXTENSION_ORDER.map {|ext| deets[ext] }, details[:formats])
end
alias_method_chain :find_templates, :fallbacks
end
end
# config/application.rb
config.after_initialize do
config.action_view.extension_fallbacks = {
html: [:sponsor_html, :partner_html, :html],
mobile: [:sponsor_mobile, :partner_mobile, :sponsor_html, :partner_html, :html]
}
# config/initializers/mime_types.rb
register_alias 'text/html', :mobile
# app/controllers/examples_controller.rb
class ExamplesController
respond_to :html, :mobile
def index
#examples = Examples.all
respond_with(#examples)
end
end
Note: I did see the comments around alias_method_chain, and initially did make a call to super at the appropriate spot. This actually called ActionView::Resolver#find_templates (which raises a NotImplemented exception) rather than the ActionView::PathResolver#find_templates in some cases. I wasn't patient enough to track down why. I suspect its because of being a private method.
Plus, Rails, at this time, does not report alias_method_chain as deprecated. Just that post does.
I do not like this answer as it involves some very brittle implementation around that find_templates call. In particular the assumption that you only have ONE format, but this is an assumption made all over the place in the template request.
After 4 days of trying to solve this and combing through the whole of the template request stack its the best I can come up with.
The way that I'm handling this is to simply skip_before_filter on those requests that I know I want to render the HTML views for. Obviously, that will work with partials.
If your site has a lot of mobile and/or tablet views, you probably want to set your filter in ApplicationController and skip them in subclasses, but if only a few actions have mobile specific views, you should only call the before filter on those actions/controllers you want.
If your OS has symlinks you could use those.
$ ln -s show.html.erb show.mobile.erb
I am adding another answer now that we have updated to 3.2.X. Leaving the old answer as it was in case someone needs that one. But, I will edit it to direct people to this one for current versions.
The significant difference here is to make use of the "new" (since 3.1) availability of adding in custom path resolvers. Which does make the code shorter, as Jeroen suggested. But taken a little bit further. In particular the #find_templates is no longer private and it is expected that you will write a custom one.
# lib/fallback_resolver.rb
class FallbackResolver < ::ActionView::FileSystemResolver
def initialize(path, fallbacks = nil)
#fallback_list = fallbacks
super(path)
end
def find_templates(name, prefix, partial, details)
format = details[:formats].first
return super unless #fallback_list && #fallback_list[format]
formats = Array.wrap(#fallback_list[format])
details_copy = details.dup
details_copy[:formats] = formats
path = Path.build(name, prefix, partial)
query(path, details_copy, formats)
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
append_view_path 'app/views', {
mobile: [:sponsor_mobile, :mobile, :sponsor_html, :html],
html: [:sponsor_html, :html]
}
respond_to :html, :mobile
# config/initializers/mime_types.rb
register_alias 'text/html', :mobile
Here's a simpler solution:
class ApplicationController
...
def formats=(values)
values << :html if values == [:mobile]
super(values)
end
...
end
It turns out Rails (3.2.11) adds an :html fallback for requests with the :js format. Here's how it works:
ActionController::Rendering#process_action assigns the formats array from the request (see action_controller/metal/rendering.rb)
ActionView::LookupContext#formats= gets called with the result
Here's ActionView::LookupContext#formats=,
# Override formats= to expand ["*/*"] values and automatically
# add :html as fallback to :js.
def formats=(values)
if values
values.concat(default_formats) if values.delete "*/*"
values << :html if values == [:js]
end
super(values)
end
This solution is gross but I don't know a better way to get Rails to interpret a request MIME type of "mobile" as formatters [:mobile, :html] - and Rails already does it this way.
Yes, I'm pretty sure this is the right way to do this in rails. I've defined iphone formats this way before. That's a good question about getting the format to default back to :html if a template for iphone doesn't exist. It sounds simple enough, but I think you'll have to add in a monkeypath to either rescue the missing template error, or to check if the template exists before rendering. Take a look a the type of patches shown in this question. Something like this would probably do the trick (writing this code in my browser, so more pseudo code) but throw this in an initializer
# config/initializers/default_html_view.rb
module ActionView
class PathSet
def find_template_with_exception_handling(original_template_path, format = nil, html_fallback = true)
begin
find_template_without_exception_handling(original_template_path, format, html_fallback)
rescue ActionView::MissingTemplate => e
# Template wasn't found
template_path = original_template_path.sub(/^\//, '')
# Check to see if the html version exists
if template = load_path["#{template_path}.#{I18n.locale}.html"]
# Return html version
return template
else
# The html format doesn't exist either
raise e
end
end
end
alias_method_chain :find_template, :exception_handling
end
end
Here is another example of how to do it, inspired by Simon's code, but a bit shorter and a bit less hacky:
# application_controller.rb
class ApplicationController < ActionController::Base
# ...
# When the format is iphone have it also fallback on :html
append_view_path ExtensionFallbackResolver.new("app/views", :iphone => :html)
# ...
end
and somewhere in an autoload_path or explicitly required:
# extension_fallback_resolver.rb
class ExtensionFallbackResolver < ActionView::FileSystemResolver
attr_reader :format_fallbacks
# In controller do append_view_path ExtensionFallbackResolver.new("app/views", :iphone => :html)
def initialize(path, format_fallbacks = {})
super(path)
#format_fallbacks = format_fallbacks
end
private
def find_templates(name, prefix, partial, details)
fallback_details = details.dup
fallback_details[:formats] = Array(format_fallbacks[details[:formats].first])
path = build_path(name, prefix, partial, details)
query(path, EXTENSION_ORDER.map { |ext| fallback_details[ext] }, details[:formats])
end
end
The above is still a hack because it is using a private API, but possibly less fragile as Simon's original proposal.
Note that you need to take care of the layout seperately. You will need to implement a method that chooses the layout based on the user agent or something similar. The will only take care of the fallback for the normal templates.
Rails 4.1 includes Variants, this is a great feature that allow you to set different views for the same mime. You can now simply add a before_action and let the variant to do the magic:
before_action :detect_device_variant
def detect_device_variant
case request.user_agent
when /iPad/i
request.variant = :tablet
when /iPhone/i
request.variant = :phone
end
end
Then, in your action:
respond_to do |format|
format.json
format.html # /app/views/the_controller/the_action.html.erb
format.html.phone # /app/views/the_controller/the_action.html+phone.erb
format.html.tablet do
#some_tablet_specific_variable = "foo"
end
end
More info here.
You can in this case for the format to html. By example you want always use the html in user show method
class UserController
def show
..your_code..
render :show, :format => :html
end
end
In this case, if you request show on User controller you render all the time the html version.
If you want render JSON too by example you can made some test about your type like :
class UserController
def show
..your_code..
if [:mobile, :tablet, :html].include?(request.format)
render :show, :format => :html
else
respond_with(#user)
end
end
end
I made a monkey patch for that, but now, I use a better solution :
In application_controller.rb :
layout :which_layout
def which_layout
mobile? ? 'mobile' : 'application'
end
With the mobile? method you can write.
So I have a different layout but all the same views, and in the mobile.html.erb layout, I use a different CSS file.
I need the same thing. I researched this including this stack overflow question (and the other similar one) as well as followed the rails thread (as mentioned in this question) at https://github.com/rails/rails/issues/3855 and followed its threads/gists/gems.
Heres what I ended up doing that works with Rails 3.1 and engines. This solution allows you to place the *.mobile.haml (or *.mobile.erb etc.) in the same location as your other view files with no need for 2 hierarchies (one for regular and one for mobile).
Engine and preparation Code
in my 'base' engine I added this in config/initializers/resolvers.rb:
module Resolvers
# this resolver graciously shared by jdelStrother at
# https://github.com/rails/rails/issues/3855#issuecomment-5028260
class MobileFallbackResolver < ::ActionView::FileSystemResolver
def find_templates(name, prefix, partial, details)
if details[:formats] == [:mobile]
# Add a fallback for html, for the case where, eg, 'index.html.haml' exists, but not 'index.mobile.haml'
details = details.dup
details[:formats] = [:mobile, :html]
end
super
end
end
end
ActiveSupport.on_load(:action_controller) do
tmp_view_paths = view_paths.dup # avoid endless loop as append_view_path modifies view_paths
tmp_view_paths.each do |path|
append_view_path(Resolvers::MobileFallbackResolver.new(path.to_s))
end
end
Then, in my 'base' engine's application controller I added a mobile? method:
def mobile?
request.user_agent && request.user_agent.downcase =~ /mobile|iphone|webos|android|blackberry|midp|cldc/ && request.user_agent.downcase !~ /ipad/
end
And also this before_filter:
before_filter :set_layout
def set_layout
request.format = :mobile if mobile?
end
Finally, I added this to the config/initializers/mime_types.rb:
Mime::Type.register_alias "text/html", :mobile
Usage
Now I can have (at my application level, or in an engine):
app/views/layouts/application.mobile.haml
and in any view a .mobile.haml instead of a .html.haml file.
I can even use a specific mobile layout if I set it in any controller:
layout 'mobile'
which will use app/views/layouts/mobile.html.haml (or even mobile.mobile.haml).
I solved this problem by using this before_filter in my ApplicationController:
def set_mobile_format
request.formats.unshift(Mime::MOBILE) if mobile_client?
end
This puts the mobile format to the front of the list of acceptable formats. So, the Resolver prefers .mobile.erb templates, but will fall back to .html.erb if no mobile version is found.
Of course, for this to work you need to implement some kind of #mobile_client? function and put Mime::Type.register_alias "text/html", :mobile into your config/initializers/mime_types.rb

How do you use errors.add_to_base outside of the validates_ or validate_ model methods?

I have a Course class that has many WeightingScales and I am trying to get the WeightingScales validation to propagate through the system. The short of the problem is that the following code works except for the fact that the errors.add_to_base() call doesn't do anything (that I can see). The Course object saves just fine and the WeightingScale objects fail to save, but I don't ever see the error in the controller.
def weight_attributes=(weight_attributes)
weighting_scales.each do |scale|
scale.weight = weight_attributes.fetch(scale.id.to_s).fetch("weight")
unless scale.save
errors.add_to_base("The file is not in CSV format")
end
end
end
My question is similar to this 1: How can you add errors to a Model without being in a "validates" method?
link text
If you want the save to fail, you'll need to use a validate method. If not, you'll have to use callbacks like before_save or before_create to check that errors.invalid? is false before you allow the save to go through. Personally, i'll just use validate. Hope it helps =)
I had a similar problem, I wanted to validate a parameter that never needed to be saved to the model (just a confirmation flag).
If I did:
#user.errors.add_to_base('You need to confirm') unless params[:confirmed]
if #user.save
# yay
else
# show errors
end
it didn't work. I didn't delve into the source but from playing around in the console it looked like calling #user.save or #user.valid? cleared #user.errors before running the validations.
My workaround was to do something like:
if #user.valid? && params[:confirmed]
#user.save
# redirect to... yay!
elsif !params[:confirmed]
#user.errors.add_to_base('You need to confirm')
end
# show errors
Errors added to base after validations have been run stick around and display correctly in the view.
But this is a little different to your situation as it looks like you want to add errors in a setter not a controller action. You might want to look into before_validation or after_validation instead.

Resources