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
@@ -17,17 +17,28 @@ module DataMapper
|
|
17
17
|
@klass_or_name = klass_or_name
|
18
18
|
|
19
19
|
@adapter = adapter
|
20
|
+
|
21
|
+
@temporary = false
|
20
22
|
@columns = SortedSet.new
|
21
23
|
@columns_hash = Hash.new { |h,k| h[k] = columns.find { |c| c.name == k } }
|
22
24
|
|
23
25
|
@associations = AssociationsSet.new
|
24
26
|
|
25
27
|
@multi_class = false
|
28
|
+
@paranoid = false
|
29
|
+
@paranoid_column = nil
|
26
30
|
|
27
31
|
if @klass && @klass.ancestors.include?(DataMapper::Base) && @klass.superclass != DataMapper::Base
|
28
|
-
|
32
|
+
|
33
|
+
super_table = @adapter.table(@klass.superclass)
|
34
|
+
|
35
|
+
super_table.columns.each do |column|
|
29
36
|
self.add_column(column.name, column.type, column.options)
|
30
37
|
end
|
38
|
+
|
39
|
+
super_table.associations.each do |association|
|
40
|
+
@associations << association
|
41
|
+
end
|
31
42
|
end
|
32
43
|
end
|
33
44
|
|
@@ -35,10 +46,30 @@ module DataMapper
|
|
35
46
|
@schema || @schema = @adapter.schema
|
36
47
|
end
|
37
48
|
|
49
|
+
def paranoid?
|
50
|
+
@paranoid
|
51
|
+
end
|
52
|
+
|
53
|
+
def paranoid_column
|
54
|
+
@paranoid_column
|
55
|
+
end
|
56
|
+
|
38
57
|
def multi_class?
|
39
58
|
@multi_class
|
40
59
|
end
|
41
60
|
|
61
|
+
def type_column
|
62
|
+
@type_column
|
63
|
+
end
|
64
|
+
|
65
|
+
def temporary?
|
66
|
+
@temporary
|
67
|
+
end
|
68
|
+
|
69
|
+
def temporary=(value)
|
70
|
+
@temporary = value
|
71
|
+
end
|
72
|
+
|
42
73
|
def associations
|
43
74
|
@associations
|
44
75
|
end
|
@@ -49,35 +80,124 @@ module DataMapper
|
|
49
80
|
|
50
81
|
def columns
|
51
82
|
key if @key.nil?
|
52
|
-
|
83
|
+
class << self
|
84
|
+
attr_reader :columns
|
85
|
+
end
|
86
|
+
|
87
|
+
self.columns
|
53
88
|
end
|
54
89
|
|
55
90
|
def exists?
|
56
|
-
@adapter.
|
91
|
+
@adapter.connection do |db|
|
92
|
+
command = db.create_command(to_exists_sql)
|
93
|
+
command.execute_reader(name, schema.name) do |reader|
|
94
|
+
reader.has_rows?
|
95
|
+
end
|
96
|
+
end
|
57
97
|
end
|
58
98
|
|
59
99
|
def drop!
|
60
|
-
|
100
|
+
if exists?
|
101
|
+
@adapter.connection do |db|
|
102
|
+
result = db.create_command(to_drop_sql).execute_non_query
|
103
|
+
database.identity_map.clear!(name)
|
104
|
+
schema.delete(self)
|
105
|
+
true
|
106
|
+
end
|
107
|
+
else
|
108
|
+
false
|
109
|
+
end
|
61
110
|
end
|
62
111
|
|
63
112
|
def create!(force = false)
|
64
|
-
|
65
|
-
|
66
|
-
|
113
|
+
if exists?
|
114
|
+
if force
|
115
|
+
drop!
|
116
|
+
create!
|
117
|
+
else
|
118
|
+
false
|
119
|
+
end
|
120
|
+
else
|
121
|
+
@adapter.connection do |db|
|
122
|
+
db.create_command(to_create_sql).execute_non_query
|
123
|
+
schema << self
|
124
|
+
true
|
125
|
+
end
|
67
126
|
end
|
68
127
|
end
|
69
|
-
|
70
|
-
def
|
128
|
+
|
129
|
+
def delete_all!
|
130
|
+
@adapter.connection do |db|
|
131
|
+
db.create_command("DELETE FROM #{to_sql}").execute_non_query
|
132
|
+
end
|
133
|
+
database.identity_map.clear!(name)
|
134
|
+
end
|
135
|
+
|
136
|
+
def truncate!
|
137
|
+
@adapter.connection do |db|
|
138
|
+
result = db.create_command("TRUNCATE TABLE #{to_sql}").execute_non_query
|
139
|
+
database.identity_map.clear!(name)
|
140
|
+
result.to_i > 0
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def count(*args)
|
145
|
+
@adapter.connection do |db|
|
146
|
+
sql = "SELECT COUNT(*) AS row_count FROM #{to_sql}"
|
147
|
+
sql << "WHERE #{args}" unless args.empty?
|
148
|
+
|
149
|
+
command = db.create_command(sql)
|
150
|
+
command.execute_reader do |reader|
|
151
|
+
if reader.has_rows?
|
152
|
+
reader.current_row.first.to_i
|
153
|
+
else
|
154
|
+
0
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def insert(hash)
|
161
|
+
@adapter.connection do |db|
|
162
|
+
|
163
|
+
columns_to_insert = []
|
164
|
+
values = []
|
165
|
+
|
166
|
+
hash.each_pair do |k,v|
|
167
|
+
column = self[k.to_sym]
|
168
|
+
columns_to_insert << (column ? column.to_sql : k)
|
169
|
+
values << v
|
170
|
+
end
|
171
|
+
|
172
|
+
command = db.create_command("INSERT INTO #{to_sql} (#{columns_to_insert.join(', ')}) VALUES (#{values.map { '?' }.join(', ')})")
|
173
|
+
command.execute_non_query(*values)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def key
|
71
178
|
@key || begin
|
72
179
|
@key = @columns.find { |column| column.key? }
|
73
180
|
|
74
181
|
if @key.nil?
|
75
|
-
@key = add_column(:id, :integer, :
|
76
|
-
@klass.send(:attr_reader, :id) unless @klass.methods.include?(:id)
|
182
|
+
@key = add_column(:id, :integer, :serial => true, :ordinal => -1)
|
183
|
+
@klass.send(:attr_reader, :id) unless @klass.nil? || @klass.methods.include?(:id)
|
77
184
|
end
|
78
185
|
|
79
186
|
@key
|
80
187
|
end
|
188
|
+
|
189
|
+
class << self
|
190
|
+
attr_accessor :key
|
191
|
+
end
|
192
|
+
Base::dependencies.resolve!
|
193
|
+
|
194
|
+
self.key
|
195
|
+
end
|
196
|
+
|
197
|
+
def keys
|
198
|
+
@keys || begin
|
199
|
+
@keys = @columns.select { |column| column.key? }
|
200
|
+
end
|
81
201
|
end
|
82
202
|
|
83
203
|
def add_column(column_name, type, options)
|
@@ -91,8 +211,19 @@ module DataMapper
|
|
91
211
|
column = @adapter.class::Mappings::Column.new(@adapter, self, column_name, type, column_ordinal, options)
|
92
212
|
@columns << column
|
93
213
|
|
94
|
-
|
95
|
-
|
214
|
+
if column_name == :type
|
215
|
+
@multi_class = true
|
216
|
+
@type_column = column
|
217
|
+
end
|
218
|
+
|
219
|
+
if column_name.to_s =~ /^deleted\_(at|on)$/
|
220
|
+
@paranoid = true
|
221
|
+
@paranoid_column = column
|
222
|
+
end
|
223
|
+
|
224
|
+
self.flush_sql_caches!
|
225
|
+
@columns_hash.clear
|
226
|
+
|
96
227
|
return column
|
97
228
|
end
|
98
229
|
|
@@ -101,36 +232,77 @@ module DataMapper
|
|
101
232
|
end
|
102
233
|
|
103
234
|
def name
|
104
|
-
@name || @name =
|
105
|
-
@
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
235
|
+
@name || @name = begin
|
236
|
+
if @custom_name
|
237
|
+
@custom_name
|
238
|
+
elsif @klass_or_name.kind_of?(String)
|
239
|
+
@klass_or_name
|
240
|
+
elsif @klass_or_name.kind_of?(Class)
|
241
|
+
if @klass_or_name.superclass != DataMapper::Base \
|
242
|
+
&& @klass_or_name.ancestors.include?(DataMapper::Base)
|
243
|
+
@adapter.table(@klass_or_name.superclass).name
|
244
|
+
else
|
245
|
+
Inflector.tableize(@klass_or_name.name)
|
246
|
+
end
|
110
247
|
else
|
111
|
-
|
248
|
+
raise "+klass_or_name+ (#{@klass_or_name.inspect}) must be a Class or a string containing the name of a table"
|
112
249
|
end
|
113
|
-
else
|
114
|
-
raise "+klass_or_name+ (#{@klass_or_name.inspect}) must be a Class or a string containing the name of a table"
|
115
250
|
end.freeze
|
116
251
|
end
|
117
252
|
|
118
253
|
def name=(value)
|
119
|
-
|
254
|
+
flush_sql_caches!
|
255
|
+
@custom_name = value
|
256
|
+
self.name
|
120
257
|
end
|
121
258
|
|
122
259
|
def default_foreign_key
|
123
|
-
@default_foreign_key || (@default_foreign_key = "#{Inflector.underscore(Inflector.singularize(name))}
|
260
|
+
@default_foreign_key || (@default_foreign_key = "#{Inflector.underscore(Inflector.singularize(name))}_#{key.name}".freeze)
|
124
261
|
end
|
125
262
|
|
126
263
|
def to_sql
|
127
264
|
@to_sql || @to_sql = quote_table.freeze
|
128
265
|
end
|
129
266
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
267
|
+
def to_s
|
268
|
+
name.to_s
|
269
|
+
end
|
270
|
+
|
271
|
+
def unquote_default(default)
|
272
|
+
default
|
273
|
+
end
|
274
|
+
|
275
|
+
def get_database_columns
|
276
|
+
columns = []
|
277
|
+
@adapter.connection do |db|
|
278
|
+
command = db.create_command(to_columns_sql)
|
279
|
+
command.execute_reader(name, schema.name) do |reader|
|
280
|
+
columns = reader.map {
|
281
|
+
@adapter.class::Mappings::Column.new(@adapter, self, reader.item(1),
|
282
|
+
@adapter.class::TYPES.index(reader.item(2)),reader.item(0).to_i,
|
283
|
+
:nullable => reader.item(3).to_i != 99, :default => unquote_default(reader.item(4)))
|
284
|
+
}
|
285
|
+
end
|
133
286
|
end
|
287
|
+
columns
|
288
|
+
end
|
289
|
+
alias_method :database_columns, :get_database_columns
|
290
|
+
|
291
|
+
def to_create_sql
|
292
|
+
@to_create_sql || @to_create_sql = begin
|
293
|
+
sql = "CREATE"
|
294
|
+
sql << " TEMPORARY" if temporary?
|
295
|
+
sql << " TABLE #{to_sql} (#{ columns.map { |c| c.to_long_form }.join(",\n") }"
|
296
|
+
unless keys.blank? || (keys.size == 1 && keys.first.serial?)
|
297
|
+
sql << ", PRIMARY KEY (#{keys.map { |c| c.to_sql }.join(', ') })"
|
298
|
+
end
|
299
|
+
sql << ")"
|
300
|
+
sql.compress_lines
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def to_drop_sql
|
305
|
+
@to_drop_sql || @to_drop_sql = "DROP TABLE #{to_sql}"
|
134
306
|
end
|
135
307
|
|
136
308
|
def to_exists_sql
|
@@ -138,7 +310,28 @@ module DataMapper
|
|
138
310
|
SELECT TABLE_NAME
|
139
311
|
FROM INFORMATION_SCHEMA.TABLES
|
140
312
|
WHERE TABLE_NAME = ?
|
141
|
-
AND
|
313
|
+
AND #{@adapter.database_column_name} = ?
|
314
|
+
EOS
|
315
|
+
end
|
316
|
+
|
317
|
+
def to_column_exists_sql
|
318
|
+
@to_column_exists_sql || @to_column_exists_sql = <<-EOS.compress_lines
|
319
|
+
SELECT TABLE_NAME, COLUMN_NAME
|
320
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
321
|
+
WHERE TABLE_NAME = ?
|
322
|
+
AND COLUMN_NAME = ?
|
323
|
+
AND #{@adapter.database_column_name} = ?
|
324
|
+
EOS
|
325
|
+
end
|
326
|
+
|
327
|
+
def to_columns_sql
|
328
|
+
@to_column_exists_sql || @to_column_exists_sql = <<-EOS.compress_lines
|
329
|
+
SELECT ORDINAL_POSITION, COLUMN_NAME, DATA_TYPE,
|
330
|
+
(CASE IS_NULLABLE WHEN 'NO' THEN 99 ELSE 0 END),
|
331
|
+
COLUMN_DEFAULT
|
332
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
333
|
+
WHERE TABLE_NAME = ?
|
334
|
+
AND #{@adapter.database_column_name} = ?
|
142
335
|
EOS
|
143
336
|
end
|
144
337
|
|
@@ -155,7 +348,41 @@ module DataMapper
|
|
155
348
|
@columns.inspect
|
156
349
|
]
|
157
350
|
end
|
158
|
-
|
351
|
+
|
352
|
+
def flush_sql_caches!(flush_columns = true)
|
353
|
+
@to_column_exists_sql = nil
|
354
|
+
@to_column_exists_sql = nil
|
355
|
+
@to_exists_sql = nil
|
356
|
+
@to_create_sql = nil
|
357
|
+
@to_drop_sql = nil
|
358
|
+
@to_sql = nil
|
359
|
+
@name = nil
|
360
|
+
|
361
|
+
if flush_columns
|
362
|
+
@columns.each do |column|
|
363
|
+
column.send(:flush_sql_caches!)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
true
|
368
|
+
end
|
369
|
+
|
370
|
+
def activate!
|
371
|
+
@activated = true
|
372
|
+
@associations.each do |association|
|
373
|
+
association.activate!
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
def activated?
|
378
|
+
@activated
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
class Schema
|
384
|
+
def to_tables_sql
|
385
|
+
end
|
159
386
|
end
|
160
387
|
|
161
388
|
end
|
@@ -4,7 +4,7 @@ begin
|
|
4
4
|
rescue LoadError
|
5
5
|
STDERR.puts <<-EOS
|
6
6
|
You must install the DataObjects::SQLite3 driver.
|
7
|
-
|
7
|
+
gem install do_sqlite3
|
8
8
|
EOS
|
9
9
|
exit
|
10
10
|
end
|
@@ -18,7 +18,8 @@ module DataMapper
|
|
18
18
|
:integer => 'INTEGER'.freeze,
|
19
19
|
:string => 'TEXT'.freeze,
|
20
20
|
:text => 'TEXT'.freeze,
|
21
|
-
:class => 'TEXT'.freeze
|
21
|
+
:class => 'TEXT'.freeze,
|
22
|
+
:boolean => 'INTEGER'.freeze
|
22
23
|
})
|
23
24
|
|
24
25
|
TABLE_QUOTING_CHARACTER = '"'.freeze
|
@@ -31,22 +32,45 @@ module DataMapper
|
|
31
32
|
return conn
|
32
33
|
end
|
33
34
|
|
34
|
-
def
|
35
|
-
|
36
|
-
session.identity_map.clear!(name)
|
37
|
-
result.to_i > 0
|
35
|
+
def batch_insertable?
|
36
|
+
false
|
38
37
|
end
|
39
|
-
|
38
|
+
|
40
39
|
module Mappings
|
40
|
+
|
41
|
+
class Schema
|
42
|
+
def to_tables_sql
|
43
|
+
@to_tables_sql || @to_tables_sql = <<-EOS.compress_lines
|
44
|
+
SELECT "name"
|
45
|
+
FROM sqlite_master
|
46
|
+
where "type"= "table"
|
47
|
+
and "name" <> "sqlite_sequence"
|
48
|
+
EOS
|
49
|
+
end
|
50
|
+
alias_method :database_tables, :get_database_tables
|
51
|
+
end # class Schema
|
52
|
+
|
41
53
|
class Table
|
42
54
|
def to_exists_sql
|
43
55
|
@to_exists_sql || @to_exists_sql = <<-EOS.compress_lines
|
44
56
|
SELECT "name"
|
45
|
-
FROM "sqlite_master"
|
57
|
+
FROM "#{temporary? ? 'sqlite_temp_master' : 'sqlite_master'}"
|
46
58
|
WHERE "type" = "table"
|
47
59
|
AND "name" = ?
|
48
60
|
EOS
|
49
61
|
end
|
62
|
+
|
63
|
+
def to_column_exists_sql
|
64
|
+
@to_column_exists_sql || @to_column_exists_sql = <<-EOS.compress_lines
|
65
|
+
PRAGMA TABLE_INFO(?)
|
66
|
+
EOS
|
67
|
+
end
|
68
|
+
alias_method :to_columns_sql, :to_column_exists_sql
|
69
|
+
|
70
|
+
def unquote_default(default)
|
71
|
+
default.gsub(/(^'|'$)/, "") rescue default
|
72
|
+
end
|
73
|
+
|
50
74
|
end # class Table
|
51
75
|
|
52
76
|
class Column
|
@@ -57,6 +81,67 @@ module DataMapper
|
|
57
81
|
def size
|
58
82
|
nil
|
59
83
|
end
|
84
|
+
|
85
|
+
def alter!
|
86
|
+
@adapter.connection do |db|
|
87
|
+
flush_sql_caches!
|
88
|
+
backup_table = @adapter.table("#{@table.name}_backup")
|
89
|
+
|
90
|
+
@table.columns.each do |column|
|
91
|
+
backup_table.add_column(column.name, column.type, column.options)
|
92
|
+
end
|
93
|
+
|
94
|
+
backup_table.temporary = true
|
95
|
+
|
96
|
+
backup_table.create!
|
97
|
+
|
98
|
+
sql = <<-EOS.compress_lines
|
99
|
+
INSERT INTO #{backup_table.to_sql} SELECT #{@table.columns.map { |c| c.to_sql }.join(', ')} FROM #{@table.to_sql};
|
100
|
+
DROP TABLE #{@table.to_sql};
|
101
|
+
#{@table.to_create_sql};
|
102
|
+
INSERT INTO #{@table.to_sql} SELECT #{backup_table.columns.map { |c| c.to_sql }.join(', ')} FROM #{backup_table.to_sql};
|
103
|
+
EOS
|
104
|
+
|
105
|
+
sql.split(';').each do |part|
|
106
|
+
db.create_command(part).execute_non_query
|
107
|
+
end
|
108
|
+
|
109
|
+
backup_table.drop!
|
110
|
+
flush_sql_caches!
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def drop!
|
115
|
+
@adapter.connection do |db|
|
116
|
+
@table.columns.delete(self)
|
117
|
+
flush_sql_caches!
|
118
|
+
|
119
|
+
backup_table = @adapter.table("#{@table.name}_backup")
|
120
|
+
|
121
|
+
@table.columns.each do |column|
|
122
|
+
backup_table.add_column(column.name, column.type, column.options)
|
123
|
+
end
|
124
|
+
|
125
|
+
backup_table.temporary = true
|
126
|
+
|
127
|
+
backup_table.create!
|
128
|
+
|
129
|
+
sql = <<-EOS.compress_lines
|
130
|
+
INSERT INTO #{backup_table.to_sql} SELECT #{@table.columns.map { |c| c.to_sql }.join(', ')} FROM #{@table.to_sql};
|
131
|
+
DROP TABLE #{@table.to_sql};
|
132
|
+
#{@table.to_create_sql};
|
133
|
+
INSERT INTO #{@table.to_sql} SELECT #{backup_table.columns.map { |c| c.to_sql }.join(', ')} FROM #{backup_table.to_sql};
|
134
|
+
EOS
|
135
|
+
|
136
|
+
sql.split(';').each do |part|
|
137
|
+
db.create_command(part).execute_non_query
|
138
|
+
end
|
139
|
+
|
140
|
+
backup_table.drop!
|
141
|
+
flush_sql_caches!
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
60
145
|
end # class Column
|
61
146
|
end # module Mappings
|
62
147
|
|
@@ -5,9 +5,7 @@ module DataMapper
|
|
5
5
|
|
6
6
|
class BelongsToAssociation < HasNAssociation
|
7
7
|
|
8
|
-
def define_accessor(klass)
|
9
|
-
klass.property((@options[:foreign_key] || "#{name}_id").to_sym, :integer)
|
10
|
-
|
8
|
+
def define_accessor(klass)
|
11
9
|
klass.class_eval <<-EOS
|
12
10
|
|
13
11
|
def create_#{@association_name}(options = {})
|
@@ -33,14 +31,47 @@ module DataMapper
|
|
33
31
|
EOS
|
34
32
|
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
# Reverse the natural order for BelongsToAssociations
|
35
|
+
alias constant associated_constant
|
36
|
+
def associated_constant
|
37
|
+
@constant
|
40
38
|
end
|
41
|
-
|
39
|
+
|
40
|
+
def foreign_key_name
|
41
|
+
@foreign_key_name || @foreign_key_name = (@options[:foreign_key] || "#{name}_#{key_table.key.name}".to_sym)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_sql
|
45
|
+
"JOIN #{key_table.to_sql} ON #{foreign_key_column.to_sql(true)} = #{primary_key_column.to_sql(true)}"
|
46
|
+
end
|
47
|
+
|
42
48
|
class Instance < Associations::Reference
|
43
|
-
|
49
|
+
|
50
|
+
def dirty?
|
51
|
+
@associated && @new_member
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_recursively(event, cleared)
|
55
|
+
@associated.nil? || cleared.include?(@associated) || @associated.validate_recursively(event, cleared)
|
56
|
+
end
|
57
|
+
|
58
|
+
def save_without_validation(database_context)
|
59
|
+
@new_member = false
|
60
|
+
unless @associated.nil?
|
61
|
+
@instance.instance_variable_set(
|
62
|
+
association.foreign_key_column.instance_variable_name,
|
63
|
+
@associated.key
|
64
|
+
)
|
65
|
+
@instance.database_context.adapter.save_without_validation(database_context, @instance)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def reload!
|
70
|
+
@new_member = false
|
71
|
+
@associated = nil
|
72
|
+
instance
|
73
|
+
end
|
74
|
+
|
44
75
|
def instance
|
45
76
|
@associated || @associated = begin
|
46
77
|
if @instance.loaded_set.nil?
|
@@ -48,11 +79,11 @@ module DataMapper
|
|
48
79
|
else
|
49
80
|
|
50
81
|
# Temp variable for the instance variable name.
|
51
|
-
fk = association.
|
82
|
+
fk = association.foreign_key_column.to_sym
|
52
83
|
|
53
84
|
set = @instance.loaded_set.group_by { |instance| instance.send(fk) }
|
54
85
|
|
55
|
-
@instance.
|
86
|
+
@instance.database_context.all(association.constant, association.associated_table.key.to_sym => set.keys).each do |assoc|
|
56
87
|
set[assoc.key].each do |primary_instance|
|
57
88
|
primary_instance.send(setter_method, assoc)
|
58
89
|
end
|
@@ -64,11 +95,11 @@ module DataMapper
|
|
64
95
|
end
|
65
96
|
|
66
97
|
def create(options)
|
67
|
-
@associated = association.
|
98
|
+
@associated = association.associated_constant.create(options)
|
68
99
|
end
|
69
100
|
|
70
101
|
def build(options)
|
71
|
-
@associated = association.
|
102
|
+
@associated = association.associated_constant.new(options)
|
72
103
|
end
|
73
104
|
|
74
105
|
def setter_method
|
@@ -76,51 +107,19 @@ module DataMapper
|
|
76
107
|
end
|
77
108
|
|
78
109
|
def set(val)
|
79
|
-
@instance
|
110
|
+
raise "RecursionError" if val == @instance
|
111
|
+
@new_member = true
|
112
|
+
@instance.instance_variable_set(association.foreign_key_column.instance_variable_name, val.key)
|
80
113
|
@associated = val
|
81
114
|
end
|
115
|
+
|
116
|
+
def ensure_foreign_key!
|
117
|
+
if @associated
|
118
|
+
@instance.instance_variable_set(association.foreign_key.instance_variable_name, @associated.key)
|
119
|
+
end
|
120
|
+
end
|
82
121
|
|
83
122
|
end # class Instance
|
84
|
-
|
85
|
-
|
86
|
-
# def find
|
87
|
-
# return @result unless @result.nil?
|
88
|
-
#
|
89
|
-
# unless @instance.loaded_set.nil?
|
90
|
-
#
|
91
|
-
# # Temp variable for the instance variable name.
|
92
|
-
# setter_method = "#{@association_name}=".to_sym
|
93
|
-
# instance_variable_name = "@#{foreign_key}".to_sym
|
94
|
-
#
|
95
|
-
# set = @instance.loaded_set.group_by { |instance| instance.instance_variable_get(instance_variable_name) }
|
96
|
-
#
|
97
|
-
# # Fetch the foreign objects for all instances in the current object's loaded-set.
|
98
|
-
# @instance.session.all(constant, :id => set.keys).each do |owner|
|
99
|
-
# set[owner.key].each do |instance|
|
100
|
-
# instance.send(setter_method, owner)
|
101
|
-
# end
|
102
|
-
# end
|
103
|
-
# end
|
104
|
-
#
|
105
|
-
# return @result
|
106
|
-
# end
|
107
|
-
|
108
|
-
# def create(options = {})
|
109
|
-
# associated = constant.new(options)
|
110
|
-
# if associated.save
|
111
|
-
# @instance.send("#{constant.foreign_key}=", associated.id)
|
112
|
-
# @result = associated
|
113
|
-
# end
|
114
|
-
# end
|
115
|
-
#
|
116
|
-
# def build(options = {})
|
117
|
-
# @result = constant.new(options)
|
118
|
-
# end
|
119
|
-
#
|
120
|
-
# def set(val)
|
121
|
-
# @result = val
|
122
|
-
# end
|
123
|
-
|
124
123
|
end
|
125
124
|
|
126
125
|
end
|