joinfix 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/README +1 -1
  2. data/lib/joinfix/fixtures.rb +130 -155
  3. data/lib/joinfix/fixtures_class.rb +95 -30
  4. data/lib/joinfix.rb +108 -45
  5. data/rails/log/development.log +71 -0
  6. data/rails/log/test.log +85 -0
  7. data/rails/test/test_helper.rb +1 -0
  8. data/test/belongs_to_polymorphic_test.rb +78 -0
  9. data/test/belongs_to_test.rb +135 -44
  10. data/test/belongs_to_with_options_test.rb +57 -0
  11. data/test/has_and_belongs_to_many_test.rb +36 -36
  12. data/test/has_and_belongs_to_many_with_options_test.rb +84 -0
  13. data/test/has_many_as_test.rb +66 -0
  14. data/test/has_many_test.rb +33 -69
  15. data/test/has_many_through_test.rb +79 -0
  16. data/test/has_many_through_with_options_test.rb +85 -0
  17. data/test/has_many_with_options_test.rb +63 -0
  18. data/test/has_one_test.rb +26 -27
  19. data/test/has_one_with_options_test.rb +57 -0
  20. data/test/joinfix_test.rb +5 -5
  21. data/test/joinfix_test_helper.rb +76 -16
  22. data/test/missing_fixture_test.rb +54 -0
  23. data/test/nested_test.rb +13 -6
  24. data/test/todo +15 -0
  25. metadata +48 -51
  26. data/test/fixtures/as_children.yml +0 -0
  27. data/test/fixtures/bt_children.yml +0 -0
  28. data/test/fixtures/bt_parents.yml +0 -30
  29. data/test/fixtures/habtm_children.yml +0 -0
  30. data/test/fixtures/habtm_children_habtm_parents.yml +0 -0
  31. data/test/fixtures/habtm_joins.yml +0 -0
  32. data/test/fixtures/habtm_parents.yml +0 -18
  33. data/test/fixtures/hm_children.yml +0 -0
  34. data/test/fixtures/hm_joins.yml +0 -0
  35. data/test/fixtures/hm_parents.yml +0 -34
  36. data/test/fixtures/ho_children.yml +0 -0
  37. data/test/fixtures/ho_parents.yml +0 -14
  38. data/test/fixtures/no_join_fixes.yml +0 -4
  39. data/test/fixtures/omap_no_join_fixes.yml +0 -7
  40. data/test/fixtures/polymorphic_children.yml +0 -0
data/README CHANGED
@@ -37,7 +37,7 @@
37
37
  # groups: admin_group # => reference to the 'admin_group' entry
38
38
  #
39
39
  # jane:
40
- # id: 3 # => you can specify ids if you want
40
+ # id: 3 # => you can specify ids if you want
41
41
  # login: jane
42
42
  # groups: # => an array of joins
43
43
  # - admin_group
@@ -1,32 +1,32 @@
1
1
  class Fixtures
2
- attr_reader :join_configs, :templates
2
+ attr_reader :klass, :attributes, :templates, :fixture_path
3
3
 
4
+ alias join_fix_original_initialize initialize
5
+ def initialize(*args)
6
+ join_fix_original_initialize(*args)
7
+ @klass = Object.const_get(@class_name)
8
+
9
+ # default attributes, constructed from reflecting on the active record table
10
+ @attributes = {}
11
+ @klass.columns.each do |column|
12
+ @attributes[column.name] = column.default
13
+ end
14
+
15
+ @join_configs = {}
16
+ end
17
+
4
18
  alias join_fix_original_insert_fixtures insert_fixtures
5
19
  def insert_fixtures
6
20
  Fixtures.template_all_loaded_fixtures if templates.nil?
7
21
  join_fix_original_insert_fixtures
8
22
  end
9
-
10
- @@entry_stack = []
11
-
23
+
12
24
  def template_fixtures
13
25
  return unless templates.nil?
14
26
 
15
- begin
16
- configure
17
- rescue
18
- raise "\nError configuring fixtures for: #{@class_name}\n#{$!.message}"
19
- end
20
-
21
27
  extract_templates
22
-
23
- begin
24
- templates.each_pair do |entry_name, entry|
25
- make_entry(entry_name, entry, true)
26
- end
27
- rescue
28
- entry_str = "Entry:\n" + @@entry_stack.last.to_yaml
29
- raise "Error making entry for: '#{@class_name}'\n#{entry_str}#{$!.message}"
28
+ templates.each_pair do |entry_name, entry|
29
+ make_entry(entry_name, entry, true)
30
30
  end
