I would like to implement warning errors on model validation with Ruby mongoid gem.
I kinda followed this example found here Efficient way to report record validation warnings as well as errors?
class MyModel
validate :check_warnings
def warnings
#warnings ||= ActiveModel::Errors.new(self)
end
def check_warnings
warnings.add(:some_field, :not_exist) if some_field.blank?
warnings.add(:another_field, :not_exist) if another_field.blank?
end
end
When warnings value is acessed in Rails helper to display warnings, it is empty.
p doc.warnings.size => 0
But if I force a validation error
def check_warnings
warnings.add(:some_field, :not_exist) if some_field.blank?
warnings.add(:another_field, :not_exist) if another_field.blank?
errors.add(:yet_another_field, :not_exist) if yet_another_field.blank?
end
# in helper
p doc.errors.size => 1
p doc.warnings.size => 2
warning errors are preserved.
Is this maybe a mongoid bug or am I missing something.
by
TheR
You need to perform the validation (by calling validate, valid?, save/save! etc.) before looking at the errors or your warnings. If you simply look at errors or warnings without having validated you will have empty sets.
Related
While working on a Rails app and investigating a bug, I narrowed it down to the following situation, where line AAA would cause events to return nil, running no code after points XXX or YYYY, but line BBB worked as expected. I switched back and forth several times making a clean environment every time, and it always behaved the same odd way.
Ruby 2.5.0, haven't tried downgrading yet but I suspect the issue was present in 2.3 (I skipped 2.4 with this app).
# this is the method that has the issue when called. It's in a PORO service object instantiated in a helper method & called from a Rails view.
def events
#events ||= begin
events = []
# some code building up events
events << :whatever if fulfillment.ready_to_process?
#XXX -- execution never reaches here with #AAA. But no error is thrown, and events returns nil
# some more code building up events
end
end
# Fulfillment & LineItem are ActiveRecords
class Fulfillment
def ready_to_process?
# some "return false if" assertions
result = line_items.any?(&:fulfillment_needed?) #AAA
result = line_items.any?{ |line_item| line_item.fulfillment_needed? } #BBB
# YYY -- exececution never reaches here with #AAA
result
end
end
class LineItem
def fulfillment_needed?
# various assertions
true
end
end
EDIT: note that in the context where the bug was appearing, fulfillment_needed? was always true.
EDIT 2: actually, I found another something in fulfillment_needed? that can toggle the bug:
class LineItem
has_many :vendor_line_items, dependent: :destroy
def fulfillment_needed?
return true if vendor_line_items.empty? # Displays the bug, AAA fails but BBB works
return true # Does not display the bug AAA && BBB work
end
end
vendor_line_items.empty? behaves normally elsewhere.
I am trying to use a vanity URL that uses the SecureRandom issued ident to a culvert, and then display that culvert via the show page.
This is a screenshot of the error message:
This is a screenshot of the browser url:
My Culvert Controller is:
I have tried Both:
#culvert = Culvert.find_by_culvert_ident(params[:id])
AND
#culvert = Culvert.find_by_id(params[:culvert_ident])
In my culvert controller show action, both yield the same result (screenshot)
private
# Use callbacks to share common setup or constraints between actions.
def set_culvert
#culvert = Culvert.find_by_culvert_ident(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def culvert_params
params.require(:culvert).permit(:culvert_ident, :latitude, :longitude, :address, :user_id)
end
This is my Culvert Model ident generator and vanity url methods:
before_create :generate_culvert_ident
# Relationships
belongs_to :user
# Model Validations
validates_uniqueness_of :culvert_ident
# Ident Generator
def generate_culvert_ident
begin
self.culvert_ident = SecureRandom.hex(3).upcase
other_culvert = Culvert.find_by(culvert_ident: self.culvert_ident)
end while other_culvert
end
# Url Direction
def to_param
culvert_ident
end
So my goal is to create the culvert, auto assign a unique identifier, save it and display the culvert using the custom identifier as opposed to the standard 1,2,3,4 id's
this works in another web app i have used, is setup exactly the same but i am getting this error here and cant figure out why. Please let me knwo if you require further info!
**
EDIT # 1 - Adds Screenshot of Console output
**
So the issue here was that I removed the culverts controller
before_action :set_culvert
as soon as I re-added the set_user action the issue was resolved.
thanks for your assistance!
This simple example uses DataMapper's before :save callback (aka hook) to increment callback_count. callback_count is initialized to 0 and should be set to 1 by the callback.
This callback is invoked when the TestObject is created via:
TestObject.create()
but the callback is skipped when created by FactoryGirl via:
FactoryGirl.create(:test_object)
Any idea why? [Note: I'm running ruby 1.9.3, factory_girl 4.2.0, data_mapper 1.2.0]
Full details follow...
The DataMapper model
# file: models/test_model.rb
class TestModel
include DataMapper::Resource
property :id, Serial
property :callback_count, Integer, :default => 0
before :save do
self.callback_count += 1
end
end
The FactoryGirl declaration
# file: spec/factories.rb
FactoryGirl.define do
factory :test_model do
end
end
The RSpec tests
# file: spec/models/test_model_spec.rb
require 'spec_helper'
describe "TestModel Model" do
it 'calls before :save using TestModel.create' do
test_model = TestModel.create
test_model.callback_count.should == 1
end
it 'fails to call before :save using FactoryGirl.create' do
test_model = FactoryGirl.create(:test_model)
test_model.callback_count.should == 1
end
end
The test results
Failures:
1) TestModel Model fails to call before :save using FactoryGirl.create
Failure/Error: test_model.callback_count.should == 1
expected: 1
got: 0 (using ==)
# ./spec/models/test_model_spec.rb:10:in `block (2 levels) in <top (required)>'
Finished in 0.00534 seconds
2 examples, 1 failure
At least for factory_girl 4.2 (don't know since which version it is supported), there is another workwaround through the use of custom methods to persist objects. As it is stated in a response to an issue about it in Github, it is just a matter of calling save instead of save!.
FactoryGirl.define do
to_create do |instance|
if !instance.save
raise "Save failed for #{instance.class}"
end
end
end
Of course it is not ideal because it should be functional in FactoryGirl core, but I think right now it is the best solution and, at the moment, I'm not having conflicts with other tests...
The caveat is that you have to define it in each factory (but for me it wasn't an inconvenient)
Solved.
#Jim Stewart pointed me to this FactoryGirl issue where it says "FactoryGirl calls save! on the instance [that it creates]". In the world of DataMapper, save! expressly does not run the callbacks -- this explains the behavior that I'm seeing. (But it doesn't explain why it works for #enthrops!)
That same link offers some workarounds specifically for DataMapper and I'll probably go with one of them. Still, it would be nice if an un-modified FactoryGirl played nice with DataMapper.
update
Here's the code suggested by Joshua Clayton of thoughtbot. I added it to my spec/factories.rb file and test_model_spec.rb now passes without error. Cool beans.
# file: factories.rb
class CreateForDataMapper
def initialize
#default_strategy = FactoryGirl::Strategy::Create.new
end
delegate :association, to: :#default_strategy
def result(evaluation)
evaluation.singleton_class.send :define_method, :create do |instance|
instance.save ||
raise(instance.errors.send(:errors).map{|attr,errors| "- #{attr}: #{errors}" }.join("\n"))
end
#default_strategy.result(evaluation)
end
end
FactoryGirl.register_strategy(:create, CreateForDataMapper)
update 2
Well. perhaps I spoke too soon. Adding the CreateForDataMapper fixes that one specific test, but appears to break others. So I'm un-answering my question for now. Someone else have a good solution?
Use build to build your object, then call save manually...
t = build(:test_model)
t.save
I am trying to do Ajax login with Devise, as explained here: http://jessehowarth.com/2011/04/27/ajax-login-with-devise#comment-5 (see comment from jBeasley).
My controller is attempting to return
class Users::SessionsController < Devise::SessionsController
def failure
render :json => {:success => false, :errors => ["Login failed."]}
end
end
which results in this error:
NameError (wrong constant name ["{\"success\":false,\"errors\":[\"Login failed.\"]}"]Controller):
and Firebug showing [500 Internal Server Error].
How can I fix this? I am running Rails 3.1 and devise 1.4.5.
Thanks!!
Did you do the step recommended by Jeff Poulton in comment #4? The :recall option in 1.4.5 looks to be completely incompatible to older versions. It now requires you send the controller, whereas in the tutorial you're following he just sends the action (the old way).
In your case, :recall => :failure must be changed to :recall => "users/sessions#failure" in Devise 1.4.5.
This is because of the way the controller for the failure action is determined. In older versions, it was simply pulled from the params.
def recall_controller
"#{params[:controller]}.camelize}Controller".constantize
end
# called via recall_controller.action(warden_options[:recall]).call(env)
In 1.4.5, it expects a string specifying the controller and action, in the style of routes:
def recall_app(app)
controller, action = app.split('#')
controller_name = ActiveSupport::Inflector.camelize(controller)
controlller_klass = ActiveSupport::Inflector.constantize("#{controller_name}Controller")
controller_klass.action(action)
end
# called via recall_app(warden_options[:recall]).call(env)
It would seem as though your app is actually passing the JSONified hash of options to recall_app, which, lacking a '#', isn't being split, and the entire string is concatenated to "Controller" to attempt to ascertain the failure controller's class.
You are missing the return in
def failure
return render:json => {:success => false, :errors => ["Login failed."]}
end
Does that make a difference?
This is a painfully noob question, but I have to ask it. I want validation to trip if a particular field, let's call it :token isn't a particular string. So, I call my custom validation:
validate :use_beta_token
And then I define my validation method
def use_beta_token
errors.add(:token, "Incorrect beta token") if token not 'pizza'
end
Whenever I set the token to a string that isn't "pizza", and I test with valid? it's coming back true. What am I messing up here? I've also tried if token !== 'pizza', but that's not working either. I'm sure the answer is painfully obvious, but I can't seem to dig it up.
try
errors.add(:token, "Incorrect beta token") unless token == 'pizza'
the not method works like !, it's a unary boolean operator rather than a binary comparison operator.
as for how to write them, keep it concise. See the rails guide for examples.
One way to use custom validators for Rails 3 is to define your own Validator class that inherits from ActiveModel::Validator then implement the validate method and attach errors like so:
# define my validator
class MyValidator < ActiveModel::Validator
# implement the method where the validation logic must reside
def validate(record)
# do my validations on the record and add errors if necessary
record.errors[:token] << "Incorrect beta token" unless token == 'pizza'
end
end
Once you define your validator, you must then include it into your model so it can be used and apply it with the validates_with method.
class ModelName
# include my validator and validate the record
include ActiveModel::Validations
validates_with MyValidator
end