datamapper 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/example.rb +5 -5
  2. data/lib/data_mapper/adapters/abstract_adapter.rb +2 -2
  3. data/lib/data_mapper/adapters/data_object_adapter.rb +141 -147
  4. data/lib/data_mapper/adapters/mysql_adapter.rb +14 -1
  5. data/lib/data_mapper/adapters/postgresql_adapter.rb +123 -18
  6. data/lib/data_mapper/adapters/sql/coersion.rb +21 -9
  7. data/lib/data_mapper/adapters/sql/commands/load_command.rb +36 -19
  8. data/lib/data_mapper/adapters/sql/mappings/column.rb +111 -17
  9. data/lib/data_mapper/adapters/sql/mappings/schema.rb +27 -0
  10. data/lib/data_mapper/adapters/sql/mappings/table.rb +256 -29
  11. data/lib/data_mapper/adapters/sqlite3_adapter.rb +93 -8
  12. data/lib/data_mapper/associations/belongs_to_association.rb +53 -54
  13. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +157 -25
  14. data/lib/data_mapper/associations/has_many_association.rb +45 -15
  15. data/lib/data_mapper/associations/has_n_association.rb +79 -20
  16. data/lib/data_mapper/associations/has_one_association.rb +2 -2
  17. data/lib/data_mapper/associations/reference.rb +1 -1
  18. data/lib/data_mapper/auto_migrations.rb +40 -0
  19. data/lib/data_mapper/base.rb +201 -98
  20. data/lib/data_mapper/context.rb +16 -10
  21. data/lib/data_mapper/database.rb +22 -11
  22. data/lib/data_mapper/dependency_queue.rb +28 -0
  23. data/lib/data_mapper/embedded_value.rb +61 -17
  24. data/lib/data_mapper/property.rb +4 -0
  25. data/lib/data_mapper/support/active_record_impersonation.rb +13 -5
  26. data/lib/data_mapper/support/errors.rb +5 -0
  27. data/lib/data_mapper/support/serialization.rb +8 -4
  28. data/lib/data_mapper/validatable_extensions/errors.rb +12 -0
  29. data/lib/data_mapper/validatable_extensions/macros.rb +7 -0
  30. data/lib/data_mapper/validatable_extensions/validatable_instance_methods.rb +62 -0
  31. data/lib/data_mapper/validatable_extensions/validation_base.rb +18 -0
  32. data/lib/data_mapper/validatable_extensions/validations/formats/email.rb +43 -0
  33. data/lib/data_mapper/validatable_extensions/validations/validates_acceptance_of.rb +7 -0
  34. data/lib/data_mapper/validatable_extensions/validations/validates_confirmation_of.rb +7 -0
  35. data/lib/data_mapper/validatable_extensions/validations/validates_each.rb +7 -0
  36. data/lib/data_mapper/validatable_extensions/validations/validates_format_of.rb +28 -0
  37. data/lib/data_mapper/validatable_extensions/validations/validates_length_of.rb +15 -0
  38. data/lib/data_mapper/validatable_extensions/validations/validates_numericality_of.rb +7 -0
  39. data/lib/data_mapper/validatable_extensions/validations/validates_presence_of.rb +7 -0
  40. data/lib/data_mapper/validatable_extensions/validations/validates_true_for.rb +7 -0
  41. data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +33 -0
  42. data/lib/data_mapper/validations.rb +20 -0
  43. data/lib/data_mapper.rb +39 -34
  44. data/performance.rb +24 -18
  45. data/plugins/dataobjects/do_rb +0 -0
  46. data/rakefile.rb +12 -2
  47. data/spec/active_record_impersonation_spec.rb +133 -0
  48. data/spec/acts_as_tree_spec.rb +25 -9
  49. data/spec/associations_spec.rb +124 -4
  50. data/spec/attributes_spec.rb +13 -0
  51. data/spec/auto_migrations_spec.rb +44 -0
  52. data/spec/base_spec.rb +189 -1
  53. data/spec/column_spec.rb +85 -7
  54. data/spec/conditions_spec.rb +2 -2
  55. data/spec/dependency_spec.rb +25 -0
  56. data/spec/embedded_value_spec.rb +123 -3
  57. data/spec/fixtures/animals.yaml +1 -0
  58. data/spec/fixtures/careers.yaml +5 -0
  59. data/spec/fixtures/comments.yaml +1 -0
  60. data/spec/fixtures/people.yaml +14 -9
  61. data/spec/fixtures/projects.yaml +4 -0
  62. data/spec/fixtures/sections.yaml +5 -0
  63. data/spec/fixtures/serializers.yaml +6 -0
  64. data/spec/fixtures/users.yaml +1 -0
  65. data/spec/load_command_spec.rb +5 -4
  66. data/spec/mock_adapter.rb +2 -2
  67. data/spec/models/animal.rb +2 -1
  68. data/spec/models/animals_exhibit.rb +2 -2
  69. data/spec/models/career.rb +6 -0
  70. data/spec/models/comment.rb +4 -0
  71. data/spec/models/exhibit.rb +4 -0
  72. data/spec/models/person.rb +3 -13
  73. data/spec/models/project.rb +1 -1
  74. data/spec/models/serializer.rb +3 -0
  75. data/spec/models/user.rb +4 -0
  76. data/spec/models/zoo.rb +8 -1
  77. data/spec/natural_key_spec.rb +36 -0
  78. data/spec/paranoia_spec.rb +36 -0
  79. data/spec/property_spec.rb +70 -0
  80. data/spec/schema_spec.rb +10 -2
  81. data/spec/serialization_spec.rb +6 -3
  82. data/spec/serialize_spec.rb +19 -0
  83. data/spec/single_table_inheritance_spec.rb +7 -1
  84. data/spec/spec_helper.rb +26 -8
  85. data/spec/table_spec.rb +33 -0
  86. data/spec/validates_confirmation_of_spec.rb +20 -4
  87. data/spec/validates_format_of_spec.rb +22 -8
  88. data/spec/validates_length_of_spec.rb +26 -13
  89. data/spec/validates_uniqueness_of_spec.rb +18 -5
  90. data/spec/validations_spec.rb +55 -10
  91. data/tasks/fixtures.rb +13 -7
  92. metadata +189 -153
  93. data/lib/data_mapper/validations/confirmation_validator.rb +0 -53
  94. data/lib/data_mapper/validations/contextual_validations.rb +0 -50
  95. data/lib/data_mapper/validations/format_validator.rb +0 -85
  96. data/lib/data_mapper/validations/formats/email.rb +0 -78
  97. data/lib/data_mapper/validations/generic_validator.rb +0 -22
  98. data/lib/data_mapper/validations/length_validator.rb +0 -76
  99. data/lib/data_mapper/validations/required_field_validator.rb +0 -41
  100. data/lib/data_mapper/validations/unique_validator.rb +0 -56
  101. data/lib/data_mapper/validations/validation_errors.rb +0 -37
  102. data/lib/data_mapper/validations/validation_helper.rb +0 -77
  103. data/plugins/dataobjects/REVISION +0 -1
  104. data/plugins/dataobjects/Rakefile +0 -9
  105. data/plugins/dataobjects/do.rb +0 -348
  106. data/plugins/dataobjects/do_mysql.rb +0 -212
  107. data/plugins/dataobjects/do_postgres.rb +0 -196
  108. data/plugins/dataobjects/do_sqlite3.rb +0 -157
  109. data/plugins/dataobjects/spec/do_spec.rb +0 -150
  110. data/plugins/dataobjects/spec/spec_helper.rb +0 -81
  111. data/plugins/dataobjects/swig_mysql/extconf.rb +0 -45
  112. data/plugins/dataobjects/swig_mysql/mysql_c.c +0 -16602
  113. data/plugins/dataobjects/swig_mysql/mysql_c.i +0 -67
  114. data/plugins/dataobjects/swig_mysql/mysql_supp.i +0 -46
  115. data/plugins/dataobjects/swig_postgres/extconf.rb +0 -29
  116. data/plugins/dataobjects/swig_postgres/postgres_c.c +0 -8185
  117. data/plugins/dataobjects/swig_postgres/postgres_c.i +0 -73
  118. data/plugins/dataobjects/swig_sqlite/extconf.rb +0 -9
  119. data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +0 -4725
  120. data/plugins/dataobjects/swig_sqlite/sqlite_c.i +0 -168
  121. data/tasks/drivers.rb +0 -20