31
31
  end
32
32
 
@@ -37,158 +37,62 @@ class Fixtures
37
37
  # @connection. QUERY FOR NEXT ID
38
38
  end
39
39
 
40
+ def join_config(assoc_name)
41
+ @join_configs[assoc_name] ||= JoinFix.send("configure", klass, assoc_name)
42
+ end
43
+
40
44
  def each_pair(&block)
41
45
  map { |entry_name, fixture| yield(entry_name, fixture) }
42
46
  end
43
47
 
44
- def config
45
- configure unless @config
46
- @config
48
+ def to_hash
49
+ hash = {}
50
+ each_pair do |name, fixture|
51
+ hash[name] = fixture.to_hash
52
+ end
53
+ hash
47
54
  end
48
55
 
49
56
  protected
50
57
 
51
- def configure
52
- klass = @class_name.constantize
53
- file_base = @class_name.underscore.singularize
54
-
55
- # default attributes, constructed from reflecting on the active record table
56
- attributes = {}
57
- klass.columns.each do |column|
58
- next if JoinFix.preserves?(column.name)
59
- attributes[column.name] = column.default
60
- end
61
-
62
- # default associations, constructed from reflecting on the active record class
63
- associations = {}
64
- klass.reflect_on_all_associations.each do |association|
65
- begin
66
- config = {:macro => association.macro}.merge(association.options)
67
-
68
- # get the child table name either by reflecting on the association class
69
- config[:class_name] ||= association.class_name
70
- config[:table_name] = config[:class_name].constantize.table_name unless config[:polymorphic]
71
-
72
- associations[association.name] = config
73
- rescue
74
- # known failure retrieving class name for has_many :through if the join model doesn't
75
- # have a 'belongs_to' pointing to the source model
76
- raise "Could not reflect on association: #{association.name}\n" +
77
- "Check that your join models are properly configured.\n" +
78
- $!.message
79
- end
80
- end
81
-
82
- # merge the defaults with the file configurations
83
- @config = {
84
- 'class_name' => klass.class_name,
85
- 'table_name' => klass.table_name,
86
- 'attributes' => attributes,
87
- 'associations' => associations
88
- # FUTURE! bring back if you start running methods
89
- #'modules' => [JoinFix, "#{@class_name}Template"]
90
- }.with_indifferent_access
91
-
92
- @join_configs = {}.with_indifferent_access
93
- parent_config = @config
94
- associations.each_pair do |association, child_config|
95
- # define shared parameters
96
- join_config = {
97
- :parent_table => parent_config[:table_name],
98
- :child_table => child_config[:table_name],
99
- :macro => child_config[:macro]
100
- }.with_indifferent_access
101
-
102
- case join_config[:macro].to_sym
103
- when :belongs_to
104
- if child_config[:polymorphic]
105
- join_config[:polymorphic] = true
106
- join_config[:associable] = association.to_s
107
- join_config[:foreign_key] = child_config[:foreign_key] || "#{association}_id"
108
- join_config.delete(:child_table) # not necessary, but reflects the fact that the polymorphic entry must specify this directly
109
- else
110
- join_config[:foreign_key] = child_config[:foreign_key] || "#{join_config[:child_table].singularize}_id"
111
- end
112
- when :has_one
113
- join_config[:foreign_key] = child_config[:foreign_key] || "#{join_config[:parent_table].singularize}_id"
114
- when :has_many
115
- if child_config[:as]
116
- join_config[:as] = child_config[:as]
117
- join_config[:foreign_key] = child_config[:foreign_key] || "#{child_config[:as]}_id"
118
- join_config[:parent_class] = parent_config[:class_name]
119
- elsif child_config[:through]
120
- join_config[:through] = child_config[:through]
121
- join_config[:join_table] = associations[child_config[:through]][:table_name]
122
- join_config[:foreign_key] = "#{join_config[:parent_table].singularize}_id"
123
- join_config[:association_foreign_key] = "#{join_config[:child_table].singularize}_id"
124
- else
125
- join_config[:foreign_key] = child_config[:foreign_key] || "#{join_config[:parent_table].singularize}_id"
126
- end
127
- when :has_and_belongs_to_many
128
- join_config[:join_table] = child_config[:join_table] || (join_config[:parent_table] < join_config[:child_table] ? "#{join_config[:parent_table]}_#{join_config[:child_table]}" : "#{join_config[:child_table]}_#{join_config[:parent_table]}") # echos the way join tables are guessed by ActiveRecord
129
- join_config[:foreign_key] = child_config[:foreign_key] || "#{join_config[:parent_table].singularize}_id"
130
- join_config[:association_foreign_key] = child_config[:association_foreign_key] || "#{join_config[:child_table].singularize}_id"
131
- else
132
- raise ArgumentError, "Unknown join type '#{join_config[:macro]}'."
133
- end
134
-
135
- join_config[:attributes] = Fixtures.fixture(join_config[:join_table]).config[:attributes] if join_config.has_key?(:join_table)
136
- @join_configs[association] = join_config
137
- end
138
- end
139
-
140
58
  def extract_templates
