Skip to content

Commit

Permalink
Make ActiveRecordException descendants args optional
Browse files Browse the repository at this point in the history
This change allows to instantiate all ActiveRecordError descendant
execption classes without arguments, which might be useful in testing
and is far less surprising than mandatory arguments.
  • Loading branch information
rwz committed Sep 7, 2015
1 parent f443ae6 commit 4ff626c
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 66 deletions.
134 changes: 99 additions & 35 deletions activerecord/lib/active_record/associations.rb
Expand Up @@ -5,65 +5,105 @@

module ActiveRecord
class AssociationNotFoundError < ConfigurationError #:nodoc:
def initialize(record, association_name)
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
def initialize(record = nil, association_name = nil)
if record && association_name
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
else
super("Association was not found.")
end
end
end

class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
def initialize(reflection, associated_class = nil)
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
def initialize(reflection = nil, associated_class = nil)
if reflection
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
else
super("Could not find the inverse association.")
end
end
end

class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection)
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
def initialize(owner_class_name = nil, reflection = nil)
if owner_class_name && reflection
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
else
super("Could not find the association.")
end
end
end

class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection, source_reflection)
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
if owner_class_name && reflection && source_reflection
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
else
super("Cannot have a has_many :through association.")
end
end
end

class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection)
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
def initialize(owner_class_name = nil, reflection = nil)
if owner_class_name && reflection
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
else
super("Cannot have a has_many :through association.")
end
end
end

class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection, source_reflection)
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
if owner_class_name && reflection && source_reflection
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
else
super("Cannot have a has_many :through association.")
end
end
end

class HasOneThroughCantAssociateThroughCollection < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection, through_reflection)
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
if owner_class_name && reflection && through_reflection
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
else
super("Cannot have a has_one :through association.")
end
end
end

class HasOneAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection)
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
def initialize(owner_class_name = nil, reflection = nil)
if owner_class_name && reflection
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
else
super("Cannot have a has_one :through association.")
end
end
end

class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
def initialize(reflection)
through_reflection = reflection.through_reflection
source_reflection_names = reflection.source_reflection_names
source_associations = reflection.through_reflection.klass._reflections.keys
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
def initialize(reflection = nil)
if reflection
through_reflection = reflection.through_reflection
source_reflection_names = reflection.source_reflection_names
source_associations = reflection.through_reflection.klass._reflections.keys
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
else
super("Could not find the source association(s).")
end
end
end

class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc:
def initialize(owner, reflection)
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
def initialize(owner = nil, reflection = nil)
if owner && reflection
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
else
super("Cannot modify association.")
end
end
end

Expand All @@ -74,20 +114,32 @@ class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssoc
end

class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
def initialize(owner, reflection)
super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
def initialize(owner = nil, reflection = nil)
if owner && reflection
super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
else
super("Cannot associate new records.")
end
end
end

class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
def initialize(owner, reflection)
super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
def initialize(owner = nil, reflection = nil)
if owner && reflection
super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
else
super("Cannot dissociate new records.")
end
end
end

class ThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc:
def initialize(owner, reflection)
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
def initialize(owner = nil, reflection = nil)
if owner && reflection
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
else
super("Through nested associations are read-only.")
end
end
end

Expand All @@ -98,23 +150,35 @@ class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreR
end

class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
def initialize(reflection)
super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
def initialize(reflection = nil)
if reflection
super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
else
super("Eager load polymorphic error.")
end
end
end