data/example.rb CHANGED
@@ -37,7 +37,7 @@ DataMapper::Database.setup({
37
37
  :username => 'root'
38
38
  })
39
39
 
40
- class Animal < DataMapper::Base
40
+ class Animal < DataMapper::Base #:nodoc:
41
41
  set_table_name 'animals' # Just as an example. Same inflector as Rails,
42
42
  # so this really isn't necessary.
43
43
 
@@ -47,17 +47,17 @@ class Animal < DataMapper::Base
47
47
  has_and_belongs_to_many :exhibits
48
48
  end
49
49
 
50
- class Exhibit < DataMapper::Base
50
+ class Exhibit < DataMapper::Base #:nodoc:
51
51
  property :name, :string
52
52
  belongs_to :zoo
53
53
  end
54
54
 
55
- class Zoo < DataMapper::Base
55
+ class Zoo < DataMapper::Base #:nodoc:
56
56
  property :name, :string
57
57
  has_many :exhibits
58
58
  end
59
59
 
60
- class Person < DataMapper::Base
60
+ class Person < DataMapper::Base #:nodoc:
61
61
 
62
62
  property :name, :string
63
63
  property :age, :integer
@@ -127,4 +127,4 @@ end
127
127
  # DataMapper find_by_sql equivilent
128
128
  database.query("SELECT * FROM zoos")
