activerecord 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (106) hide show
  1. data/CHANGELOG +581 -0
  2. data/README +361 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/dev-utils/eval_debugger.rb +9 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/associations.rb +87 -0
  7. data/examples/shared_setup.rb +15 -0
  8. data/examples/validation.rb +88 -0
  9. data/install.rb +60 -0
  10. data/lib/active_record.rb +48 -0
  11. data/lib/active_record/aggregations.rb +165 -0
  12. data/lib/active_record/associations.rb +536 -0
  13. data/lib/active_record/associations/association_collection.rb +70 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -0
  15. data/lib/active_record/associations/has_many_association.rb +104 -0
  16. data/lib/active_record/base.rb +985 -0
  17. data/lib/active_record/callbacks.rb +337 -0
  18. data/lib/active_record/connection_adapters/abstract_adapter.rb +326 -0
  19. data/lib/active_record/connection_adapters/mysql_adapter.rb +131 -0
  20. data/lib/active_record/connection_adapters/postgresql_adapter.rb +177 -0
  21. data/lib/active_record/connection_adapters/sqlite_adapter.rb +107 -0
  22. data/lib/active_record/deprecated_associations.rb +70 -0
  23. data/lib/active_record/fixtures.rb +172 -0
  24. data/lib/active_record/observer.rb +71 -0
  25. data/lib/active_record/reflection.rb +126 -0
  26. data/lib/active_record/support/class_attribute_accessors.rb +43 -0
  27. data/lib/active_record/support/class_inheritable_attributes.rb +37 -0
  28. data/lib/active_record/support/clean_logger.rb +10 -0
  29. data/lib/active_record/support/inflector.rb +70 -0
  30. data/lib/active_record/transactions.rb +102 -0
  31. data/lib/active_record/validations.rb +205 -0
  32. data/lib/active_record/vendor/mysql.rb +1117 -0
  33. data/lib/active_record/vendor/simple.rb +702 -0
  34. data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
  35. data/lib/active_record/wrappings.rb +59 -0
  36. data/rakefile +122 -0
  37. data/test/abstract_unit.rb +16 -0
  38. data/test/aggregations_test.rb +34 -0
  39. data/test/all.sh +8 -0
  40. data/test/associations_test.rb +477 -0
  41. data/test/base_test.rb +513 -0
  42. data/test/class_inheritable_attributes_test.rb +33 -0
  43. data/test/connections/native_mysql/connection.rb +24 -0
  44. data/test/connections/native_postgresql/connection.rb +24 -0
  45. data/test/connections/native_sqlite/connection.rb +24 -0
  46. data/test/deprecated_associations_test.rb +336 -0
  47. data/test/finder_test.rb +67 -0
  48. data/test/fixtures/accounts/signals37 +3 -0
  49. data/test/fixtures/accounts/unknown +2 -0
  50. data/test/fixtures/auto_id.rb +4 -0
  51. data/test/fixtures/column_name.rb +3 -0
  52. data/test/fixtures/companies/first_client +6 -0
  53. data/test/fixtures/companies/first_firm +4 -0
  54. data/test/fixtures/companies/second_client +6 -0
  55. data/test/fixtures/company.rb +37 -0
  56. data/test/fixtures/company_in_module.rb +33 -0
  57. data/test/fixtures/course.rb +3 -0
  58. data/test/fixtures/courses/java +2 -0
  59. data/test/fixtures/courses/ruby +2 -0
  60. data/test/fixtures/customer.rb +30 -0
  61. data/test/fixtures/customers/david +6 -0
  62. data/test/fixtures/db_definitions/mysql.sql +96 -0
  63. data/test/fixtures/db_definitions/mysql2.sql +4 -0
  64. data/test/fixtures/db_definitions/postgresql.sql +113 -0
  65. data/test/fixtures/db_definitions/postgresql2.sql +4 -0
  66. data/test/fixtures/db_definitions/sqlite.sql +85 -0
  67. data/test/fixtures/db_definitions/sqlite2.sql +4 -0
  68. data/test/fixtures/default.rb +2 -0
  69. data/test/fixtures/developer.rb +8 -0
  70. data/test/fixtures/developers/david +2 -0
  71. data/test/fixtures/developers/jamis +2 -0
  72. data/test/fixtures/developers_projects/david_action_controller +2 -0
  73. data/test/fixtures/developers_projects/david_active_record +2 -0
  74. data/test/fixtures/developers_projects/jamis_active_record +2 -0
  75. data/test/fixtures/entrant.rb +3 -0
  76. data/test/fixtures/entrants/first +3 -0
  77. data/test/fixtures/entrants/second +3 -0
  78. data/test/fixtures/entrants/third +3 -0
  79. data/test/fixtures/fixture_database.sqlite +0 -0
  80. data/test/fixtures/fixture_database_2.sqlite +0 -0
  81. data/test/fixtures/movie.rb +5 -0
  82. data/test/fixtures/movies/first +2 -0
  83. data/test/fixtures/movies/second +2 -0
  84. data/test/fixtures/project.rb +3 -0
  85. data/test/fixtures/projects/action_controller +2 -0
  86. data/test/fixtures/projects/active_record +2 -0
  87. data/test/fixtures/reply.rb +21 -0
  88. data/test/fixtures/subscriber.rb +5 -0
  89. data/test/fixtures/subscribers/first +2 -0
  90. data/test/fixtures/subscribers/second +2 -0
  91. data/test/fixtures/topic.rb +20 -0
  92. data/test/fixtures/topics/first +9 -0
  93. data/test/fixtures/topics/second +8 -0
  94. data/test/fixtures_test.rb +20 -0
  95. data/test/inflector_test.rb +104 -0
  96. data/test/inheritance_test.rb +125 -0
  97. data/test/lifecycle_test.rb +110 -0
  98. data/test/modules_test.rb +21 -0
  99. data/test/multiple_db_test.rb +46 -0
  100. data/test/pk_test.rb +57 -0
  101. data/test/reflection_test.rb +78 -0
  102. data/test/thread_safety_test.rb +33 -0
  103. data/test/transactions_test.rb +83 -0
  104. data/test/unconnected_test.rb +24 -0
  105. data/test/validations_test.rb +126 -0
  106. metadata +166 -0