class ReadOnlyAssociation < ActiveRecordError #:nodoc:
def initialize(reflection)
super("Cannot add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
def initialize(reflection = nil)
if reflection
super("Cannot add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
else
super("Read-only reflection error.")
end
end
end

# This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
# (has_many, has_one) when there is at least 1 child associated instance.
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
class DeleteRestrictionError < ActiveRecordError #:nodoc:
def initialize(name)
super("Cannot delete record because of dependent #{name}")
def initialize(name = nil)
if name
super("Cannot delete record because of dependent #{name}")
else
super("Delete restriction error.")
end
end
end

Expand Down
38 changes: 23 additions & 15 deletions activerecord/lib/active_record/errors.rb
Expand Up @@ -63,7 +63,7 @@ def initialize(message = nil, model = nil, primary_key = nil, id = nil)
class RecordNotSaved < ActiveRecordError
attr_reader :record

def initialize(message, record = nil)
def initialize(message = nil, record = nil)
@record = record
super(message)
end
Expand All @@ -80,7 +80,7 @@ def initialize(message, record = nil)
class RecordNotDestroyed < ActiveRecordError
attr_reader :record

def initialize(message, record = nil)
def initialize(message = nil, record = nil)
@record = record
super(message)
end
Expand All @@ -92,9 +92,9 @@ def initialize(message, record = nil)
class StatementInvalid < ActiveRecordError
attr_reader :original_exception

def initialize(message, original_exception = nil)
super(message)
def initialize(message = nil, original_exception = nil)
@original_exception = original_exception
super(message)
end
end

Expand Down Expand Up @@ -134,10 +134,14 @@ class NoDatabaseError < StatementInvalid
class StaleObjectError < ActiveRecordError
attr_reader :record, :attempted_action

def initialize(record, attempted_action)
super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
@record = record
@attempted_action = attempted_action
def initialize(record = nil, attempted_action = nil)
if record && attempted_action
@record = record
@attempted_action = attempted_action
super("Attempted to #{attempted_action} a stale object: #{record.class.name}.")
else
super("Stale object error.")
end
end

end
Expand Down Expand Up @@ -196,7 +200,7 @@ class DangerousAttributeError < ActiveRecordError
class AttributeAssignmentError < ActiveRecordError
attr_reader :exception, :attribute

def initialize(message, exception, attribute)
def initialize(message = nil, exception = nil, attribute = nil)
super(message)
@exception = exception
@attribute = attribute
Expand All @@ -209,7 +213,7 @@ def initialize(message, exception, attribute)
class MultiparameterAssignmentErrors < ActiveRecordError
attr_reader :errors

def initialize(errors)
def initialize(errors = nil)
@errors = errors
end
end
Expand All @@ -218,11 +222,15 @@ def initialize(errors)
class UnknownPrimaryKey < ActiveRecordError
attr_reader :model

def initialize(model, description = nil)
message = "Unknown primary key for table #{model.table_name} in model #{model}."
message += "\n#{description}" if description
super(message)
@model = model
def initialize(model = nil, description = nil)
if model
message = "Unknown primary key for table #{model.table_name} in model #{model}."
message += "\n#{description}" if description
@model = model
super(message)
else
super("Unknown primary key.")
end
end
end

Expand Down
42 changes: 30 additions & 12 deletions activerecord/lib/active_record/migration.rb
Expand Up @@ -84,35 +84,53 @@ class IrreversibleMigration < MigrationError
end

class DuplicateMigrationVersionError < MigrationError#:nodoc:
def initialize(version)
super("Multiple migrations have the version number #{version}")
def initialize(version = nil)
if version
super("Multiple migrations have the version number #{version}.")
else
super("Duplicate migration version error.")
end
end
end

class DuplicateMigrationNameError < MigrationError#:nodoc:
def initialize(name)
super("Multiple migrations have the name #{name}")
def initialize(name = nil)
if name
super("Multiple migrations have the name #{name}.")
else
super("Duplicate migration name.")
end
end
end

class UnknownMigrationVersionError < MigrationError #:nodoc:
def initialize(version)
super("No migration with version number #{version}")
def initialize(version = nil)
if version
super("No migration with version number #{version}.")
else
super("Unknown migration version.")
end
end
end

class IllegalMigrationNameError < MigrationError#:nodoc:
def initialize(name)
super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
def initialize(name = nil)
if name
super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
else
super("Illegal name for migration.")
end
end
end

class PendingMigrationError < MigrationError#:nodoc:
def initialize
if defined?(Rails.env)
super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}")
def initialize(message = nil)
if !message && defined?(Rails.env)
super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}.")
elsif !message
super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate.")
else
super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate")
super
end
end
end
Expand Down
14 changes: 10 additions & 4 deletions activerecord/lib/active_record/validations.rb
Expand Up @@ -12,10 +12,16 @@ module ActiveRecord
class RecordInvalid < ActiveRecordError
attr_reader :record

def initialize(record)
@record = record
errors = @record.errors.full_messages.join(", ")
super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
def initialize(record = nil)
if record
@record = record
errors = @record.errors.full_messages.join(", ")
message = I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", errors: errors, default: :"errors.messages.record_invalid")
else
message = "Record invalid"
end

super(message)
end
end

Expand Down

0 comments on commit 4ff626c

Please sign in to comment.