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
@@ -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
|