129
129
 
130
- end
130
+ end
@@ -21,11 +21,11 @@ module DataMapper
21
21
  raise NotImplementedError.new
22
22
  end
23
23
 
24
- def save(session, instance)
24
+ def save(database_context, instance)
25
25
  raise NotImplementedError.new
26
26
  end
27
27
 
28
- def load(session, klass, options)
28
+ def load(database_context, klass, options)
29
29
  raise NotImplementedError.new
30
30
  end
31
31
 
@@ -49,13 +49,26 @@ module DataMapper
49
49
 
50
50
  def initialize(configuration)
51
51
  super
52
- @connection_pool = Support::ConnectionPool.new(4) { create_connection }
52
+ @connection_pool = Support::ConnectionPool.new { create_connection }
53
+ end
54
+
55
+ def activated?
56
+ @activated
57
+ end
58
+
59
+ def activate!
60
+ @activated = true
61
+ schema.activate!
53
62
  end
54
63
 
55
64
  def create_connection
56
65
  raise NotImplementedError.new
57
66
  end
58
67
 
68
+ def batch_insertable?
69
+ true
70
+ end
71
+
59
72
  # Yields an available connection. Flushes the connection-pool and reconnects
60
73
  # if the connection returns an error.
61
74
  def connection
@@ -95,32 +108,34 @@ module DataMapper
95
108
  raise NotImplementedError.new
96
109
  end
97
110
 
98
- def query(*args)
99
- connection do |db|
111
+ def query(*args)
112
+ db = create_connection
100
113
 
101
- command = db.create_command(args.shift)
102
- logger.debug { command.text }
103
-
104
- command.execute_reader(*args) do |reader|
105
- fields = reader.fields.map { |field| Inflector.underscore(field).to_sym }
106
-
107
- results = []
108
-
109
- if fields.size > 1
110
- struct = Struct.new(*fields)
111
-
112
- reader.each do
113
- results << struct.new(*reader.current_row)
114
- end
115
- else
116
- reader.each do
117
- results << reader.item(0)
118
- end
119
- end
114
+ command = db.create_command(args.shift)
115
+
116
+ reader = command.execute_reader(*args)
117
+ fields = reader.fields.map { |field| Inflector.underscore(field).to_sym }
118
+ results = []
120
119
 
121
- results
120
+ if fields.size > 1
121
+ struct = Struct.new(*fields)
122
+
123
+ reader.each do
124
+ results << struct.new(*reader.current_row)
125
+ end
126
+ else
127
+ reader.each do
128
+ results << reader.item(0)
122
129
  end
123
130
  end
131
+
132
+ return results
133
+ rescue => e
134
+ logger.error { e }
135
+ raise e
136
+ ensure
137
+ reader.close if reader
138
+ db.close
124
139
  end
125
140
 
126
141
  def handle_error(error)
@@ -128,153 +143,68 @@ module DataMapper
128
143
  end
129
144
 
130
145
  def schema