141
59
  # fixture is a template if it contains an association
142
60
  @templates = {}
143
- associations = config[:associations].keys
61
+ assoc_names = klass.reflect_on_all_associations.collect {|assoc| assoc.name.to_s}
144
62
  each do |entry_name, fixture|
145
- #next if key.to_s !~ /[=|!]$/ # FUTURE! bring back if you start running methods
146
63
  entry = fixture.to_hash
147
- next if (entry.keys & associations).empty?
64
+ next if (entry.keys & assoc_names).empty?
148
65
  @templates[entry_name] = entry
149
66
  end
150
67
  delete_if do |entry_name, fixture|
151
68
  @templates.has_key?(entry_name)
152
69
  end
153
70
  end
154
-
155
- def make_entry(entry_name, entry, add_entry_on_complete)
156
- @@entry_stack << entry.dup
71
+
72
+ def make_entry(entry_name, entry_template, add_entry_on_complete)
73
+ raise NoEntryNameError.new(self, entry_name, entry_template) unless entry_template.kind_of?(Hash)
157
74
 
158
- attributes = config[:attributes]
159
- #modules = config[:modules] # FUTURE! bring back if you start running methods
160
-
161
- entry = attributes.merge(entry)
75
+ entry = attributes.merge(entry_template)
162
76
  entry.extend JoinFix
163
77
  entry.entry_name = entry_name
164
78
 
165
- # FUTURE! bring back if you start running methods
166
- #modules.each do |module_name|
167
- # mod = get_module(module_name, module_options)
168
- # template.extend(mod) if mod
169
- #end
170
-
171
79
  # extract templates for entries that will be joined to the current entry
172
80
  # Associated entries are indicated by any key undeclared in attributes, that also does not
173
81
  # match one of the reseved key patterns... ie 'id' or keys ending in '_id'. Additionally includes
174
82
  # join references, which will all be arrays.
175
- associated_entries = entry.extract_unless(attributes.keys) do |key, value|
176
- JoinFix.preserves?(key) || key.kind_of?(Array)
83
+ assoc_entries = entry.extract_unless(attributes.keys) do |key, value|
84
+ key == "id" || key.kind_of?(Array)
177
85
  end
178
86
 
179
87
  # also extract templates for entries that have an array value. These entries indicate a set of
180
88
  # associated entries that should each be joined to the current entry
181
- associated_entries.merge( entry.extract_if { |key, value| value.kind_of?(Array) } )
182
- associated_entries.each_pair do |association, association_templates|
183
- # ensure that association_templates is an array of template
184
- association_templates = [association_templates] unless association_templates.kind_of?(Array)
89
+ assoc_entries.merge( entry.extract_if { |key, value| value.kind_of?(Array) } )
90
+ assoc_entries.each_pair do |assoc_name, assoc_array|
91
+ # ensure that assoc_array is an array of template
92
+ assoc_array = [assoc_array] unless assoc_array.kind_of?(Array)
185
93
 
186
94
  # translate the association configuration into a join configuration
187
- join_config = join_configs[association]
188
- unless join_config
189
- raise ArgumentError, "Unknown association '#{association}' for '#{table_name}'."
190
- end
191
-
95
+ join_config = join_config(assoc_name)
192
96
  join_table = join_config[:join_table]
193
97
  macro = join_config[:macro]
194
98
 
@@ -197,22 +101,25 @@ class Fixtures
197
101
  if join_config[:polymorphic] == true
198
102
  join_config = join_config.dup
199
103
 
200
- associable_type = "#{join_config[:associable]}_type"
201
- unless entry.has_key?(associable_type)
202
- raise ArgumentError, "Polymorphic type '#{associable_type}' missing in entry '#{entry_name}' for '#{table_name}'."
203
- end
204
-
104
+ associable_type = join_config[:associable_type]
205
105
  associable_class = entry[associable_type]
