Skip to content

Commit

Permalink
Ensure the association class exists on association matchers.
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Jun 4, 2009
1 parent 33cb680 commit 456d8f2
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 14 deletions.
Expand Up @@ -9,7 +9,7 @@ class AssociationMatcher < Remarkable::ActiveRecord::Base #:nodoc:
optionals :uniq, :readonly, :validate, :autosave, :polymorphic, :counter_cache, :default => true

collection_assertions :association_exists?, :macro_matches?, :through_exists?, :source_exists?,
:join_table_exists?, :foreign_key_exists?, :polymorphic_exists?,
:klass_exists?, :join_table_exists?, :foreign_key_exists?, :polymorphic_exists?,
:counter_cache_exists?, :options_match?

protected
Expand All @@ -32,6 +32,10 @@ def source_exists?
reflection.source_reflection rescue false
end

def klass_exists?
reflection.klass rescue nil
end

# has_and_belongs_to_many only works if the tables are in the same
# database, so we always look for the table in the subject connection.
#
Expand Down Expand Up @@ -74,11 +78,18 @@ def reflection
@reflection ||= subject_class.reflect_on_association(@association.to_sym)
end

# Rescue nil to avoid raising errors in invalid through associations
def subject_table_name
subject_class.table_name.to_s
end

def reflection_class_name
reflection.class_name.to_s rescue nil
end

def reflection_table_name
reflection.klass.table_name.to_s rescue nil
end

def reflection_foreign_key
reflection.primary_key_name.to_s
end
Expand Down Expand Up @@ -107,9 +118,9 @@ def foreign_key_table
elsif reflection.macro == :has_and_belongs_to_many
reflection.options[:join_table]
elsif reflection.macro == :belongs_to
subject_class.table_name
subject_table_name
else
reflection.klass.table_name
reflection_table_name
end
end

Expand All @@ -132,14 +143,14 @@ def interpolation_options
if @subject && reflection
options.merge!(
:actual_macro => Remarkable.t(reflection.macro, :scope => matcher_i18n_scope, :default => reflection.macro.to_s),
:subject_table => subject_class.table_name.inspect,
:reflection_table => reflection.klass.table_name.inspect,
:subject_table => subject_table_name.inspect,
:reflection_table => reflection_table_name.inspect,
:foreign_key_table => foreign_key_table.inspect,
:polymorphic_column => reflection_foreign_key.sub(/_id$/, '_type').inspect,
:counter_cache_column => reflection.counter_cache_column.to_s.inspect,
:join_table => reflection.options[:join_table].inspect,
:foreign_key => reflection_foreign_key.inspect
) rescue nil # rescue to allow specs to run properly
)
end

options
Expand Down
3 changes: 2 additions & 1 deletion remarkable_activerecord/locale/en.yml
Expand Up @@ -53,10 +53,11 @@ en:
has_one: have one
description: "{{macro}} {{associations}}"
expectations:
association_exists: "{{subject_name}} records {{macro}} {{association}}, got no association"
association_exists: "{{subject_name}} records {{macro}} {{association}}, but the association does not exist"
macro_matches: "{{subject_name}} records {{macro}} {{association}}, got {{subject_name}} records {{actual_macro}} {{association}}"
through_exists: "{{subject_name}} records {{macro}} {{association}} through {{through}}, through association does not exist"
source_exists: "{{subject_name}} records {{macro}} {{association}} through {{through}}, source association does not exist"
klass_exists: "{{subject_name}} records {{macro}} {{association}}, but the association class does not exist"
join_table_exists: "join table {{join_table}} to exist, but does not"
foreign_key_exists: "foreign key {{foreign_key}} to exist on {{foreign_key_table}}, but does not"
polymorphic_exists: "{{subject_table}} to have {{polymorphic_column}} as column, but does not"
Expand Down
30 changes: 26 additions & 4 deletions remarkable_activerecord/spec/association_matcher_spec.rb
Expand Up @@ -9,10 +9,12 @@
def define_and_validate(options={})
columns = options.delete(:association_columns) || { :projects_count => :integer }
define_model :company, columns
define_model :super_company, columns

columns = options.delete(:model_columns) || { :company_id => :integer, :company_type => :string }
@model = define_model :project, columns do
belongs_to :company, options
belongs_to :unknown
end

belong_to :company
Expand Down Expand Up @@ -47,7 +49,7 @@ def define_and_validate(options={})
define_and_validate
matcher = belong_to('whatever')
matcher.matches?(@model)
matcher.failure_message.should == 'Expected Project records belong to whatever, got no association'
matcher.failure_message.should == 'Expected Project records belong to whatever, but the association does not exist'
end

it 'should set macro_matches? message' do
Expand All @@ -57,6 +59,13 @@ def define_and_validate(options={})
matcher.failure_message.should == 'Expected Project records have one company, got Project records belong to company'
end

it 'should set klass_exists? message' do
define_and_validate
matcher = belong_to('unknown')
matcher.matches?(@model)
matcher.failure_message.should == 'Expected Project records belong to unknown, but the association class does not exist'
end

it 'should set foreign_key_exists? message' do
matcher = define_and_validate(:model_columns => {})
matcher.matches?(@model)
Expand Down Expand Up @@ -93,6 +102,7 @@ def define_and_validate(options={})
before(:each){ define_and_validate }