131
- @schema || ( @schema = Mappings::Schema.new(self, @configuration.database) )
146
+ @schema || ( @schema = self.class::Mappings::Schema.new(self, @configuration.database) )
132
147
  end
133
148
 
134
- def table_exists?(name)
149
+ def column_exists_for_table?(table_name, column_name)
135
150
  connection do |db|
136
- table = self.table(name)
137
- command = db.create_command(table.to_exists_sql)
138
- command.execute_reader(table.name, table.schema.name) do |reader|
139
- reader.has_rows?
151
+ table = self.table(table_name)
152
+ command = db.create_command(table.to_column_exists_sql)
153
+ command.execute_reader(table.name, column_name, table.schema.name) do |reader|
154
+ reader.any? { reader.item(1) == column_name.to_s }
140
155
  end
141
156
  end
142
157
  end
143
-
144
- def truncate(session, name)
145
- connection do |db|
146
- result = db.create_command("TRUNCATE TABLE #{table(name).to_sql}").execute_non_query
147
- session.identity_map.clear!(name)
148
- result.to_i > 0
149
- end
150
- end
151
-
152
- def drop(session, name)
153
- table = self.table(name)
154
-
155
- if table.exists?
156
- connection do |db|
157
- result = db.create_command("DROP TABLE #{table.to_sql}").execute_non_query
158
- session.identity_map.clear!(name)
159
- true
160
- end
161
- else
162
- false
163
- end
164
- end
165
-
166
- def create_table(name)
167
- table = self.table(name)
168
-
169
- if table.exists?
170
- false
171
- else
172
- connection do |db|
173
- db.create_command(table.to_create_table_sql).execute_non_query
174
- true
175
- end
176
- end
177
- end
178
-
179
- def delete(session, instance)
158
+
159
+ def delete(database_context, instance)
180
160
  table = self.table(instance)
181
161
 
182
162
  if instance.is_a?(Class)
183
- connection do |db|
184
- db.create_command("DELETE FROM #{table.to_sql}").execute_non_query
185
- end
186
- session.identity_map.clear!(instance)
163
+ table.delete_all!
187
164
  else
188
165
  callback(instance, :before_destroy)
189
166
 
190
- if connection do |db|
191
- command = db.create_command("DELETE FROM #{table.to_sql} WHERE #{table.key.to_sql} = ?")
192
- command.execute_non_query(instance.key).to_i > 0
193
- end # connection do...end # if continued below:
194
- instance.instance_variable_set(:@new_record, true)
195
- instance.session = session
196
- instance.original_values.clear
197
- session.identity_map.delete(instance)
198
- callback(instance, :after_destroy)
199
- end
167
+ if table.paranoid?
168
+ instance.instance_variable_set(table.paranoid_column.instance_variable_name, Time::now)
169
+ instance.save
170
+ else
171
+ if connection do |db|
172
+ command = db.create_command("DELETE FROM #{table.to_sql} WHERE #{table.key.to_sql} = ?")
173
+ command.execute_non_query(instance.key).to_i > 0
174
+ end # connection do...end # if continued below:
175
+ instance.instance_variable_set(:@new_record, true)
176
+ instance.database_context = database_context
177
+ instance.original_values.clear
178
+ database_context.identity_map.delete(instance)
179
+ callback(instance, :after_destroy)
180
+ end
181
+ end
200
182
  end
201
183
  end
202
184
 
203
- def save(session, instance)
185
+ def save(database_context, instance, validate = true)
204
186
  case instance
205
- when Class, Mappings::Table then create_table(instance)
187
+ when Class then table(instance).create!
188
+ when Mappings::Table then instance.create!
206
189
  when DataMapper::Base then
207
- return false unless instance.dirty? && instance.valid?
190
+ return false unless instance.new_record? || instance.dirty?
208
191
 
209
- callback(instance, :before_save)
192
+ event = instance.new_record? ? :create : :update
210
193
 
211
- # INSERT
212
- result = if instance.new_record?
213
- callback(instance, :before_create)
214
-
215
- table = self.table(instance)
216
- attributes = instance.dirty_attributes
217
-
218
- unless attributes.empty?
219
- if table.multi_class?
220
- instance.instance_variable_set(
221
- table[:type].instance_variable_name,
222
- attributes[:type] = instance.class.name
223
- )
224
- end
225
-
226
- keys = []
227
- values = []
228
- attributes.each_pair do |key, value|
229
- keys << table[key].to_sql
230
- values << value
231
- end
194
+ return false if validate && !instance.validate_recursively(event, Set.new)
232
195
 
