ActiveRecord::Base.transaction validation missing - ruby-on-rails

Rails 4.1.16 or 4.2.7
Lets, for example, we have
class Foo < ActiveRecord::Base
validate :name, presence: true
end
Then in IRB we will get
Foo.create # => validation error
Foo.transaction{ Foo.create } # COMMIT
I can figure out, that there is no raise in transaction block, it mean, that it will be committed (on course I can use create!, but it does not cancel the question). But validation fails! IMHO this behaviour is not less than strange.
Can you clarify, why it was made in this way? May be I'm do not understood some specific in this case?
It will be perfect, if you know how to make ActiveRecord failing loudly, with no sense of !(bang) at the and of the command.
Thank you!

Related

Ruby on Rails - inconsistency when needing to reload model?

I found something which seems a bit confusing to me being new to Rails. I was told I need to do reload! in the console whenever I made a change in the model.
Let's assume I call reload! before these two senarios.
Let's say I have scenario A, with a model with a specific syntax error as such:
class Article < ActiveRecord::Base
validator :title, presence: true
end
Running Article.new(title: "Test 1") will throw a NoMethodError, as it understandably would. But if I then go in and fix the error, even if I don't run reload!, running Article.new(title: "Test 1") works now.
Scenario B, going in the opposite direction. I have a model with the correct syntax as such:
class Article < ActiveRecord::Base
validates :title, presence: true
end
Running Article.new(title: "Test 1") will work with no error, as it should. But if I then go in and change validates to validator. If I don't run reload!, running Article.new(title: "Test 1") still works despite the article.rb file having a syntax error. It isn't until I run reload! explicitly that I now get a NoMethodError.
What's exactly going on here? I know it's very specific, but I don't see why this would be the case. It seems like sometimes you have to run reload! to update the model (like scenario B) and sometimes, like in scenario A, you don't.
In your first example, Rails was not able to load the class because it raised an error. After fixing the error there was no need to class reload because the class was not loaded successfully before.
In your second example, the class was loaded successfully. Therefore you need to call reload! to tell Rails to reload the class into memory.

Issue with collection `build` method record instantiation in Rails 4.1.1

