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.
- data/README +1 -1
- data/lib/joinfix/fixtures.rb +130 -155
- data/lib/joinfix/fixtures_class.rb +95 -30
- data/lib/joinfix.rb +108 -45
- data/rails/log/development.log +71 -0
- data/rails/log/test.log +85 -0
- data/rails/test/test_helper.rb +1 -0
- data/test/belongs_to_polymorphic_test.rb +78 -0
- data/test/belongs_to_test.rb +135 -44
- data/test/belongs_to_with_options_test.rb +57 -0
- data/test/has_and_belongs_to_many_test.rb +36 -36
- data/test/has_and_belongs_to_many_with_options_test.rb +84 -0
- data/test/has_many_as_test.rb +66 -0
- data/test/has_many_test.rb +33 -69
- data/test/has_many_through_test.rb +79 -0
- data/test/has_many_through_with_options_test.rb +85 -0
- data/test/has_many_with_options_test.rb +63 -0
- data/test/has_one_test.rb +26 -27
- data/test/has_one_with_options_test.rb +57 -0
- data/test/joinfix_test.rb +5 -5
- data/test/joinfix_test_helper.rb +76 -16
- data/test/missing_fixture_test.rb +54 -0
- data/test/nested_test.rb +13 -6
- data/test/todo +15 -0
- metadata +48 -51
- data/test/fixtures/as_children.yml +0 -0
- data/test/fixtures/bt_children.yml +0 -0
- data/test/fixtures/bt_parents.yml +0 -30
- data/test/fixtures/habtm_children.yml +0 -0
- data/test/fixtures/habtm_children_habtm_parents.yml +0 -0
- data/test/fixtures/habtm_joins.yml +0 -0
- data/test/fixtures/habtm_parents.yml +0 -18
- data/test/fixtures/hm_children.yml +0 -0
- data/test/fixtures/hm_joins.yml +0 -0
- data/test/fixtures/hm_parents.yml +0 -34
- data/test/fixtures/ho_children.yml +0 -0
- data/test/fixtures/ho_parents.yml +0 -14
- data/test/fixtures/no_join_fixes.yml +0 -4
- data/test/fixtures/omap_no_join_fixes.yml +0 -7
- data/test/fixtures/polymorphic_children.yml +0 -0
data/README
CHANGED
data/lib/joinfix/fixtures.rb
CHANGED
@@ -1,32 +1,32 @@
|
|
1
1
|
class Fixtures
|
2
|
-
attr_reader :
|
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
|
-
|
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
|
45
|
-
|
46
|
-
|
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
|
-
|
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 &
|
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,
|
156
|
-
|
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
|
-
|
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
|
-
|
176
|
-
|
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
|
-
|
182
|
-
|
183
|
-
# ensure that
|
184
|
-
|
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 =
|
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 =
|
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.
|
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
|
212
|
-
raise
|
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
|
-
|
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(
|
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({},
|
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
|
261
|
-
"
|
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
|
269
|
-
|
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
|
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[
|
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?(
|
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[
|
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 |
|
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
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
87
|
+
join_entry = join_fixtures[join_name]
|
84
88
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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[
|
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
|