106
+
107
+ unless associable_class
108
+ raise MissingPolymorphicTypeError.new(self, entry_name, entry_template,
109
+ "No <#{associable_type}> was specified.")
110
+ end
111
+
206
112
  join_config[:child_class] = associable_class
207
- join_config[:child_table] = associable_class.constantize.table_name
113
+ join_config[:child_table] = Object.const_get(associable_class).table_name
208
114
  end
209
115
 
210
116
  # raise an error if the macro doesn't allow for multiple joins, but multiple associated entries are specified
211
- unless association_templates.length == 1 || JoinFix.macro_allows_multiple(macro)
212
- raise ArgumentError, "Multiple associated entries specified for the single-association macro '#{macro}' in table '#{table_name}'."
117
+ unless assoc_array.length == 1 || JoinFix.macro_allows_multiple(macro)
118
+ raise MultipleChildrenError.new(self, entry_name, entry_template,
119
+ "Multiple joined entries specified for the single-join macro '#{macro}'.")
213
120
  end
214
121
 
215
- association_templates.each do |template|
122
+ assoc_array.each do |template|
216
123
  # template is a reference to another entry
217
124
  template = {template => JoinFix.new(template)} if template.kind_of?(String)
218
125
 
@@ -229,14 +136,13 @@ class Fixtures
229
136
  # Note: join is sent through the make_entry machinery before adding... this ensures that the
230
137
  # join entry will be processed according to the wizard modules specified for the join table.
231
138
  child_fixture.add_entry(child_name, child)
232
- Fixtures.fixture(join_table).make_entry(join_name(entry_name, child_name), join, true) if join
139
+ Fixtures.fixture(join_table).make_entry(join.entry_name, join, true) if join
233
140
  end
234
141
  end
235
142
  end
236
143
 
237
144
  # add the entry if flagged and return the entry
238
145
  add_entry(entry_name, entry) if add_entry_on_complete
239
- @@entry_stack.pop
240
146
  entry
241
147
  end
242
148
 
@@ -245,27 +151,96 @@ class Fixtures
245
151
  #return unless entry.addable?
246
152
 
247
153
  # remove any attributes that are equal to their default
248
- attributes = config[:attributes]
249
154
  entry.delete_if do |attribute, value|
250
155
  default = attributes[attribute]
251
156
  value == default
252
157
  end
253
158
 
254
159
  # create a new fixture if one by the entry_name doesn't exist
255
- existing = self[entry_name] ||= Fixture.new({}, config[:class_name])
160
+ existing = self[entry_name] ||= Fixture.new({}, klass.class_name)
256
161
 
257
162
  # merge new data, checking for data collissions
258
163
  entry.each_pair do |attribute, value|
259
164
  if existing.has_key?(attribute) && existing[attribute] != value
260
- raise ArgumentError,
261
- "Data collision: #{@table_name}(:#{entry_name}).#{attribute}\n" +
262
- "<#{existing[attribute]}> is not equal to\n<#{value}>"
165
+ raise EntryCollisionError.new(self, entry_name, entry,
166
+ "<#{value}> is not equal to the existing '#{attribute}'\n<#{existing[attribute]}>")
263
167
  end
264
168
  existing[attribute] = value
265
169
  end
266
170
  end
171
+ end
172
+
173
+ class MakeEntryError < RuntimeError # :nodoc:
174
+ attr_reader :fixtures, :entry_name, :entry, :msg
175
+ attr_accessor :advice
267
176
 
