datamapper 0.2.3 → 0.2.4
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/example.rb +5 -5
- data/lib/data_mapper/adapters/abstract_adapter.rb +2 -2
- data/lib/data_mapper/adapters/data_object_adapter.rb +141 -147
- data/lib/data_mapper/adapters/mysql_adapter.rb +14 -1
- data/lib/data_mapper/adapters/postgresql_adapter.rb +123 -18
- data/lib/data_mapper/adapters/sql/coersion.rb +21 -9
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +36 -19
- data/lib/data_mapper/adapters/sql/mappings/column.rb +111 -17
- data/lib/data_mapper/adapters/sql/mappings/schema.rb +27 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +256 -29
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +93 -8
- data/lib/data_mapper/associations/belongs_to_association.rb +53 -54
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +157 -25
- data/lib/data_mapper/associations/has_many_association.rb +45 -15
- data/lib/data_mapper/associations/has_n_association.rb +79 -20
- data/lib/data_mapper/associations/has_one_association.rb +2 -2
- data/lib/data_mapper/associations/reference.rb +1 -1
- data/lib/data_mapper/auto_migrations.rb +40 -0
- data/lib/data_mapper/base.rb +201 -98
- data/lib/data_mapper/context.rb +16 -10
- data/lib/data_mapper/database.rb +22 -11
- data/lib/data_mapper/dependency_queue.rb +28 -0
- data/lib/data_mapper/embedded_value.rb +61 -17
- data/lib/data_mapper/property.rb +4 -0
- data/lib/data_mapper/support/active_record_impersonation.rb +13 -5
- data/lib/data_mapper/support/errors.rb +5 -0
- data/lib/data_mapper/support/serialization.rb +8 -4
- data/lib/data_mapper/validatable_extensions/errors.rb +12 -0
- data/lib/data_mapper/validatable_extensions/macros.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validatable_instance_methods.rb +62 -0
- data/lib/data_mapper/validatable_extensions/validation_base.rb +18 -0
- data/lib/data_mapper/validatable_extensions/validations/formats/email.rb +43 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_acceptance_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_confirmation_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_each.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_format_of.rb +28 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_length_of.rb +15 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_numericality_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_presence_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_true_for.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +33 -0
- data/lib/data_mapper/validations.rb +20 -0
- data/lib/data_mapper.rb +39 -34
- data/performance.rb +24 -18
- data/plugins/dataobjects/do_rb +0 -0
- data/rakefile.rb +12 -2
- data/spec/active_record_impersonation_spec.rb +133 -0
- data/spec/acts_as_tree_spec.rb +25 -9
- data/spec/associations_spec.rb +124 -4
- data/spec/attributes_spec.rb +13 -0
- data/spec/auto_migrations_spec.rb +44 -0
- data/spec/base_spec.rb +189 -1
- data/spec/column_spec.rb +85 -7
- data/spec/conditions_spec.rb +2 -2
- data/spec/dependency_spec.rb +25 -0
- data/spec/embedded_value_spec.rb +123 -3
- data/spec/fixtures/animals.yaml +1 -0
- data/spec/fixtures/careers.yaml +5 -0
- data/spec/fixtures/comments.yaml +1 -0
- data/spec/fixtures/people.yaml +14 -9
- data/spec/fixtures/projects.yaml +4 -0
- data/spec/fixtures/sections.yaml +5 -0
- data/spec/fixtures/serializers.yaml +6 -0
- data/spec/fixtures/users.yaml +1 -0
- data/spec/load_command_spec.rb +5 -4
- data/spec/mock_adapter.rb +2 -2
- data/spec/models/animal.rb +2 -1
- data/spec/models/animals_exhibit.rb +2 -2
- data/spec/models/career.rb +6 -0
- data/spec/models/comment.rb +4 -0
- data/spec/models/exhibit.rb +4 -0
- data/spec/models/person.rb +3 -13
- data/spec/models/project.rb +1 -1
- data/spec/models/serializer.rb +3 -0
- data/spec/models/user.rb +4 -0
- data/spec/models/zoo.rb +8 -1
- data/spec/natural_key_spec.rb +36 -0
- data/spec/paranoia_spec.rb +36 -0
- data/spec/property_spec.rb +70 -0
- data/spec/schema_spec.rb +10 -2
- data/spec/serialization_spec.rb +6 -3
- data/spec/serialize_spec.rb +19 -0
- data/spec/single_table_inheritance_spec.rb +7 -1
- data/spec/spec_helper.rb +26 -8
- data/spec/table_spec.rb +33 -0
- data/spec/validates_confirmation_of_spec.rb +20 -4
- data/spec/validates_format_of_spec.rb +22 -8
- data/spec/validates_length_of_spec.rb +26 -13
- data/spec/validates_uniqueness_of_spec.rb +18 -5
- data/spec/validations_spec.rb +55 -10
- data/tasks/fixtures.rb +13 -7
- metadata +189 -153
- data/lib/data_mapper/validations/confirmation_validator.rb +0 -53
- data/lib/data_mapper/validations/contextual_validations.rb +0 -50
- data/lib/data_mapper/validations/format_validator.rb +0 -85
- data/lib/data_mapper/validations/formats/email.rb +0 -78
- data/lib/data_mapper/validations/generic_validator.rb +0 -22
- data/lib/data_mapper/validations/length_validator.rb +0 -76
- data/lib/data_mapper/validations/required_field_validator.rb +0 -41
- data/lib/data_mapper/validations/unique_validator.rb +0 -56
- data/lib/data_mapper/validations/validation_errors.rb +0 -37
- data/lib/data_mapper/validations/validation_helper.rb +0 -77
- data/plugins/dataobjects/REVISION +0 -1
- data/plugins/dataobjects/Rakefile +0 -9
- data/plugins/dataobjects/do.rb +0 -348
- data/plugins/dataobjects/do_mysql.rb +0 -212
- data/plugins/dataobjects/do_postgres.rb +0 -196
- data/plugins/dataobjects/do_sqlite3.rb +0 -157
- data/plugins/dataobjects/spec/do_spec.rb +0 -150
- data/plugins/dataobjects/spec/spec_helper.rb +0 -81
- data/plugins/dataobjects/swig_mysql/extconf.rb +0 -45
- data/plugins/dataobjects/swig_mysql/mysql_c.c +0 -16602
- data/plugins/dataobjects/swig_mysql/mysql_c.i +0 -67
- data/plugins/dataobjects/swig_mysql/mysql_supp.i +0 -46
- data/plugins/dataobjects/swig_postgres/extconf.rb +0 -29
- data/plugins/dataobjects/swig_postgres/postgres_c.c +0 -8185
- data/plugins/dataobjects/swig_postgres/postgres_c.i +0 -73
- data/plugins/dataobjects/swig_sqlite/extconf.rb +0 -9
- data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +0 -4725
- data/plugins/dataobjects/swig_sqlite/sqlite_c.i +0 -168
- data/tasks/drivers.rb +0 -20
@@ -3,23 +3,27 @@ module DataMapper
|
|
3
3
|
|
4
4
|
class HasAndBelongsToManyAssociation
|
5
5
|
|
6
|
-
attr_reader :adapter
|
6
|
+
attr_reader :adapter
|
7
7
|
|
8
8
|
def initialize(klass, association_name, options)
|
9
9
|
@adapter = database.adapter
|
10
|
-
@
|
10
|
+
@key_table = adapter.table(klass)
|
11
11
|
@association_name = association_name.to_sym
|
12
12
|
@options = options
|
13
13
|
|
14
14
|
define_accessor(klass)
|
15
15
|
end
|
16
16
|
|
17
|
+
# def key_table
|
18
|
+
# @key_table
|
19
|
+
# end
|
20
|
+
|
17
21
|
def name
|
18
22
|
@association_name
|
19
23
|
end
|
20
24
|
|
21
25
|
def foreign_name
|
22
|
-
@foreign_name || (@foreign_name = (@options[:foreign_name] || @
|
26
|
+
@foreign_name || (@foreign_name = (@options[:foreign_name] || @key_table.name).to_sym)
|
23
27
|
end
|
24
28
|
|
25
29
|
def constant
|
@@ -38,23 +42,27 @@ module DataMapper
|
|
38
42
|
|
39
43
|
end
|
40
44
|
end
|
45
|
+
|
46
|
+
def activate!
|
47
|
+
join_table.create!
|
48
|
+
end
|
41
49
|
|
42
|
-
def
|
43
|
-
|
50
|
+
def associated_columns
|
51
|
+
associated_table.columns.reject { |column| column.lazy? } + join_columns
|
44
52
|
end
|
45
53
|
|
46
54
|
def join_columns
|
47
55
|
[ left_foreign_key, right_foreign_key ]
|
48
56
|
end
|
49
57
|
|
50
|
-
def
|
51
|
-
@
|
58
|
+
def associated_table
|
59
|
+
@associated_table || (@associated_table = adapter.table(constant))
|
52
60
|
end
|
53
61
|
|
54
62
|
def join_table
|
55
63
|
@join_table || @join_table = begin
|
56
64
|
join_table_name = @options[:join_table] ||
|
57
|
-
[
|
65
|
+
[ @key_table.name.to_s, database.schema[constant].name.to_s ].sort.join('_')
|
58
66
|
|
59
67
|
adapter.table(join_table_name)
|
60
68
|
end
|
@@ -63,32 +71,55 @@ module DataMapper
|
|
63
71
|
def left_foreign_key
|
64
72
|
@left_foreign_key || @left_foreign_key = begin
|
65
73
|
join_table.add_column(
|
66
|
-
(@options[:left_foreign_key] ||
|
67
|
-
:integer,
|
74
|
+
(@options[:left_foreign_key] || @key_table.default_foreign_key),
|
75
|
+
:integer, :key => true)
|
68
76
|
end
|
69
77
|
end
|
70
78
|
|
71
79
|
def right_foreign_key
|
72
80
|
@right_foreign_key || @right_foreign_key = begin
|
73
81
|
join_table.add_column(
|
74
|
-
(@options[:right_foreign_key] ||
|
75
|
-
:integer,
|
82
|
+
(@options[:right_foreign_key] || associated_table.default_foreign_key),
|
83
|
+
:integer, :key => true)
|
76
84
|
end
|
77
85
|
end
|
78
86
|
|
79
87
|
def to_sql
|
80
88
|
<<-EOS.compress_lines
|
81
89
|
JOIN #{join_table.to_sql} ON
|
82
|
-
#{left_foreign_key.to_sql(true)} = #{
|
83
|
-
JOIN #{
|
84
|
-
#{
|
90
|
+
#{left_foreign_key.to_sql(true)} = #{@key_table.key.to_sql(true)}
|
91
|
+
JOIN #{associated_table.to_sql} ON
|
92
|
+
#{associated_table.key.to_sql(true)} = #{right_foreign_key.to_sql(true)}
|
85
93
|
EOS
|
86
94
|
end
|
87
95
|
|
88
96
|
def to_shallow_sql
|
89
97
|
<<-EOS.compress_lines
|
90
98
|
JOIN #{join_table.to_sql} ON
|
91
|
-
#{left_foreign_key.to_sql(true)} = #{
|
99
|
+
#{left_foreign_key.to_sql(true)} = #{@key_table.key.to_sql(true)}
|
100
|
+
EOS
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_insert_sql
|
104
|
+
<<-EOS.compress_lines
|
105
|
+
INSERT INTO #{join_table.to_sql}
|
106
|
+
(#{left_foreign_key.to_sql}, #{right_foreign_key.to_sql})
|
107
|
+
VALUES
|
108
|
+
EOS
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_delete_sql
|
112
|
+
<<-EOS
|
113
|
+
DELETE FROM #{join_table.to_sql}
|
114
|
+
WHERE #{left_foreign_key.to_sql} = ?
|
115
|
+
EOS
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_delete_member_sql
|
119
|
+
<<-EOS
|
120
|
+
DELETE FROM #{join_table.to_sql}
|
121
|
+
WHERE #{left_foreign_key.to_sql} = ?
|
122
|
+
AND #{right_foreign_key.to_sql} = ?
|
92
123
|
EOS
|
93
124
|
end
|
94
125
|
|
@@ -105,16 +136,13 @@ module DataMapper
|
|
105
136
|
EOS
|
106
137
|
end
|
107
138
|
|
108
|
-
class Set
|
139
|
+
class Set < Associations::Reference
|
109
140
|
|
110
141
|
include Enumerable
|
111
142
|
|
112
|
-
def initialize(
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
def association
|
117
|
-
@association || (@association = @instance.session.schema[@instance.class].associations[@association_name])
|
143
|
+
def initialize(*args)
|
144
|
+
super
|
145
|
+
@new_members = false
|
118
146
|
end
|
119
147
|
|
120
148
|
def each
|
@@ -125,7 +153,11 @@ module DataMapper
|
|
125
153
|
entries.size
|
126
154
|
end
|
127
155
|
alias length size
|
128
|
-
|
156
|
+
|
157
|
+
def count
|
158
|
+
entries.size
|
159
|
+
end
|
160
|
+
|
129
161
|
def [](key)
|
130
162
|
entries[key]
|
131
163
|
end
|
@@ -134,6 +166,106 @@ module DataMapper
|
|
134
166
|
entries.empty?
|
135
167
|
end
|
136
168
|
|
169
|
+
def dirty?
|
170
|
+
@new_members || (@entries && @entries.any? { |item| item != @instance && item.dirty? })
|
171
|
+
end
|
172
|
+
|
173
|
+
def validate_recursively(event, cleared)
|
174
|
+
@entries.blank? || @entries.all? { |item| cleared.include?(item) || item.validate_recursively(event, cleared) }
|
175
|
+
end
|
176
|
+
|
177
|
+
def save_without_validation(database_context)
|
178
|
+
unless @entries.nil?
|
179
|
+
|
180
|
+
if @new_members || dirty?
|
181
|
+
adapter = @instance.database_context.adapter
|
182
|
+
|
183
|
+
adapter.connection do |db|
|
184
|
+
command = db.create_command(association.to_delete_sql)
|
185
|
+
command.execute_non_query(@instance.key)
|
186
|
+
end
|
187
|
+
|
188
|
+
unless @entries.empty?
|
189
|
+
if adapter.batch_insertable?
|
190
|
+
sql = association.to_insert_sql
|
191
|
+
values = []
|
192
|
+
keys = []
|
193
|
+
|
194
|
+
@entries.each do |member|
|
195
|
+
adapter.save_without_validation(database_context, member)
|
196
|
+
values << "(?, ?)"
|
197
|
+
keys << @instance.key << member.key
|
198
|
+
end
|
199
|
+
|
200
|
+
adapter.connection do |db|
|
201
|
+
command = db.create_command(sql << ' ' << values.join(', '))
|
202
|
+
command.execute_non_query(*keys)
|
203
|
+
end
|
204
|
+
|
205
|
+
else # adapter doesn't support batch inserts...
|
206
|
+
@entries.each do |member|
|
207
|
+
adapter.save_without_validation(database_context, member)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Just to keep the same flow as the batch-insert mode.
|
211
|
+
@entries.each do |member|
|
212
|
+
adapter.connection do |db|
|
213
|
+
command = db.create_command("#{association.to_insert_sql} (?, ?)")
|
214
|
+
command.execute_non_query(@instance.key, member.key)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end # if adapter.batch_insertable?
|
218
|
+
end # unless @entries.empty?
|
219
|
+
|
220
|
+
@new_members = false
|
221
|
+
end # if @new_members || dirty?
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def <<(member)
|
226
|
+
@new_members = true
|
227
|
+
entries << member
|
228
|
+
end
|
229
|
+
|
230
|
+
def clear
|
231
|
+
@new_members = true
|
232
|
+
@entries = []
|
233
|
+
end
|
234
|
+
|
235
|
+
def reload!
|
236
|
+
@new_members = false
|
237
|
+
@entries = nil
|
238
|
+
end
|
239
|
+
|
240
|
+
def delete(member)
|
241
|
+
@new_members = true
|
242
|
+
if entries.delete(member)
|
243
|
+
@instance.database_context.adapter.connection do |db|
|
244
|
+
command = db.create_command(association.to_delete_member_sql)
|
245
|
+
command.execute_non_query(@instance.key, member.key)
|
246
|
+
end
|
247
|
+
member
|
248
|
+
else
|
249
|
+
nil
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def method_missing(symbol, *args, &block)
|
254
|
+
if entries.respond_to?(symbol)
|
255
|
+
entries.send(symbol, *args, &block)
|
256
|
+
elsif association.associated_table.associations.any? { |assoc| assoc.name == symbol }
|
257
|
+
results = []
|
258
|
+
each do |item|
|
259
|
+
unless (val = item.send(symbol)).blank?
|
260
|
+
results << (val.is_a?(Enumerable) ? val.entries : val)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
results.flatten
|
264
|
+
else
|
265
|
+
super
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
137
269
|
def entries
|
138
270
|
@entries || @entries = begin
|
139
271
|
|
@@ -163,7 +295,7 @@ module DataMapper
|
|
163
295
|
end
|
164
296
|
end
|
165
297
|
|
166
|
-
@instance.
|
298
|
+
@instance.database_context.all(association.constant,
|
167
299
|
left_foreign_key => @instance.loaded_set.map(&:key),
|
168
300
|
:shallow_include => association.foreign_name,
|
169
301
|
:intercept_load => matcher
|
@@ -18,25 +18,39 @@ module DataMapper
|
|
18
18
|
EOS
|
19
19
|
end
|
20
20
|
|
21
|
+
def to_disassociate_sql
|
22
|
+
"UPDATE #{associated_table.to_sql} SET #{foreign_key_column.to_sql} = NULL WHERE #{foreign_key_column.to_sql} = ?"
|
23
|
+
end
|
24
|
+
|
21
25
|
class Set < Associations::Reference
|
22
26
|
|
23
27
|
include Enumerable
|
24
28
|
|
25
29
|
def dirty?
|
26
|
-
@items && @items.any? { |item| item
|
30
|
+
@items && @items.any? { |item| item.dirty? }
|
27
31
|
end
|
28
32
|
|
29
|
-
def
|
30
|
-
@items.blank? || @items.all? { |item| item.
|
33
|
+
def validate_recursively(event, cleared)
|
34
|
+
@items.blank? || @items.all? { |item| cleared.include?(item) || item.validate_recursively(event, cleared) }
|
31
35
|
end
|
32
36
|
|
33
|
-
def
|
37
|
+
def save_without_validation(database_context)
|
38
|
+
|
39
|
+
adapter = @instance.database_context.adapter
|
40
|
+
|
41
|
+
adapter.connection do |db|
|
42
|
+
command = db.create_command(association.to_disassociate_sql)
|
43
|
+
command.execute_non_query(@instance.key)
|
44
|
+
end
|
45
|
+
|
34
46
|
unless @items.nil? || @items.empty?
|
47
|
+
|
48
|
+
|
35
49
|
setter_method = "#{@association_name}=".to_sym
|
36
|
-
ivar_name = association.
|
50
|
+
ivar_name = association.foreign_key_column.instance_variable_name
|
37
51
|
@items.each do |item|
|
38
52
|
item.instance_variable_set(ivar_name, @instance.key)
|
39
|
-
item
|
53
|
+
@instance.database_context.adapter.save_without_validation(database_context, item)
|
40
54
|
end
|
41
55
|
end
|
42
56
|
end
|
@@ -49,15 +63,27 @@ module DataMapper
|
|
49
63
|
items << associated_item
|
50
64
|
|
51
65
|
# TODO: Optimize!
|
52
|
-
fk = association.
|
53
|
-
foreign_association = association.
|
54
|
-
mapping.is_a?(BelongsToAssociation) && mapping.
|
66
|
+
fk = association.foreign_key_column
|
67
|
+
foreign_association = association.associated_table.associations.find do |mapping|
|
68
|
+
mapping.is_a?(BelongsToAssociation) && mapping.foreign_key_column == fk
|
55
69
|
end
|
56
70
|
|
57
71
|
associated_item.send("#{foreign_association.name}=", @instance) if foreign_association
|
58
72
|
|
59
73
|
return @items
|
60
74
|
end
|
75
|
+
|
76
|
+
def build(options)
|
77
|
+
item = association.associated_constant.new(options)
|
78
|
+
self << item
|
79
|
+
item
|
80
|
+
end
|
81
|
+
|
82
|
+
def create(options)
|
83
|
+
item = build(options)
|
84
|
+
item.save
|
85
|
+
item
|
86
|
+
end
|
61
87
|
|
62
88
|
def set(items)
|
63
89
|
@items = items
|
@@ -66,7 +92,7 @@ module DataMapper
|
|
66
92
|
def method_missing(symbol, *args, &block)
|
67
93
|
if items.respond_to?(symbol)
|
68
94
|
items.send(symbol, *args, &block)
|
69
|
-
elsif association.
|
95
|
+
elsif association.associated_table.associations.any? { |assoc| assoc.name == symbol }
|
70
96
|
results = []
|
71
97
|
each do |item|
|
72
98
|
unless (val = item.send(symbol)).blank?
|
@@ -82,19 +108,23 @@ module DataMapper
|
|
82
108
|
def respond_to?(symbol)
|
83
109
|
items.respond_to?(symbol) || super
|
84
110
|
end
|
111
|
+
|
112
|
+
def reload!
|
113
|
+
@items = nil
|
114
|
+
end
|
85
115
|
|
86
116
|
def items
|
87
117
|
@items || begin
|
88
118
|
if @instance.loaded_set.nil?
|
89
119
|
@items = []
|
90
120
|
else
|
91
|
-
fk = association.
|
121
|
+
fk = association.foreign_key_column.to_sym
|
92
122
|
|
93
|
-
finder_options = { association.
|
123
|
+
finder_options = { association.foreign_key_column.to_sym => @instance.loaded_set.map { |item| item.key } }
|
94
124
|
finder_options.merge!(association.finder_options)
|
95
125
|
|
96
|
-
associated_items = @instance.
|
97
|
-
association.
|
126
|
+
associated_items = @instance.database_context.all(
|
127
|
+
association.associated_constant,
|
98
128
|
finder_options
|
99
129
|
).group_by { |entry| entry.send(fk) }
|
100
130
|
|
@@ -116,4 +146,4 @@ module DataMapper
|
|
116
146
|
end
|
117
147
|
|
118
148
|
end
|
119
|
-
end
|
149
|
+
end
|
@@ -1,9 +1,12 @@
|
|
1
|
-
module DataMapper
|
1
|
+
module DataMapper
|
2
|
+
|
3
|
+
class ForeignKeyNotFoundError < StandardError; end
|
4
|
+
|
2
5
|
module Associations
|
3
6
|
|
4
7
|
class HasNAssociation
|
5
8
|
|
6
|
-
attr_reader :
|
9
|
+
attr_reader :options
|
7
10
|
|
8
11
|
OPTIONS = [
|
9
12
|
:class,
|
@@ -12,12 +15,35 @@ module DataMapper
|
|
12
15
|
]
|
13
16
|
|
14
17
|
def initialize(klass, association_name, options)
|
18
|
+
@constant = klass
|
15
19
|
@adapter = database.adapter
|
16
|
-
@table = adapter.table(klass)
|
20
|
+
@table = @adapter.table(klass)
|
17
21
|
@association_name = association_name.to_sym
|
18
22
|
@options = options || Hash.new
|
19
23
|
|
20
24
|
define_accessor(klass)
|
25
|
+
|
26
|
+
Base::dependencies.add(associated_constant_name) do |klass|
|
27
|
+
@foreign_key_column = associated_table[foreign_key_name]
|
28
|
+
|
29
|
+
unless @foreign_key_column
|
30
|
+
associated_constant.property(foreign_key_name, foreign_key_type)
|
31
|
+
|
32
|
+
@foreign_key_column = associated_table[foreign_key_name]
|
33
|
+
|
34
|
+
if @foreign_key_column.nil?
|
35
|
+
raise ForeignKeyNotFoundError.new(<<-EOS.compress_lines)
|
36
|
+
key_table => #{key_table.inspect},
|
37
|
+
association_table => #{associated_table.inspect},
|
38
|
+
association_name => #{name},
|
39
|
+
foreign_key_name => #{foreign_key_name.inspect},
|
40
|
+
foreign_key_type => #{foreign_key_type.inspect},
|
41
|
+
constant => #{constant.inspect},
|
42
|
+
associated_constant => #{associated_constant.inspect}
|
43
|
+
EOS
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
21
47
|
end
|
22
48
|
|
23
49
|
def name
|
@@ -25,39 +51,72 @@ module DataMapper
|
|
25
51
|
end
|
26
52
|
|
27
53
|
def constant
|
28
|
-
@
|
29
|
-
|
30
|
-
|
31
|
-
|
54
|
+
@constant
|
55
|
+
end
|
56
|
+
|
57
|
+
def associated_constant
|
58
|
+
@associated_constant || @associated_constant = Kernel.const_get(associated_constant_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def associated_constant_name
|
62
|
+
@associated_constant_name || begin
|
63
|
+
|
64
|
+
if @options.has_key?(:class) || @options.has_key?(:class_name)
|
65
|
+
@associated_constant_name = (@options[:class] || @options[:class_name])
|
66
|
+
|
67
|
+
if @associated_constant_name.kind_of?(String)
|
68
|
+
@associated_constant_name = Inflector.classify(@associated_constant_name)
|
69
|
+
elsif @associated_constant_name.kind_of?(Class)
|
70
|
+
@associated_constant_name = @associated_constant_name.name
|
71
|
+
end
|
32
72
|
else
|
33
|
-
|
73
|
+
@associated_constant_name = Inflector.classify(@association_name)
|
34
74
|
end
|
35
|
-
|
36
|
-
|
75
|
+
|
76
|
+
@associated_constant_name
|
37
77
|
end
|
78
|
+
|
38
79
|
end
|
39
80
|
|
40
|
-
def
|
41
|
-
@
|
42
|
-
association_table[@options[:foreign_key] || table.default_foreign_key]
|
43
|
-
end
|
81
|
+
def primary_key_column
|
82
|
+
@primary_key_column || @primary_key_column = key_table.key
|
44
83
|
end
|
45
84
|
|
46
|
-
def
|
47
|
-
@
|
85
|
+
def foreign_key_column
|
86
|
+
@foreign_key_column
|
48
87
|
end
|
49
88
|
|
50
|
-
def
|
51
|
-
|
89
|
+
def foreign_key_name
|
90
|
+
@foreign_key_name || @foreign_key_name = (@options[:foreign_key] || key_table.default_foreign_key)
|
52
91
|
end
|
53
92
|
|
54
|
-
def
|
55
|
-
|
93
|
+
def foreign_key_type
|
94
|
+
@foreign_key_type || @foreign_key_type = key_table.key.type
|
95
|
+
end
|
96
|
+
|
97
|
+
def key_table
|
98
|
+
@key_table || @key_table = @adapter.table(constant)
|
99
|
+
end
|
100
|
+
|
101
|
+
def associated_table
|
102
|
+
@association_table || @association_table = @adapter.table(associated_constant)
|
103
|
+
end
|
104
|
+
|
105
|
+
def associated_columns
|
106
|
+
associated_table.columns.reject { |column| column.lazy? }
|
56
107
|
end
|
57
108
|
|
58
109
|
def finder_options
|
59
110
|
@finder_options || @finder_options = @options.reject { |k,v| self.class::OPTIONS.include?(k) }
|
60
111
|
end
|
112
|
+
|
113
|
+
def to_sql
|
114
|
+
"JOIN #{associated_table.to_sql} ON #{foreign_key_column.to_sql(true)} = #{primary_key_column.to_sql(true)}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def activate!
|
118
|
+
foreign_key_column
|
119
|
+
end
|
61
120
|
end
|
62
121
|
|
63
122
|
end
|
@@ -40,12 +40,12 @@ module DataMapper
|
|
40
40
|
else
|
41
41
|
# Temp variable for the instance variable name.
|
42
42
|
setter_method = "#{@association_name}=".to_sym
|
43
|
-
instance_variable_name = "@#{association.
|
43
|
+
instance_variable_name = "@#{association.foreign_key_column}".to_sym
|
44
44
|
|
45
45
|
set = @instance.loaded_set.group_by { |instance| instance.key }
|
46
46
|
|
47
47
|
# Fetch the foreign objects for all instances in the current object's loaded-set.
|
48
|
-
@instance.
|
48
|
+
@instance.database_context.all(association.associated_constant, association.foreign_key_column => set.keys).each do |assoc|
|
49
49
|
set[assoc.instance_variable_get(instance_variable_name)].first.send(setter_method, assoc)
|
50
50
|
end
|
51
51
|
|
@@ -38,7 +38,7 @@ module DataMapper
|
|
38
38
|
# #association provides lazily initialized access to the declared
|
39
39
|
# Association.
|
40
40
|
def association
|
41
|
-
@association || (@association = @instance.
|
41
|
+
@association || (@association = @instance.database_context.table(@instance.class).associations[@association_name])
|
42
42
|
end
|
43
43
|
|
44
44
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module AutoMigrations
|
3
|
+
def auto_migrate!
|
4
|
+
if self::subclasses.empty?
|
5
|
+
table = database.table(self)
|
6
|
+
table.associations.each do |association|
|
7
|
+
association.activate!
|
8
|
+
end
|
9
|
+
|
10
|
+
table.create!(true)
|
11
|
+
else
|
12
|
+
schema = database.schema
|
13
|
+
columns = self::subclasses.inject(schema[self].columns) do |span, subclass|
|
14
|
+
span + schema[subclass].columns
|
15
|
+
end
|
16
|
+
|
17
|
+
table_name = schema[self].name.to_s
|
18
|
+
table = schema[table_name]
|
19
|
+
columns.each do |column|
|
20
|
+
table.add_column(column.name, column.type, column.options)
|
21
|
+
end
|
22
|
+
|
23
|
+
table.associations.each do |association|
|
24
|
+
association.activate!
|
25
|
+
end
|
26
|
+
|
27
|
+
return table.create!(true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def create_table(table)
|
33
|
+
raise NotImplementedError.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def modify_table(table, columns)
|
37
|
+
raise NotImplementedError.new
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|