I'm having a problem with the Rails collection.build(attrs) method, specifically with how the framework instantiates a new record. For example, here is a much simplified view of my models:
class Document < ActiveRecord::Base
belongs_to :account
has_many :descriptions, before_add: :id_test
validates :account, presence: true
def id_test
puts self.account_id
end
end
When I do something like:
current_account.documents.build(:descriptions => [desc])
then id_test prints nothing. That is, the account_id is not set in the before_add callback (and yes, I've tried the after_add callback as well; account_id is not set in that case either).
If I do:
d = current_account.documents.build
d.assign_attributes(:descriptions => [desc])
Then everything works as expected. However, I would prefer a better alternative, since this would be a pain to implement in the controllers...
Is there a way to get Rails to add the foreign_key first, or is there some better way to set this up? I haven't gone back to check for sure, but this seems different than the way Rails 3 evaluated collection.build statements.
EDIT
Looking through the Rails code a bit, I see I can do:
current_account.documents.build do |record|
record.assign_attributes(:descriptions => [desc])
end
and everything works as expected. Although a bit more verbose, I guess this is technically more accurate anyway.
I would suggest using
Document.build(:account_id => current_account.id, :descriptions => [desc])

How can I programmatically copy ActiveModel validators from one model to another?

I'm writing a library that will require programmatically copying validations from one model to another, but I'm stumped on how to pull this off.
I have a model that is an ActiveModel::Model with some validation:
class User < ActiveRecord::Base
validates :name, presence: true
end
And another model that I'd like to have the same validations:
class UserForm
include ActiveModel::Model
attr_accessor :name
end
Now I'd like to give UserForm the same validations as User, and without modifying User. Copying the validators over doesn't work, because ActiveModel::Validations hooks into callbacks during the validation check:
UserForm._validators = User._validators
UserForm.new.valid?
# => true # We wanted to see `false` here, but no validations
# are actually running because the :validate callback
# is empty.
Unfortunately, there doesn't seem to be an easy way that I can see to programmatically give one model another's validation callbacks and still have it work. I think my best bet is if I can ask Rails to regenerate the validation callbacks based on the validators that are present at a given moment in time.
Is that possible? If not, is there a better way to do this?
Checking into the code of activerecord/lib/active_record/validations/presence.rb reveals how this can be achieved:
# File activerecord/lib/active_record/validations/presence.rb, line 60
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
end
So I guess I would try to hook into validates_with with an alias_method
alias_method :orig_validates_with :validates_with
Now you have a chance to get ahold of the values passed, so you can store them somewhere and retrieve them when you need to recreate the validation on UserForm
alias_method :orig_validates_with, :validates_with
def validates_with(*args)
# save the stuff you need, so you can recreate this method call on UserForm
orig_validates_with(*args)
end
Then you should be able to just call UserForm.validates_with(*saved_attrs). Sorry this is not something you can just copy/paste, but this should get you started. HTH

Rails simple validations not working

class User < ActiveRecord::Base
attr_accessible :email, :name
validates :name,:presence=>true,
:length=>{:maximum=>15}
validates :email,:presence=>true,
:length=>{:maximum=>15}
end
I am new to rails and the simplest of the validators are not working. I think I may be making a very silly mistake . I have a User model with 2 attributes only and when I create a new user in ruby console with wrong validations like no name or a longer name than 15 characters it gets added happily Please suggest.I am using rails version:3.2.13 and ruby version:1.9.3
If you are on rails console, be sure to type reload! after making changes to models. In this way, all changes will be reloaded in the console instance.
Moreover, are you sure you are saving these models? You should try something like this:
user = User.new(email: "john.doe#gmail.com")
user.save
If the result of the last line is false, you can view the validation errors with
p user.errors

How to call ActiveRecord validators as instance methods (ala Sequel)?

I've got a model that needs different validators depending on its current state. How should I go about calling ActiveRecord validators per instance? I'd like to reuse as much plumbing as possible, but I'm not sure how to continue.
class Order < ActiveRecord::Base
attr_accessible :state
validate :state_specific_validations
def state_specific_validations
if :paid == self.state
# Warning: here be Unicorns...
# Wishful thinking...
validate_presence_of :paid_at
validate_associated :purchaser
# Hopeful. What are the validators called internally in Rails?
errors << PresenceValidator.new(self, :paid_at).valid?
errors << AssociationValidator.new(self, :paid_at).valid?
# Plan B
# ... Hoping for help from the audience ...
else
# Even more complicated validator logic, hoping for some DRY validators
end
end
end
I could just use custom validators, but why would I need to duplicate all the built-in validator logic (i18n error messages, etc.)?
Is there a neat way of calling the Rails validators as instance methods?
I think Sequel's approach of instance-based validators is more reasonable than ActiveRecord's class-based, but I'm not here to judge. I'd just like to get back to solving more interesting problems. I'm just hoping that others have come across this and can point me to some interesting gist or gem.
I'm pretty sure all of the validate_* methods can take an :if option - which can point to another method (and likely accept a Proc as well), so you could break up your validations to be more something like:
validates_presence_of :paid_at, :if => :paid?
validates_association :purchaser, :if => :paid?
To clean things up further, there's the with_options helper:
with_options :if => :paid? do |v|
v.validates_presence_of :paid_at
v.validates_association :purchaser
end
Not sure if either can be used with a standard validate :custom_validate_method though - but it wouldn't surprise me.
Is there any reason why this is inappropriate? It seems like it might work, but maybe metaprogramming warped my brain...
class Order < ActiveRecord::Base
attr_accessible :state
validate :state_specific_validations
def state_specific_validations
if :paid == self.state
class << self
validate_presence_of :paid_at
validate_associated :purchaser
end
end
end
end
Worst part is that tests are passing, so I'm not sure if I solved it or I need better tests. For example, I'm not 100% positive this singleton modification does not affect other Orders.
sigh Need some sleep.

Resources