268
- def join_name(entry_name, child_name)
269
- entry_name < child_name ? "#{entry_name}_#{child_name}" : "#{child_name}_#{entry_name}"
177
+ def initialize(fixtures, entry_name, entry, msg=nil)
178
+ @fixtures = fixtures
179
+ @entry_name = entry_name
180
+ @entry = entry
181
+ @msg =msg
182
+ end
183
+
184
+ def message
185
+ "Error making <#{fixtures.klass.table_name}(:#{entry_name})> in <#{fixtures.fixture_path}>.\n" +
186
+ {entry_name => entry}.to_yaml +
187
+ (msg.nil? ? '' : "\n#{msg}\n") +
188
+ (advice.nil? ? '' : "#{advice}\n")
189
+ end
190
+ end
191
+
192
+ class EntryCollisionError < MakeEntryError # :nodoc:
193
+ end
194
+
195
+ class NoEntryNameError < MakeEntryError # :nodoc:
196
+ def advice
197
+ %Q{
198
+ This error occurs when an entry is not named as in:
199
+ ---
200
+ dog:
201
+ title: Dog
202
+ author:
203
+ # <an entry name like 'ferlinghetti' is missing here>
204
+ full_name: Lawrence Ferlinghetti
205
+ ...}
206
+ end
207
+ end
208
+
209
+ class MultipleChildrenError < MakeEntryError # :nodoc:
210
+ def advice
211
+ %Q{
212
+ Single entry joins should specify a single entry, not an array of entries.
213
+ Use a different association if you need multiple joined entries.
214
+ ---
215
+ poem:
216
+ title: Slave's Dream
217
+ author:
218
+ longfellow:
219
+ full_name: Henry Wadsworth Longfellow}
220
+ end
221
+ end
222
+
223
+ class MissingPolymorphicTypeError < MakeEntryError # :nodoc:
224
+ def advice
225
+ %Q{
226
+ When specifying a belongs_to :polymorphic join, the type
227
+ of the joined entry must be specified because it cannot be
228
+ inferred from association itself. Use something like:
229
+ --
230
+ book_I_read:
231
+ opinion: Great!
232
+ readable_type: Book
233
+ readable:
234
+ the_jungle_books:
235
+ author: Rudyard Kipling
236
+ title: The Jungle Books
237
+
238
+ poem_I_read:
239
+ opinion: Essential!
240
+ readable_type: Poem
241
+ readable:
242
+ sea_fever:
243
+ poet: John Masefield
244
+ title: Sea-Fever}
270
245
  end
271
246
  end
@@ -23,25 +23,29 @@ class Fixtures
23
23
  # not yet been loaded.
24
24
  def fixture(table_name)
25
25
  fixture = all_loaded_fixtures[table_name.to_s]
26
- raise ArgumentError, "No fixture loaded for: #{table_name}" unless fixture
26
+ raise MissingFixtureError.new(table_name) unless fixture
27
27
  fixture
28
28
  end
29
29
 
30
30
  protected
31
31
 
32
+ # Sets the primary key in each of the input fixtures, beginning one-past fixtures.offset
33
+ # (generally this is 1). If a fixture already has an id assigned, it will be skipped.
32
34
  def index_fixtures(fixtures)
35
+ id = fixtures.klass.primary_key
36
+
33
37
  # first find entries with an id and record the ids so that they will be skipped
34
38
  skip_indicies = []
35
39
  fixtures.each_pair do |name, fixture|
36
- skip_indicies << fixture["id"].to_i if fixture.has_key?("id")
40
+ skip_indicies << fixture[id].to_i if fixture.has_key?(id)
37
41
  end
38
42
 
39
43
  # next find and index entries that do not have an id defined
40
44
  index = fixtures.offset
41
45
  fixtures.each_pair do |name, fixture|
42
46
  # skip entries that already have an id defined
43
- next if fixture.has_key?("id")
44
-
47
+ next if fixture.has_key?(id)
48
+
45
49
  # find the next available index
46
50
  # note this must happen before the id assignment,
47
51
  # in case index 1 is marked for skipping
@@ -50,12 +54,13 @@ class Fixtures
50
54
  break unless skip_indicies.include?(index)
51
55
  end
52
56
 
53
- fixture["id"] = index
57
+ fixture[id] = index
54
58
  end
55
59
  end
56
60
 
61
+ # Resolves each join reference in the input fixtures
57
62
  def resolve_references(fixtures)
58
- fixtures.each_pair do |name, fixture|
63
+ fixtures.each_pair do |entry_name, fixture|
59
64
  # search the fixture for join references
60
65
  fixture.each_pair do |join_ref, join_name|
61
66
  # next if the key isn't a join reference
@@ -63,35 +68,38 @@ class Fixtures
63
68
 
64
69
  foreign_key = join_ref.first
65
70
  join_table_name = join_ref.last
66
-
67
- # next if the foreign key is already defined
68
- next if fixture.has_key?(foreign_key)
69
-
70
- begin
71
- # raise an error if the join table isn't loaded; the reference cannot be resolved
72
- unless Fixtures.all_loaded_fixtures.has_key?(join_table_name)
73
- raise ArgumentError, "The join table '#{join_table_name}' has not been loaded."
74
- end
75
-
76
- join_fixtures = Fixtures.all_loaded_fixtures[join_table_name]
71
+
72
+ # raise an error if the join table isn't loaded; the reference cannot be resolved
73
+ unless Fixtures.all_loaded_fixtures.has_key?(join_table_name)
74
+ raise ResolveJoinReferenceError.new(fixtures, entry_name, join_table_name, join_name,
75
+ "The join table '#{join_table_name}' has not been loaded.")
76
+ end
77
77
 
