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