it { should belong_to(:company) }
it { should_not belong_to(:unknown) }
it { should_not belong_to(:project) }
it { should_not have_one(:company) }
it { should_not have_many(:company) }
Expand Down Expand Up @@ -158,6 +168,7 @@ def define_and_validate(options={})
m.foreign_key = :company_id
end

should_not_belong_to :unknown
should_not_belong_to :project
should_not_have_one :company
should_not_have_many :companies
Expand All @@ -174,6 +185,7 @@ def define_and_validate(options={})
# Defines a model, create a validation and returns a raw matcher
def define_and_validate(options={})
define_model :label
define_model :super_label

columns = options.delete(:association_columns) || [ :label_id, :project_id ]
create_table(options.delete(:association_table) || :labels_projects) do |table|
Expand Down Expand Up @@ -209,7 +221,7 @@ def define_and_validate(options={})
define_and_validate
matcher = have_and_belong_to_many('whatever')
matcher.matches?(@model)
matcher.failure_message.should == 'Expected Project records have and belong to many whatever, got no association'
matcher.failure_message.should == 'Expected Project records have and belong to many whatever, but the association does not exist'
end

it 'should set macro_matches? message' do
Expand Down Expand Up @@ -365,7 +377,7 @@ def define_and_validate(options={})
define_and_validate
matcher = have_many('whatever')
matcher.matches?(@model)
matcher.failure_message.should == 'Expected Project records have many whatever, got no association'
matcher.failure_message.should == 'Expected Project records have many whatever, but the association does not exist'
end

it 'should set macro_matches? message' do
Expand Down Expand Up @@ -503,6 +515,7 @@ def define_and_validate(options={})
@model = define_model :project, options.delete(:model_columns) || {} do
has_many :project_managers unless options.delete(:skip_through)
has_one :manager, options
has_one :unknown
end

have_one :manager
Expand All @@ -524,7 +537,14 @@ def define_and_validate(options={})
define_and_validate
matcher = have_one('whatever')
matcher.matches?(@model)
matcher.failure_message.should == 'Expected Project records have one whatever, got no association'
matcher.failure_message.should == 'Expected Project records have one whatever, but the association does not exist'
end

it 'should set klass_exists? message' do
define_and_validate
matcher = have_one('unknown')
matcher.matches?(@model)
matcher.failure_message.should == 'Expected Project records have one unknown, but the association class does not exist'
end

it 'should set macro_matches? message' do
Expand Down Expand Up @@ -571,6 +591,7 @@ def define_and_validate(options={})
before(:each){ define_and_validate }

it { should have_one(:manager) }
it { should_not have_one(:unknown) }
it { should_not belong_to(:manager) }
it { should_not have_many(:managers) }
it { should_not have_and_belong_to_many(:managers) }
Expand Down Expand Up @@ -635,6 +656,7 @@ def define_and_validate(options={})
m.through = :project_managers
end

should_not_have_one :unknown
should_not_have_one :manager, :validate => false
should_not_have_one :manager, :through => :another_thing
end
Expand Down
3 changes: 2 additions & 1 deletion remarkable_i18n/en.yml
Expand Up @@ -174,10 +174,11 @@ en:
has_one: have one
description: "{{macro}} {{associations}}"
expectations:
association_exists: "{{subject_name}} records {{macro}} {{association}}, got no association"
association_exists: "{{subject_name}} records {{macro}} {{association}}, but the association does not exist"
macro_matches: "{{subject_name}} records {{macro}} {{association}}, got {{subject_name}} records {{actual_macro}} {{association}}"
through_exists: "{{subject_name}} records {{macro}} {{association}} through {{through}}, through association does not exist"
source_exists: "{{subject_name}} records {{macro}} {{association}} through {{through}}, source association does not exist"
klass_exists: "{{subject_name}} records {{macro}} {{association}}, but the association class does not exist"
join_table_exists: "join table {{join_table}} to exist, but does not"
foreign_key_exists: "foreign key {{foreign_key}} to exist on {{foreign_key_table}}, but does not"
polymorphic_exists: "{{subject_table}} to have {{polymorphic_column}} as column, but does not"
Expand Down
3 changes: 2 additions & 1 deletion remarkable_i18n/pt-BR.yml
Expand Up @@ -174,10 +174,11 @@ pt-BR:
has_one: possuir um(a)
description: "{{macro}} {{associations}}"
expectations:
association_exists: "{{subject_name}} pudesse {{macro}} {{association}}, obtive nenhuma associação"
association_exists: "{{subject_name}} pudesse {{macro}} {{association}}, mas associação não existe"
macro_matches: "{{subject_name}} pudesse {{macro}} {{association}}, obtive que {{subject_name}} deve {{actual_macro}} {{association}}"
through_exists: "{{subject_name}} pudesse {{macro}} {{association}} através de {{through}}, obtive que \"through_association\" não existe"
source_exists: "{{subject_name}} pudesse {{macro}} {{association}} através de {{through}}, obtive que \"source association\" não existe"
klass_exists: "{{subject_name}} records {{macro}} {{association}}, mas a classe da associação não existe"
join_table_exists: "tabela de junção {{join_table}} existisse, mas não existe"
foreign_key_exists: "chave estrangeira {{foreign_key}} existisse em {{foreign_key_table}}, mas não existe"
polymorphic_exists: "{{subject_table}} tivesse {{polymorphic_column}} como coluna, mas não tem"
Expand Down

0 comments on commit 456d8f2

Please sign in to comment.