joinfix 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|