233
- # Formatting is a bit off here, but it looks nicer in the log this way.
234
- insert_id = connection do |db|
235
- db.create_command("INSERT INTO #{table.to_sql} (#{keys.join(', ')}) VALUES ?")\
236
- .execute_non_query(values).last_insert_row
237
- end
238
- instance.instance_variable_set(:@new_record, false)
239
- instance.key = insert_id if table.key.serial? && !attributes.include?(table.key.name)
240
- session.identity_map.set(instance)
241
- callback(instance, :after_create)
242
- end
243
- # UPDATE
244
- else
245
- callback(instance, :before_update)
246
-
247
- table = self.table(instance)
248
- attributes = instance.dirty_attributes
249
- parameters = []
250
-
251
- unless attributes.empty?
252
- sql = "UPDATE " << table.to_sql << " SET "
253
-
254
- sql << attributes.map do |key, value|
255
- parameters << value
256
- "#{table[key].to_sql} = ?"
257
- end.join(', ')
258
-
259
- sql << " WHERE #{table.key.to_sql} = ?"
260
- parameters << instance.key
261
-
262
- connection do |db|
263
- db.create_command(sql).execute_non_query(*parameters).to_i > 0 \
264
- && callback(instance, :after_update)
265
- end
266
- end
267
- end
196
+ callback(instance, :before_save)
197
+ result = send(event, database_context, instance)
268
198
 
199
+ instance.database_context = database_context
269
200
  instance.attributes.each_pair do |name, value|
270
201
  instance.original_values[name] = value
271
202
  end
272
203
 
273
204
  instance.loaded_associations.each do |association|
274
- association.save if association.respond_to?(:save)
205
+ association.save_without_validation(database_context) if association.dirty?
275
206
  end
276
207
 
277
- instance.session = session
278
208
  callback(instance, :after_save)
279
209
  result
280
210
  end
@@ -283,12 +213,74 @@ module DataMapper
283
213
  raise error
284
214
  end
285
215
 
286
- def load(session, klass, options)
287
- self.class::Commands::LoadCommand.new(self, session, klass, options).call
216
+ def save_without_validation(database_context, instance)
217
+ save(database_context, instance, false)
218
+ end
219
+
220
+ def update(database_context, instance)
221
+ callback(instance, :before_update)
222
+
223
+ table = self.table(instance)
224
+ attributes = instance.dirty_attributes
225
+ parameters = []
226
+
227
+ unless attributes.empty?
228
+ sql = "UPDATE " << table.to_sql << " SET "
229
+
230
+ sql << attributes.map do |key, value|
231
+ parameters << value
232
+ "#{table[key].to_sql} = ?"
233
+ end.join(', ')
234
+
235
+ sql << " WHERE #{table.key.to_sql} = ?"
236
+ parameters << instance.key
237
+
238
+ connection do |db|
239
+ db.create_command(sql).execute_non_query(*parameters).to_i > 0 \
240
+ && callback(instance, :after_update)
241
+ end
242
+ else
243
+ true
244
+ end
245
+ end
246
+
247
+ def create(database_context, instance)
248
+ callback(instance, :before_create)
249
+
250
+ table = self.table(instance)
251
+ attributes = instance.dirty_attributes
252
+
253
+ if table.multi_class?
254
+ instance.instance_variable_set(
255
+ table[:type].instance_variable_name,
256
+ attributes[:type] = instance.class.name
257
+ )
258
+ end
259
+
260
+ keys = []
261
+ values = []
262
+ attributes.each_pair do |key, value|
263
+ keys << table[key].to_sql
264
+ values << value
265
+ end
266
+
267
+ sql = if keys.size > 0
268
+ "INSERT INTO #{table.to_sql} (#{keys.join(', ')}) VALUES ?"
269
+ else
270
+ "INSERT INTO #{table.to_sql}"
271
+ end
272
+
273
+ insert_id = connection do |db|
274
+ db.create_command(sql).execute_non_query(values).last_insert_row
275
+ end
276
+ instance.instance_variable_set(:@new_record, false)
277
+ instance.key = insert_id if table.key.serial? && !attributes.include?(table.key.name)
278
+ database_context.identity_map.set(instance)
279
+ callback(instance, :after_create)
288
280
  end
