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