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.
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
@@ -1,348 +0,0 @@
1
- require 'date'
2
-
3
- # Thanks http://www.rubyweeklynews.org/20051120
4
- class DateTime
5
- def to_time
6
- Time.mktime(year, mon, day, hour, min, sec)
7
- end
8
-
9
- def to_date
10
- Date.new(year, mon, day)
11
- end
12
- end
13
-
14
- class Time
15
- def to_datetime
16
- DateTime.civil(year, mon, day, hour, min, sec)
17
- end
18
-
19
- def to_s_db
20
- strftime("%Y-%m-%d %X")
21
- end
22
- end
23
-
24
- module DataObject
25
- STATE_OPEN = 0
26
- STATE_CLOSED = 1
27
-
28
- class Connection
29
-
30
- attr_reader :timeout, :database, :datasource, :server_version, :state
31
-
32
- def initialize(connection_string)
33
- end
34
-
35
- def logger
36
- @logger || @logger = Logger.new(nil)
37
- end
38
-
39
- def logger=(value)
40
- @logger = value
41
- end
42
-
43
- def begin_transaction
44
- # TODO: Hook this up
45
- Transaction.new
46
- end
47
-
48
- def change_database(database_name)
49
- raise NotImplementedError
50
- end
51
-
52
- def open
53
- raise NotImplementedError
54
- end
55
-
56
- def close
57
- raise NotImplementedError
58
- end
59
-
60
- def create_command(text)
61
- logger.debug { text }
62
- Command.new(self, text)
63
- end
64
-
65
- def closed?
66
- @state == STATE_CLOSED
67
- end
68
-
69
- end
70
-
71
- class Transaction
72
-
73
- attr_reader :connection
74
-
75
- def initialize(conn)
76
- @connection = conn
77
- end
78
-
79
- # Commits the transaction
80
- def commit
81
- raise NotImplementedError
82
- end
83
-
84
- # Rolls back the transaction
85
- def rollback(savepoint = nil)
86
- raise NotImplementedError
87
- end
88
-
89
- # Creates a savepoint for rolling back later (not commonly supported)
90
- def save(name)
91
- raise NotImplementedError
92
- end
93
-
94
- end
95
-
96
- class Reader
97
- include Enumerable
98
-
99
- attr_reader :field_count, :records_affected, :fields
100
-
101
- def each
102
- raise NotImplementedError
103
- end
104
-
105
- def has_rows?
106
- @has_rows
107
- end
108
-
109
- def current_row
110
- ret = []
111
- field_count.times do |i|
112
- ret[i] = item(i)
113
- end
114
- ret
115
- end
116
-
117
- def open?
118
- @state != STATE_CLOSED
119
- end
120
-
121
- def close
122
- real_close
123
- @reader = nil
124
- @state = STATE_CLOSED
125
- true
126
- end
127
-
128
- def real_close
129
- raise NotImplementedError
130
- end
131
-
132
- # retrieves the Ruby data type for a particular column number
133
- def data_type_name(col)
134
- raise ReaderClosed, "You cannot ask for metadata once the reader is closed" if state_closed?
135
- end
136
-
137
- # retrieves the name of a particular column number
138
- def name(col)
139
- raise ReaderClosed, "You cannot ask for metadata once the reader is closed" if state_closed?
140
- end
141
-
142
- # retrives the index of the column with a particular name
143
- def get_index(name)
144
- raise ReaderClosed, "You cannot ask for metadata once the reader is closed" if state_closed?
145
- end
146
-
147
- def item(idx)
148
- raise ReaderClosed, "You cannot ask for information once the reader is closed" if state_closed?
149
- end
150
-
151
- # returns an array of hashes containing the following information
152
- #
153
- # name: the column name
154
- # index: the index of the column
155
- # max_size: the maximum allowed size of the data in the column
156
- # precision: the precision (for column types that support it)
157
- # scale: the scale (for column types that support it)
158
- # unique: boolean specifying whether the values must be unique
159
- # key: boolean specifying whether this column is, or is part
160
- # of, the primary key
161
- # catalog: the name of the database this column is part of
162
- # base_name: the original name of the column (if AS was used,
163
- # this will provide the original name)
164
- # schema: the name of the schema (if supported)
165
- # table: the name of the table this column is part of
166
- # data_type: the name of the Ruby data type used
167
- # allow_null: boolean specifying whether nulls are allowed
168
- # db_type: the type specified by the DB
169
- # aliased: boolean specifying whether the column has been
170
- # renamed using AS
171
- # calculated: boolean specifying whether the field is calculated
172
- # serial: boolean specifying whether the field is a serial
173
- # column
174
- # blob: boolean specifying whether the field is a BLOB
175
- # readonly: boolean specifying whether the field is readonly
176
- def get_schema
177
- raise ReaderClosed, "You cannot ask for metadata once the reader is closed" if state_closed?
178
- end
179
-
180
- # specifies whether the column identified by the passed in index
181
- # is null.
182
- def null?(idx)
183
- raise ReaderClosed, "You cannot ask for column information once the reader is closed" if state_closed?
184
- end
185
-
186
- # Consumes the next result. Returns true if a result is consumed and
187
- # false if none is
188
- def next
189
- raise ReaderClosed, "You cannot increment the cursor once the reader is closed" if state_closed?
190
- end
191
-
192
- protected
193
- def state_closed?
194
- @state == STATE_CLOSED
195
- end
196
-
197
- def native_type
198
- raise ReaderClosed, "You cannot check the type of a column once the reader is closed" if state_closed?
199
- end
200
-
201
- end
202
-
203
- class ResultData
204
-
205
- def initialize(conn, affected_rows, last_insert_row = nil)
206
- @conn, @affected_rows, @last_insert_row = conn, affected_rows, last_insert_row
207
- end
208
-
209
- attr_reader :affected_rows, :last_insert_row
210
- alias_method :to_i, :affected_rows
211
-
212
- end
213
-
214
- class Schema < Array
215
-
216
- end
217
-
218
- class Command
219
-
220
- attr_reader :text, :timeout, :connection
221
-
222
- # initialize creates a new Command object
223
- def initialize(connection, text)
224
- @connection, @text = connection, text
225
- end
226
-
227
- def execute_non_query(*args)
228
- raise LostConnectionError, "the connection to the database has been lost" if @connection.closed?
229
- end
230
-
231
- def execute_reader(*args)
232
- raise LostConnectionError, "the connection to the database has been lost" if @connection.closed?
233
- end
234
-
235
- def prepare
236
- raise NotImplementedError
237
- end
238
-
239
- # Escape a string of SQL with a set of arguments.
240
- # The first argument is assumed to be the SQL to escape,
241
- # the remaining arguments (if any) are assumed to be
242
- # values to escape and interpolate.
243
- #
244
- # ==== Examples
245
- # escape_sql("SELECT * FROM zoos")
246
- # # => "SELECT * FROM zoos"
247
- #
248
- # escape_sql("SELECT * FROM zoos WHERE name = ?", "Dallas")
249
- # # => "SELECT * FROM zoos WHERE name = `Dallas`"
250
- #
251
- # escape_sql("SELECT * FROM zoos WHERE name = ? AND acreage > ?", "Dallas", 40)
252
- # # => "SELECT * FROM zoos WHERE name = `Dallas` AND acreage > 40"
253
- #
254
- # ==== Warning
255
- # This method is meant mostly for adapters that don't support
256
- # bind-parameters.
257
- def escape_sql(args)
258
- sql = text.dup
259
-
260
- unless args.empty?
261
- sql.gsub!(/\?/) do |x|
262
- quote_value(args.shift)
263
- end
264
- end
265
-
266
- sql
267
- end
268
-
269
- def quote_value(value)
270
- return 'NULL' if value.nil?
271
-
272
- case value
273
- when Numeric then quote_numeric(value)
274
- when String then quote_string(value)
275
- when Class then quote_class(value)
276
- when Time then quote_time(value)
277
- when DateTime then quote_datetime(value)
278
- when Date then quote_date(value)
279
- when TrueClass, FalseClass then quote_boolean(value)
280
- when Array then quote_array(value)
281
- else
282
- if value.respond_to?(:to_sql)
283
- value.to_sql
284
- else
285
- raise "Don't know how to quote #{value.inspect}"
286
- end
287
- end
288
- end
289
-
290
- def quote_numeric(value)
291
- value.to_s
292
- end
293
-
294
- def quote_string(value)
295
- "'#{value.gsub("'", "''")}'"
296
- end
297
-
298
- def quote_class(value)
299
- "'#{value.name}'"
300
- end
301
-
302
- def quote_time(value)
303
- "'#{value.xmlschema}'"
304
- end
305
-
306
- def quote_datetime(value)
307
- "'#{value.dup}'"
308
- end
309
-
310
- def quote_date(value)
311
- "'#{value.strftime("%Y-%m-%d")}'"
312
- end
313
-
314
- def quote_boolean(value)
315
- value.to_s.upcase
316
- end
317
-
318
- def quote_array(value)
319
- "(#{value.map { |entry| quote_value(entry) }.join(', ')})"
320
- end
321
-
322
- end
323
-
324
- class NotImplementedError < StandardError
325
- end
326
-
327
- class ConnectionFailed < StandardError
328
- end
329
-
330
- class ReaderClosed < StandardError
331
- end
332
-
333
- class ReaderError < StandardError
334
- end
335
-
336
- class QueryError < StandardError
337
- end
338
-
339
- class NoInsertError < StandardError
340
- end
341
-
342
- class LostConnectionError < StandardError
343
- end
344
-
345
- class UnknownError < StandardError
346
- end
347
-
348
- end
@@ -1,212 +0,0 @@
1
- require 'mysql_c'
2
- require 'do'
3
-
4
- module DataObject
5
- module Mysql
6
- TYPES = Hash[*Mysql_c.constants.select {|x| x.include?("MYSQL_TYPE")}.map {|x| [Mysql_c.const_get(x), x.gsub(/^MYSQL_TYPE_/, "")]}.flatten]
7
-
8
- QUOTE_STRING = "\""
9
- QUOTE_COLUMN = "`"
10
-
11
- class Connection < DataObject::Connection
12
-
13
- attr_reader :db
14
-
15
- def initialize(connection_string)
16
- @state = STATE_CLOSED
17
- @connection_string = connection_string
18
- opts = connection_string.split(" ")
19
- opts.each do |opt|
20
- k, v = opt.split("=")
21
- raise ArgumentError, "you specified an invalid connection component: #{opt}" unless k && v
22
- instance_variable_set("@#{k}", v)
23
- end
24
- end
25
-
26
- def change_database(database_name)
27
- @dbname = database_name
28
- @connection_string.gsub(/db_name=[^ ]*/, "db_name=#{database_name}")
29
- end
30
-
31
- def open
32
- @db = Mysql_c.mysql_init(nil)
33
- raise ConnectionFailed, "could not allocate a MySQL connection" unless @db
34
- conn = Mysql_c.mysql_real_connect(@db, @host, @user, @password, @dbname, @port || 0, @socket, @flags || 0)
35
- raise ConnectionFailed, "The connection with connection string #{@connection_string} failed\n#{Mysql_c.mysql_error(@db)}" unless conn
36
- @state = STATE_OPEN
37
- true
38
- end
39
-
40
- def close
41
- if @state == STATE_OPEN
42
- Mysql_c.mysql_close(@db)
43
- @state = STATE_CLOSED
44
- true
45
- else
46
- false
47
- end
48
- end
49
-
50
- def create_command(text)
51
- logger.debug { text }
52
- Command.new(self, text)
53
- end
54
-
55
- end
56
-
57
- class Field
58
- attr_reader :name, :type
59
-
60
- def initialize(ptr)
61
- @name, @type = ptr.name.to_s, ptr.type.to_s
62
- end
63
- end
64
-
65
- class Reader < DataObject::Reader
66
-
67
- def initialize(db, reader)
68
- @reader = reader
69
- unless @reader
70
- if Mysql_c.mysql_field_count(db) == 0
71
- @records_affected = Mysql_c.mysql_affected_rows(db)
72
- close
73
- else
74
- raise UnknownError, "An unknown error has occured while trying to process a MySQL query.\n#{Mysql_c.mysql_error(db)}"
75
- end
76
- else
77
- @field_count = @reader.field_count
78
- @state = STATE_OPEN
79
-
80
- @native_fields, @fields = Mysql_c.mysql_c_fetch_field_types(@reader, @field_count), Mysql_c.mysql_c_fetch_field_names(@reader, @field_count)
81
-
82
- raise UnknownError, "An unknown error has occured while trying to process a MySQL query. There were no fields in the resultset\n#{Mysql_c.mysql_error(db)}" if @native_fields.empty?
83
-
84
- @has_rows = !(@row = Mysql_c.mysql_c_fetch_row(@reader)).nil?
85
- end
86
- end
87
-
88
- def close
89
- if @state == STATE_OPEN
90
- Mysql_c.mysql_free_result(@reader)
91
- @state = STATE_CLOSED
92
- true
93
- else
94
- false
95
- end
96
- end
97
-
98
- def name(col)
99
- super
100
- @fields[col]
101
- end
102
-
103
- def get_index(name)
104
- super
105
- @fields.index(name)
106
- end
107
-
108
- def null?(idx)
109
- super
110
- @row[idx] == nil
111
- end
112
-
113
- def current_row
114
- @row
115
- end
116
-
117
- def item(idx)
118
- super
119
- typecast(@row[idx], idx)
120
- end
121
-
122
- def next
123
- super
124
- @row = Mysql_c.mysql_c_fetch_row(@reader)
125
- close if @row.nil?
126
- @row ? true : nil
127
- end
128
-
129
- def each
130
- return unless has_rows?
131
-
132
- while(true) do
133
- yield
134
- break unless self.next
135
- end
136
- end
137
-
138
- protected
139
- def native_type(col)
140
- super
141
- TYPES[@native_fields[col].type]
142
- end
143
-
144
- def typecast(val, idx)
145
- return nil if val.nil?
146
- field = @native_fields[idx]
147
- case TYPES[field]
148
- when "TINY"
149
- val != "0"
150
- when "BIT"
151
- val.to_i(2)
152
- when "SHORT", "LONG", "INT24", "LONGLONG"
153
- val.to_i
154
- when "DECIMAL", "NEWDECIMAL", "FLOAT", "DOUBLE", "YEAR"
155
- val.to_f
156
- when "TIMESTAMP", "DATETIME"
157
- DateTime.parse(val) rescue nil
158
- when "TIME"
159
- DateTime.parse(val).to_time rescue nil
160
- when "DATE"
161
- Date.parse(val) rescue nil
162
- when "NULL"
163
- nil
164
- else
165
- val
166
- end
167
- end
168
- end
169
-
170
- class Command < DataObject::Command
171
-
172
- def execute_reader(*args)
173
- super
174
- result = Mysql_c.mysql_query(@connection.db, escape_sql(args))
175
- # TODO: Real Error
176
- raise QueryError, "Your query failed.\n#{Mysql_c.mysql_error(@connection.db)}\n#{@text}" unless result == 0
177
- reader = Reader.new(@connection.db, Mysql_c.mysql_use_result(@connection.db))
178
- if block_given?
179
- result = yield(reader)
180
- reader.close
181
- result
182
- else
183
- reader
184
- end
185
- end
186
-
187
- def execute_non_query(*args)
188
- super
189
- result = Mysql_c.mysql_query(@connection.db, escape_sql(args))
190
- raise QueryError, "Your query failed.\n#{Mysql_c.mysql_error(@connection.db)}\n#{@text}" unless result == 0
191
- reader = Mysql_c.mysql_store_result(@connection.db)
192
- raise QueryError, "You called execute_non_query on a query: #{@text}" if reader
193
- rows_affected = Mysql_c.mysql_affected_rows(@connection.db)
194
- Mysql_c.mysql_free_result(reader)
195
- return ResultData.new(@connection, rows_affected, Mysql_c.mysql_insert_id(@connection.db))
196
- end
197
-
198
- def quote_time(value)
199
- "DATE('#{value.xmlschema}')"
200
- end
201
-
202
- def quote_datetime(value)
203
- "DATE('#{value}')"
204
- end
205
-
206
- def quote_date(value)
207
- "DATE('#{value.strftime("%Y-%m-%d")}')"
208
- end
209
- end
210
-
211
- end
212
- end