@@ -0,0 +1,337 @@
1
+ require 'observer'
2
+
3
+ module ActiveRecord
4
+ # Callbacks are hooks into the lifecycle of an Active Record object that allows you to trigger logic
5
+ # before or after an alteration of the object state. This can be used to make sure that associated and
6
+ # dependent objects are deleted when destroy is called (by overwriting before_destroy) or to massage attributes
7
+ # before they're validated (by overwriting before_validation). As an example of the callbacks initiated, consider
8
+ # the Base#save call:
9
+ #
10
+ # * (-) save
11
+ # * (-) valid?
12
+ # * (1) before_validation
13
+ # * (2) before_validation_on_create
14
+ # * (-) validate
15
+ # * (-) validate_on_create
16
+ # * (4) after_validation
17
+ # * (5) after_validation_on_create
18
+ # * (6) before_save
19
+ # * (7) before_create
20
+ # * (-) create
21
+ # * (8) after_create
22
+ # * (9) after_save
23
+ #
24
+ # That's a total of nine callbacks, which gives you immense power to react and prepare for each state in the
25
+ # Active Record lifecyle.
26
+ #
27
+ # Examples:
28
+ # class CreditCard < ActiveRecord::Base
29
+ # # Strip everything but digits, so the user can specify "555 234 34" or
30
+ # # "5552-3434" or both will mean "55523434"
31
+ # def before_validation_on_create
32
+ # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
33
+ # end
34
+ # end
35
+ #
36
+ # class Subscription < ActiveRecord::Base
37
+ # # Automatically assign the signup date
38
+ # def before_create
39
+ # self.signed_up_on = Date.today
40
+ # end
41
+ # end
42
+ #
43
+ # class Firm < ActiveRecord::Base
44
+ # # Destroys the associated clients and people when the firm is destroyed
45
+ # def before_destroy
46
+ # Client.destroy_all "client_of = #{id}"
47
+ # Person.destroy_all "firm_id = #{id}"
48
+ # end
49
+ #
50
+ # == Inheritable callback queues
51
+ #
52
+ # Besides the overwriteable callback methods, it's also possible to register callbacks through the use of the callback macros.
53
+ # Their main advantage is that the macros add behavior into a callback queue that is kept intact down through an inheritance
54
+ # hierarchy. Example:
55
+ #
56
+ # class Topic < ActiveRecord::Base
57
+ # before_destroy :destroy_author
58
+ # end
59
+ #
60
+ # class Reply < Topic
61
+ # before_destroy :destroy_readers
62
+ # end
63
+ #
64
+ # Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is run both +destroy_author+ and
65
+ # +destroy_readers+ is called. Contrast this to the situation where we've implemented the save behavior through overwriteable
66
+ # methods:
67
+ #
68
+ # class Topic < ActiveRecord::Base
69
+ # def before_destroy() destroy_author end
70
+ # end
71
+ #
72
+ # class Reply < Topic
73
+ # def before_destroy() destroy_readers end
74
+ # end
75
+ #
76
+ # In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So use the callback macros when
77
+ # you want to ensure that a certain callback is called for the entire hierarchy and the regular overwriteable methods when you
78
+ # want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
79
+ #
80
+ # == Types of callbacks
81
+ #
82
+ # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
83
+ # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects are the
84
+ # recommended approaches, inline methods using a proc is some times appropriate (such as for creating mix-ins), and inline
85
+ # eval methods are deprecated.
86
+ #
87
+ # The method reference callbacks work by specifying a protected or private method available in the object, like this:
88
+ #
89
+ # class Topic < ActiveRecord::Base
90
+ # before_destroy :delete_parents
91
+ #
92
+ # private
93
+ # def delete_parents
94
+ # self.class.delete_all "parent_id = #{id}"
95
+ # end
96
+ # end
97
+ #
98
+ # The callback objects have methods named after the callback called with the record as the only parameter, such as:
99
+ #
100
+ # class BankAccount < ActiveRecord::Base
101
+ # before_save EncryptionWrapper.new("credit_card_number")
102
+ # after_save EncryptionWrapper.new("credit_card_number")
103
+ # after_initialize EncryptionWrapper.new("credit_card_number")
104
+ # end
105
+ #
106
+ # class EncryptionWrapper
107
+ # def initialize(attribute)
108
+ # @attribute = attribute
109
+ # end
110
+ #
111
+ # def before_save(record)
112
+ # record.credit_card_number = encrypt(record.credit_card_number)
113
+ # end
114
+ #
115
+ # def after_save(record)
116
+ # record.credit_card_number = decrypt(record.credit_card_number)
117
+ # end
118
+ #
119
+ # alias_method :after_initialize, :after_save
120
+ #
121
+ # private
122
+ # def encrypt(value)
123
+ # # Secrecy is committed
124
+ # end
125
+ #
126
+ # def decrypt(value)
127
+ # # Secrecy is unvieled
128
+ # end
129
+ # end
130
+ #
131
+ # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
132
+ # a method by the name of the callback messaged.
133
+ #
134
+ # The callback macros usually accept a symbol for the method they're supposed to run, but you can also pass a "method string",
135
+ # which will then be evaluated within the binding of the callback. Example:
136
+ #
137
+ # class Topic < ActiveRecord::Base
138
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"'
139
+ # end
140
+ #
141
+ # Notice that single plings (') are used so the #{id} part isn't evaluated until the callback is triggered. Also note that these
142
+ # inline callbacks can be stacked just like the regular ones:
143
+ #
144
+ # class Topic < ActiveRecord::Base
145
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"',
146
+ # 'puts "Evaluated after parents are destroyed"'
147
+ # end
148
+ #
149
+ # == The after_find and after_initialize exceptions
150
+ #
151
+ # Because after_find and after_initialize is called for each object instantiated found by a finder, such as Base.find_all, we've had
152
+ # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, after_find and
153
+ # after_initialize can only be declared using an explicit implementation. So using the inheritable callback queue for after_find and
154
+ # after_initialize won't work.
155
+ module Callbacks
156
+ CALLBACKS = %w(
157
+ after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
158
+ after_validation before_validation_on_create after_validation_on_create before_validation_on_update
159
+ after_validation_on_update before_destroy after_destroy
160
+ )
161
+
162
+ def self.append_features(base) #:nodoc:
163
+ super
164
+
165
+ base.extend(ClassMethods)
166
+ base.class_eval do
167
+ class << self
168
+ include Observable
169
+ alias_method :instantiate_without_callbacks, :instantiate
170
+ alias_method :instantiate, :instantiate_with_callbacks
171
+ end
172
+ end
173
+
174
+ base.class_eval do
175
+ alias_method :initialize_without_callbacks, :initialize
176
+ alias_method :initialize, :initialize_with_callbacks
177
+
178
+ alias_method :create_or_update_without_callbacks, :create_or_update
179
+ alias_method :create_or_update, :create_or_update_with_callbacks
180
+
181
+ alias_method :valid_without_callbacks, :valid?
182
+ alias_method :valid?, :valid_with_callbacks
183
+
184
+ alias_method :create_without_callbacks, :create
185
+ alias_method :create, :create_with_callbacks
186
+
187
+ alias_method :update_without_callbacks, :update
188
+ alias_method :update, :update_with_callbacks
189
+
190
+ alias_method :destroy_without_callbacks, :destroy
191
+ alias_method :destroy, :destroy_with_callbacks
192
+ end
193
+
194
+ CALLBACKS.each { |cb| base.class_eval("def self.#{cb}(*methods) write_inheritable_array(\"#{cb}\", methods) end") }
195
+ end
196
+
197
+ module ClassMethods #:nodoc:
198
+ def instantiate_with_callbacks(record)
199
+ object = instantiate_without_callbacks(record)
200
+ object.callback(:after_find) if object.respond_to_without_attributes?(:after_find)
201
+ object.callback(:after_initialize) if object.respond_to_without_attributes?(:after_initialize)
202
+ object
203
+ end
204
+ end
205
+
206
+ # Is called when the object was instantiated by one of the finders, like Base.find.
207
+ # def after_find() end
208
+
209
+ # Is called after the object has been instantiated by a call to Base.new.
210
+ # def after_initialize() end
211
+ def initialize_with_callbacks(attributes = nil) #:nodoc:
212
+ initialize_without_callbacks(attributes)
213
+ yield self if block_given?
214
+ after_initialize if respond_to_without_attributes?(:after_initialize)
215
+ end
216
+
217
+ # Is called _before_ Base.save (regardless of whether it's a create or update save).
218
+ def before_save() end
219
+
220
+ # Is called _after_ Base.save (regardless of whether it's a create or update save).
221
+ def after_save() end
222
+ def create_or_update_with_callbacks #:nodoc:
223
+ callback(:before_save)
224
+ create_or_update_without_callbacks
225
+ callback(:after_save)
226
+ end
227
+
228
+ # Is called _before_ Base.save on new objects that haven't been saved yet (no record exists).
229
+ def before_create() end
230
+
231
+ # Is called _after_ Base.save on new objects that haven't been saved yet (no record exists).
232
+ def after_create() end
233
+ def create_with_callbacks #:nodoc:
234
+ callback(:before_create)
235
+ create_without_callbacks
236
+ callback(:after_create)
237
+ end
238
+
239
+ # Is called _before_ Base.save on existing objects that has a record.
240
+ def before_update() end
241
+
242
+ # Is called _after_ Base.save on existing objects that has a record.
243
+ def after_update() end
244
+
245
+ def update_with_callbacks #:nodoc:
246
+ callback(:before_update)
247
+ update_without_callbacks
248
+ callback(:after_update)
249
+ end
250
+
251
+ # Is called _before_ Validations.validate (which is part of the Base.save call).
252
+ def before_validation() end
253
+
254
+ # Is called _after_ Validations.validate (which is part of the Base.save call).
255
+ def after_validation() end
256
+
257
+ # Is called _before_ Validations.validate (which is part of the Base.save call) on new objects
258
+ # that haven't been saved yet (no record exists).
259
+ def before_validation_on_create() end
260
+
261
+ # Is called _after_ Validations.validate (which is part of the Base.save call) on new objects
262
+ # that haven't been saved yet (no record exists).
263
+ def after_validation_on_create() end
264
+
265
+ # Is called _before_ Validations.validate (which is part of the Base.save call) on
266
+ # existing objects that has a record.
267
+ def before_validation_on_update() end
268
+
269
+ # Is called _after_ Validations.validate (which is part of the Base.save call) on
270
+ # existing objects that has a record.
271
+ def after_validation_on_update() end
272
+
273
+ def valid_with_callbacks #:nodoc:
274
+ callback(:before_validation)
275
+ if new_record? then callback(:before_validation_on_create) else callback(:before_validation_on_update) end
276
+
277
+ result = valid_without_callbacks
278
+
279
+ callback(:after_validation)
280
+ if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
281
+
282
+ return result
283
+ end
284
+
285
+ # Is called _before_ Base.destroy.
286
+ def before_destroy() end
287
+
288
+ # Is called _after_ Base.destroy (and all the attributes have been frozen).
289
+ def after_destroy() end
290
+ def destroy_with_callbacks #:nodoc:
291
+ callback(:before_destroy)
292
+ destroy_without_callbacks
293
+ callback(:after_destroy)
294
+ end
295
+
296
+ def callback(callback_method) #:nodoc:
297
+ run_callbacks(callback_method)
298
+ send(callback_method)
299
+ notify(callback_method)
300
+ end
301
+
302
+ def run_callbacks(callback_method)
303
+ filters = self.class.read_inheritable_attribute(callback_method.to_s)
304
+ if filters.nil? then return end
305
+ filters.each do |filter|
306
+ if Symbol === filter
307
+ self.send(filter)
308
+ elsif String === filter
309
+ eval(filter, binding)
310
+ elsif filter_block?(filter)
311
+ filter.call(self)
312
+ elsif filter_class?(filter, callback_method)
313
+ filter.send(callback_method, self)
314
+ else
315
+ raise(
316
+ ActiveRecordError,
317
+ "Filters need to be either a symbol, string (to be eval'ed), proc/method, or " +
318
+ "class implementing a static filter method"
319
+ )
320
+ end
321
+ end
322
+ end
323
+
324
+ def filter_block?(filter)
325
+ filter.respond_to?("call") && (filter.arity == 1 || filter.arity == -1)
326
+ end
327
+
328
+ def filter_class?(filter, callback_method)
329
+ filter.respond_to?(callback_method)
330
+ end
331
+
332
+ def notify(callback_method) #:nodoc:
333
+ self.class.changed
334
+ self.class.notify_observers(callback_method, self)
335
+ end
336
+ end
337
+ end
@@ -0,0 +1,326 @@
1
+ require 'benchmark'
2
+ require 'date'
3
+
4
+ module ActiveRecord
5
+ class Base
6
+ class ConnectionSpecification #:nodoc:
7
+ attr_reader :config, :adapter_method
8
+ def initialize (config, adapter_method)
9
+ @config, @adapter_method = config, adapter_method
10
+ end
11
+ end
12
+
13
+ # The class -> [adapter_method, config] map
14
+ @@defined_connections = {}
15
+
16
+ # Establishes the connection to the database. Accepts a hash as input where
17
+ # the :adapter key must be specified with the name of a database adapter (in lower-case)
18
+ # example for regular databases (MySQL, Postgresql, etc):
19
+ #
20
+ # ActiveRecord::Base.establish_connection(
21
+ # :adapter => "mysql",
22
+ # :host => "localhost",
23
+ # :username => "myuser",
24
+ # :password => "mypass",
25
+ # :database => "somedatabase"
26
+ # )
27
+ #
28
+ # Example for SQLite database:
29
+ #
30
+ # ActiveRecord::Base.establish_connection(
31
+ # :adapter => "sqlite",
32
+ # :dbfile => "path/to/dbfile"
33
+ # )
34
+ #
35
+ # Also accepts keys as strings (for parsing from yaml for example):
36
+ # ActiveRecord::Base.establish_connection(
37
+ # "adapter" => "sqlite",
38
+ # "dbfile" => "path/to/dbfile"
39
+ # )
40
+ #
41
+ # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
42
+ # may be returned on an error.
43
+ #
44
+ # == Connecting to another database for a single model
45
+ #
46
+ # To support different connections for different classes, you can
47
+ # simply call establish_connection with the classes you wish to have
48
+ # different connections for:
49
+ #
50
+ # class Courses < ActiveRecord::Base
51
+ # ...
52
+ # end
53
+ #
54
+ # Courses.establish_connection( ... )
55
+ def self.establish_connection(spec)
56
+ if spec.instance_of? ConnectionSpecification
57
+ @@defined_connections[self] = spec
58
+ else
59
+ if spec.nil? then raise AdapterNotSpecified end
60
+ symbolize_strings_in_hash(spec)
61
+ unless spec.key?(:adapter) then raise AdapterNotSpecified end
62
+
63
+ adapter_method = "#{spec[:adapter]}_connection"
64
+ unless methods.include?(adapter_method) then raise AdapterNotFound end
65
+ remove_connection
66
+ @@defined_connections[self] = ConnectionSpecification.new(spec, adapter_method)
67
+ end
68
+ end
69
+
70
+ # Locate the connection of the nearest super class. This can be an
71
+ # active or defined connections: if it is the latter, it will be
72
+ # opened and set as the active connection for the class it was defined
73
+ # for (not necessarily the current class).
74
+ def self.retrieve_connection #:nodoc:
75
+ klass = self
76
+ until klass == ActiveRecord::Base.superclass
77
+ Thread.current['active_connections'] ||= {}
78
+ if Thread.current['active_connections'][klass]
79
+ return Thread.current['active_connections'][klass]
80
+ elsif @@defined_connections[klass]
81
+ klass.connection = @@defined_connections[klass]
82
+ return self.connection
83
+ end
84
+ klass = klass.superclass
85
+ end
86
+ raise ConnectionNotEstablished
87
+ end
88
+
89
+ # Returns true if a connection that's accessible to this class have already been opened.
90
+ def self.connected?
91
+ klass = self
92
+ until klass == ActiveRecord::Base.superclass
93
+ if Thread.current['active_connections'].is_a?(Hash) && Thread.current['active_connections'][klass]
94
+ return true
95
+ else
96
+ klass = klass.superclass
97
+ end
98
+ end
99
+ return false
100
+ end
101
+
102
+ # Remove the connection for this class. This will close the active
103
+ # connection and the defined connection (if they exist). The result
104
+ # can be used as argument for establish_connection, for easy
105
+ # re-establishing of the connection.
106
+ def self.remove_connection(klass=self)
107
+ conn = @@defined_connections[klass]
108
+ @@defined_connections.delete(klass)
109
+ Thread.current['active_connections'] ||= {}
110
+ Thread.current['active_connections'][klass] = nil
111
+ conn.config if conn
112
+ end
113
+
114
+ # Set the connection for the class.
115
+ def self.connection=(spec)
116
+ raise ConnectionNotEstablished unless spec
117
+ conn = self.send(spec.adapter_method, spec.config)
118
+ Thread.current['active_connections'] ||= {}
119
+ Thread.current['active_connections'][self] = conn
120
+ end
121
+
122
+ # Converts all strings in a hash to symbols.
123
+ def self.symbolize_strings_in_hash(hash)
124
+ hash.each do |key, value|
125
+ if key.class == String
126
+ hash.delete key
127
+ hash[key.intern] = value
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ module ConnectionAdapters # :nodoc:
134
+ class Column # :nodoc:
135
+ attr_reader :name, :default, :type, :limit
136
+ # The name should contain the name of the column, such as "name" in "name varchar(250)"
137
+ # The default should contain the type-casted default of the column, such as 1 in "count int(11) DEFAULT 1"
138
+ # The type parameter should either contain :integer, :float, :datetime, :date, :text, or :string
139
+ # The sql_type is just used for extracting the limit, such as 10 in "varchar(10)"
140
+ def initialize(name, default, sql_type = nil)
141
+ @name, @default, @type = name, default, simplified_type(sql_type)
142
+ @limit = extract_limit(sql_type) unless sql_type.nil?
143
+ end
144
+
145
+ def default
146
+ type_cast(@default)
147
+ end
148
+
149
+ def klass
150
+ case type
151
+ when :integer then Fixnum
152
+ when :float then Float
153
+ when :datetime then Time
154
+ when :date then Date
155
+ when :text, :string then String
156
+ when :boolean then Object
157
+ end
158
+ end
159
+
160
+ def type_cast(value)
161
+ if value.nil? then return nil end
162
+ case type
163
+ when :string then value
164
+ when :text then value
165
+ when :integer then value.to_i
166
+ when :float then value.to_f
167
+ when :datetime then string_to_time(value)
168
+ when :date then string_to_date(value)
169
+ when :boolean then (value == "t" or value == true ? true : false)
170
+ else value
171
+ end
172
+ end
173
+
174
+ def human_name
175
+ Base.human_attribute_name(@name)
176
+ end
177
+
178
+ private
179
+ def string_to_date(string)
180
+ return string if Date === string
181
+ date_array = ParseDate.parsedate(string)
182
+ # treat 0000-00-00 as nil
183
+ Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
184
+ end
185
+
186
+ def string_to_time(string)
187
+ return string if Time === string
188
+ time_array = ParseDate.parsedate(string).compact
189
+ # treat 0000-00-00 00:00:00 as nil
190
+ Time.local(*time_array) rescue nil
191
+ end
192
+
193
+ def extract_limit(sql_type)
194
+ $1.to_i if sql_type =~ /\((.*)\)/
195
+ end
196
+
197
+ def simplified_type(field_type)
198
+ case field_type
199
+ when /int/i
200
+ :integer
201
+ when /float|double|decimal|numeric/i
202
+ :float
203
+ when /time/i
204
+ :datetime
205
+ when /date/i
206
+ :date
207
+ when /(c|b)lob/i, /text/i
208
+ :text
209
+ when /char/i, /string/i
210
+ :string
211
+ when /boolean/i
212
+ :boolean
213
+ end
214
+ end
215
+ end
216
+
217
+ # All the concrete database adapters follow the interface laid down in this class.
218
+ # You can use this interface directly by borrowing the database connection from the Base with
219
+ # Base.connection.
220
+ class AbstractAdapter
221
+ @@row_even = true
222
+
223
+ include Benchmark
224
+
225
+ def initialize(connection, logger = nil) # :nodoc:
226
+ @connection, @logger = connection, logger
227
+ @runtime = 0
228
+ end
229
+
230
+ # Returns an array of record hashes with the column names as a keys and fields as values.
231
+ def select_all(sql, name = nil) end
232
+
233
+ # Returns a record hash with the column names as a keys and fields as values.
234
+ def select_one(sql, name = nil) end
235
+
236
+ # Returns an array of column objects for the table specified by +table_name+.
237
+ def columns(table_name, name = nil) end
238
+
239
+ # Returns the last auto-generated ID from the affected table.
240
+ def insert(sql, name = nil, pk = nil, id_value = nil) end
241
+
242
+ # Executes the update statement.
243
+ def update(sql, name = nil) end
244
+
245
+ # Executes the delete statement.
246
+ def delete(sql, name = nil) end
247
+
248
+ def reset_runtime # :nodoc:
249
+ rt = @runtime
250
+ @runtime = 0
251
+ return rt
252
+ end
253
+
254
+ # Begins the transaction (and turns off auto-committing).
255
+ def begin_db_transaction() end
256
+
257
+ # Commits the transaction (and turns on auto-committing).
258
+ def commit_db_transaction() end
259
+
260
+ # Rollsback the transaction (and turns on auto-committing). Must be done if the transaction block
261
+ # raises an exception or returns false.
262
+ def rollback_db_transaction() end
263
+
264
+ def quote(value, column = nil)
265
+ case value
266
+ when String then "'#{value.gsub(/\\/,'\&\&').gsub(/'/, "''")}'" # ' (for ruby-mode)
267
+ when NilClass then "NULL"
268
+ when TrueClass then (column && column.type == :boolean ? "'t'" : "1")
269
+ when FalseClass then (column && column.type == :boolean ? "'f'" : "0")
270
+ when Float, Fixnum, Bignum, Date then "'#{value.to_s}'"
271
+ when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
272
+ else "'#{value.to_yaml.gsub(/'/, "''")}'"
273
+ end
274
+ end
275
+
276
+ def quote_column_name(name)
277
+ return name
278
+ end
279
+
280
+ # Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database.
281
+ def structure_dump() end
282
+
283
+ protected
284
+ def log(sql, name, connection, &action)
285
+ begin
286
+ if @logger.nil?
287
+ action.call(connection)
288
+ else
289
+ result = nil
290
+ bm = measure { result = action.call(connection) }
291
+ @runtime += bm.real
292
+ log_info(sql, name, bm.real)
293
+ result
294
+ end
295
+ rescue => e
296
+ log_info("#{e.message}: #{sql}", name, 0)
297
+ raise ActiveRecord::StatementInvalid, "#{e.message}: #{sql}"
298
+ end
299
+ end
300
+
301
+ def log_info(sql, name, runtime)
302
+ if @logger.nil? then return end
303
+
304
+ @logger.info(
305
+ format_log_entry(
306
+ "#{name.nil? ? "SQL" : name} (#{sprintf("%f", runtime)})",
307
+ sql.gsub(/ +/, " ")
308
+ )
309
+ )
310
+ end
311
+
312
+ def format_log_entry(message, dump = nil)
313
+ if @@row_even then
314
+ @@row_even = false; caller_color = "1;32"; message_color = "4;33"; dump_color = "1;37"
315
+ else
316
+ @@row_even = true; caller_color = "1;36"; message_color = "4;35"; dump_color = "0;37"
317
+ end
318
+
319
+ log_entry = " \e[#{message_color}m#{message}\e[m"
320
+ log_entry << " \e[#{dump_color}m%s\e[m" % dump if dump.kind_of?(String) && !dump.nil?
321
+ log_entry << " \e[#{dump_color}m%p\e[m" % dump if !dump.kind_of?(String) && !dump.nil?
322
+ log_entry
323
+ end
324
+ end
325
+ end
326
+ end