289
281
 
290
- def count(klass_or_instance, options)
291
- query("SELECT COUNT(*) AS row_count FROM " + table(klass_or_instance).to_sql).first.to_i
282
+ def load(database_context, klass, options)
283
+ self.class::Commands::LoadCommand.new(self, database_context, klass, options).call
292
284
  end
293
285
 
294
286
  def table(instance)
@@ -340,7 +332,9 @@ module DataMapper
340
332
  :decimal => 'decimal'.freeze,
341
333
  :float => 'float'.freeze,
342
334
  :datetime => 'datetime'.freeze,
343
- :date => 'date'.freeze
335
+ :date => 'date'.freeze,
336
+ :boolean => 'boolean'.freeze,
337
+ :object => 'text'.freeze
344
338
  }
345
339
 
346
340
  include Sql
@@ -4,7 +4,7 @@ begin
4
4
  rescue LoadError
5
5
  STDERR.puts <<-EOS
6
6
  You must install the DataObjects::Mysql driver.
7
- rake dm:install:mysql
7
+ gem install do_mysql
8
8
  EOS
9
9
  exit
10
10
  end
@@ -34,6 +34,10 @@ module DataMapper
34
34
  return conn
35
35
  end
36
36
 
37
+ def database_column_name
38
+ "TABLE_SCHEMA"
39
+ end
40
+
37
41
  module Mappings
38
42
 
39
43
  def to_create_table_sql
@@ -42,6 +46,15 @@ module DataMapper
42
46
  end
43
47
  end
44
48
 
49
+ class Schema
50
+
51
+ def database_tables
52
+ get_database_tables(@adapter.schema.name)
53
+ end
54
+
55
+ end
56
+
57
+
45
58
  end # module Mappings
46
59
  end # class MysqlAdapter
47
60
 
@@ -3,8 +3,8 @@ begin
3
3
  require 'do_postgres'
4
4
  rescue LoadError
5
5
  STDERR.puts <<-EOS
6
- You must install the DataObjects::PostgreSQL driver.
7
- rake dm:install:postgresql
6
+ You must install the DataObjects::Postgres driver.
7
+ gem install do_postgres
8
8
  EOS
9
9
  exit
10
10
  end
@@ -27,16 +27,29 @@ module DataMapper
27
27
  end
28
28
 
29
29
  def create_connection
30
- conn = DataObject::Postgres::Connection.new("dbname=#{@configuration.database}")
30
+ connection_string = ""
31
+ builder = lambda do |k,v|
32
+ connection_string << "#{k}=#{@configuration.send(v)} " unless
33
+ @configuration.send(v).blank?
34
+ end
35
+ builder['host', :host]
36
+ builder['user', :username]
37
+ builder['password', :password]
38
+ builder['dbname', :database]
39
+ builder['socket', :socket]
40
+ conn = DataObject::Postgres::Connection.new(connection_string.strip)
31
41
  conn.logger = self.logger
32
42
  conn.open
33
- return conn
34
-
43
+
35
44
  unless schema_search_path.empty?
36
45
  execute("SET search_path TO #{schema_search_path}")
37
46
  end
38
-
39
- return connection
47
+
48
+ return conn
49
+ end
50
+
51
+ def database_column_name
52
+ "TABLE_CATALOG"
40
53
  end
41
54
 
42
55
  TABLE_QUOTING_CHARACTER = '"'.freeze
@@ -72,15 +85,47 @@ module DataMapper
72
85
  return sql
73
86
  end
74
87
 