78
- # raise an error if the join entry isn't in the join table; the reference cannot be resolved
79
- unless join_fixtures.has_key?(join_name)
80
- raise ArgumentError, "The join entry '#{join_name}' doesn't exist in '#{join_table_name}'."
81
- end
78
+ join_fixtures = Fixtures.all_loaded_fixtures[join_table_name]
79
+ id = join_fixtures.klass.primary_key
80
+
81
+ # raise an error if the join entry isn't in the join table; the reference cannot be resolved
82
+ unless join_fixtures.has_key?(join_name)
83
+ raise ResolveJoinReferenceError.new(fixtures, entry_name, join_table_name, join_name,
84
+ "The join entry '#{join_name}' doesn't exist in '#{join_table_name}'.")
85
+ end
82
86
 
83
- join_entry = join_fixtures[join_name]
87
+ join_entry = join_fixtures[join_name]
84
88
 
85
- # raise an exception if a join_id was not found
86
- unless join_entry.has_key?("id")
87
- raise ArgumentError, "No id present in join entry '#{join_name}'."
88
- end
89
- rescue
90
- raise ArgumentError, "Cannot resolve reference '#{join_reference} => #{join_name}' in '#{@table_name}.#{name}'. #{$!}"
89
+ # raise an exception if a join_id was not found
90
+ unless join_entry.has_key?(id)
91
+ raise ResolveJoinReferenceError.new(fixtures, entry_name, join_table_name, join_name,
92
+ "No #{id} present in join entry '#{join_name}'.")
93
+ end
94
+
95
+ # raise an exception if the foreign key is already set
96
+ unless fixture[foreign_key].nil?
97
+ raise ForeignKeySetError.new(fixtures, entry_name, join_table_name, join_name,
98
+ "Foreign key <#{foreign_key}> is already set!")
91
99
  end
92
-
100
+
93
101
  # set the join id
94
- fixture[foreign_key] = join_entry["id"]
102
+ fixture[foreign_key] = join_entry[id]
95
103
  end
96
104
 
97
105
  # delete the join references
@@ -100,3 +108,60 @@ class Fixtures
100
108
  end
101
109
  end
102
110
  end
111
+
112
+ class MissingFixtureError < RuntimeError # :nodoc:
113
+ attr_reader :table_name
114
+ def initialize(table_name)
115
+ @table_name = table_name
116
+ end
117
+
118
+ def message
119
+ "No fixture loaded for <#{table_name}>\n" +
120
+ (advice.nil? ? '' : "#{advice}\n")
121
+ end
122
+
123
+ def advice
124
+ %Q{}
125
+ end
126
+ end
127
+
128
+ class ResolveJoinReferenceError < RuntimeError # :nodoc:
129
+ attr_reader :fixtures, :entry_name, :join_table_name, :join_name, :msg
130
+ attr_accessor :advice
131
+
132
+ def initialize(fixtures, entry_name, join_table_name, join_name, msg=nil)
133
+ @fixtures = fixtures
134
+ @entry_name = entry_name
135
+ @join_table_name = join_table_name
136
+ @join_name = join_name
137
+ @msg =msg
138
+ end
139
+
140
+ def message
141
+ "Cannot resolve reference to <#{join_table_name}(:#{join_name})> " +
142
+ "for <#{fixtures.klass.table_name}(:#{entry_name})> " +
143
+ "in <#{fixtures.fixture_path}>.\n" +
144
+ (msg.nil? ? '' : "\n#{msg}\n") +
145
+ (advice.nil? ? '' : "#{advice}\n")
146
+ end
147
+ end
148
+
149
+ class ForeignKeySetError < ResolveJoinReferenceError # :nodoc:
150
+ def advice
151
+ %Q{
152
+ This error occurs when you specifiy the foreign key, as well as a join entry.
153
+ ---
154
+ poem:
155
+ title: Poetry of Departures
156
+ author_id: 8
157
+ author: larkin
158
+
159
+ If you need to specify the foreign key, do so within the entry.
160
+ ---
161
+ poem:
162
+ title: Poetry of Departures
163
+ author:
164
+ larkin:
165
+ id: 8}
166
+ end
167
+ end