75
- def to_exists_sql
76
- @to_exists_sql || @to_exists_sql = <<-EOS.compress_lines
77
- SELECT TABLE_NAME
78
- FROM INFORMATION_SCHEMA.TABLES
79
- WHERE TABLE_NAME = ?
80
- AND TABLE_CATALOG = ?
81
- EOS
88
+ # The logic of this comes from AR; it was modified for smarter typecasting
89
+ def unquote_default(default)
90
+ # Boolean types
91
+ return true if default =~ /true/i
92
+ return false if default =~ /false/i
93
+
94
+ # Char/String/Bytea type values
95
+ return $1 if default =~ /^'(.*)'::(bpchar|text|character varying|bytea)$/
96
+
97
+ # Numeric values
98
+ return value.to_f if default =~ /^-?[0-9]+(\.[0-9]*)/
99
+ return value.to_i if default =~ /^-?[0-9]+/
100
+
101
+ # Fixed dates / times
102
+ return Date.parse($1) if default =~ /^'(.+)'::date/
103
+ return DateTime.parse($1) if default =~ /^'(.+)'::timestamp/
104
+
105
+ # Anything else is blank, some user type, or some function
106
+ # and we can't know the value of that, so return nil.
107
+ return nil
82
108
  end
83
109
 
110
+ # def to_exists_sql
111
+ # @to_exists_sql || @to_exists_sql = <<-EOS.compress_lines
112
+ # SELECT TABLE_NAME
113
+ # FROM INFORMATION_SCHEMA.TABLES
114
+ # WHERE TABLE_NAME = ?
115
+ # AND TABLE_CATALOG = ?
116
+ # EOS
117
+ # end
118
+ #
119
+ # def to_column_exists_sql
120
+ # @to_column_exists_sql || @to_column_exists_sql = <<-EOS.compress_lines
121
+ # SELECT TABLE_NAME, COLUMN_NAME
122
+ # FROM INFORMATION_SCHEMA.COLUMNS
123
+ # WHERE TABLE_NAME = ?
124
+ # AND COLUMN_NAME = ?
125
+ # AND TABLE_CATALOG = ?
126
+ # EOS
127
+ # end
128
+
84
129
  private
85
130
 
86
131
  def quote_table(table_suffix = nil)
@@ -91,17 +136,81 @@ module DataMapper
91
136
  end
92
137
  end # class Table
93
138
 
139
+ class Schema
140
+
141
+ def database_tables
142
+ get_database_tables("public")
143
+ end
144
+
145
+ end
146
+
94
147
  class Column
95
148
  def serial_declaration
96
149
  "SERIAL"
97
150
  end
98
151
 
152
+ def to_alter_sql
153
+ "ALTER TABLE " << table.to_sql << " ALTER COLUMN " << to_alter_form
154
+ end
155
+
156
+ def alter!
157
+ result = super
158
+ reset_alter_state!
159
+ result
160
+ end
161
+
162
+ def reset_alter_state!
163
+ @type_changed = false
164
+ @default_changed = false
165
+ end
166
+
167
+ def default=(value)
168
+ @default_changed = true
169
+ super
170
+ end
171
+
172
+ def type=(value)
173
+ @type_changed = true
174
+ super
175
+ end
176
+
177
+ def to_alter_form
178
+ sql = to_sql.dup
179
+
180
+ changes = 0
181
+
182
+ if @type_changed
183
+ changes += 1
184
+ sql << " TYPE " << type_declaration
185
+ case self.type
186
+ when :integer then sql << " USING #{to_sql}::integer"
187
+ when :datetime then
188
+ sql << " USING timestamp with time zone"
189
+ end
190
+ end
191
+
192
+ if @default_changed
193
+ sql << ", " if changes += 1 > 1
194
+
195
+ if default.blank? || default_declaration.blank?
196
+ sql << " DROP DEFAULT"
197
+ else
198
+ sql << " " << default_declaration
199
+ end
200
+ end
201
+
202
+ sql
203
+ end
204
+
99
205
  def to_long_form
100
206
  @to_long_form || begin
101
207
  @to_long_form = "#{to_sql}"
102
208
 
103
209
  if serial? && !serial_declaration.blank?
104
210
  @to_long_form << " #{serial_declaration}"
211
+ if key? && !primary_key_declaration.blank?
212
+ @to_long_form << " #{primary_key_declaration}"
213
+ end
105
214
  else
106
215
  @to_long_form << " #{type_declaration}"
107
216
 
@@ -109,10 +218,6 @@ module DataMapper
109
218
  @to_long_form << " #{not_null_declaration}"
110
219
  end
111
220
 
112
- if key? && !primary_key_declaration.blank?
113
- @to_long_form << " #{primary_key_declaration}"
114
- end
115
-
116
221
  if default && !default_declaration.blank?
117
222
  @to_long_form << " #{default_declaration}"
118
223
  end