activegroonga 0.0.1

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/AUTHORS +1 -0
  2. data/NEWS.ja.rdoc +5 -0
  3. data/NEWS.rdoc +5 -0
  4. data/README.ja.rdoc +49 -0
  5. data/README.rdoc +49 -0
  6. data/Rakefile +175 -0
  7. data/lib/active_groonga.rb +75 -0
  8. data/lib/active_groonga/aggregations.rb +30 -0
  9. data/lib/active_groonga/associations.rb +93 -0
  10. data/lib/active_groonga/associations/belongs_to_association.rb +25 -0
  11. data/lib/active_groonga/attribute_methods.rb +36 -0
  12. data/lib/active_groonga/base.rb +1579 -0
  13. data/lib/active_groonga/column.rb +107 -0
  14. data/lib/active_groonga/dirty.rb +30 -0
  15. data/lib/active_groonga/fixtures.rb +92 -0
  16. data/lib/active_groonga/migration.rb +150 -0
  17. data/lib/active_groonga/rails_support.rb +31 -0
  18. data/lib/active_groonga/reflection.rb +30 -0
  19. data/lib/active_groonga/schema.rb +314 -0
  20. data/lib/active_groonga/schema_dumper.rb +147 -0
  21. data/lib/active_groonga/tasks.rb +16 -0
  22. data/lib/active_groonga/tasks/groonga.rake +162 -0
  23. data/lib/active_groonga/test_case.rb +21 -0
  24. data/lib/active_groonga/test_help.rb +21 -0
  25. data/lib/active_groonga/timestamp.rb +30 -0
  26. data/lib/active_groonga/validations.rb +26 -0
  27. data/lib/active_groonga/version.rb +24 -0
  28. data/license/LGPL +504 -0
  29. data/rails/README +28 -0
  30. data/rails/init.rb +70 -0
  31. data/rails_generators/model_groonga/USAGE +28 -0
  32. data/rails_generators/model_groonga/model_groonga_generator.rb +45 -0
  33. data/rails_generators/model_groonga/templates/fixtures.yml +17 -0
  34. data/rails_generators/model_groonga/templates/migration.rb +16 -0
  35. data/rails_generators/model_groonga/templates/model.rb +2 -0
  36. data/rails_generators/model_groonga/templates/unit_test.rb +8 -0
  37. data/test-unit/Rakefile +35 -0
  38. data/test-unit/TODO +5 -0
  39. data/test-unit/bin/testrb +5 -0
  40. data/test-unit/html/classic.html +15 -0
  41. data/test-unit/html/index.html +25 -0
  42. data/test-unit/html/index.html.ja +27 -0
  43. data/test-unit/lib/test/unit.rb +342 -0
  44. data/test-unit/lib/test/unit/assertionfailederror.rb +14 -0
  45. data/test-unit/lib/test/unit/assertions.rb +1149 -0
  46. data/test-unit/lib/test/unit/attribute.rb +125 -0
  47. data/test-unit/lib/test/unit/autorunner.rb +306 -0
  48. data/test-unit/lib/test/unit/collector.rb +43 -0
  49. data/test-unit/lib/test/unit/collector/descendant.rb +23 -0
  50. data/test-unit/lib/test/unit/collector/dir.rb +108 -0
  51. data/test-unit/lib/test/unit/collector/load.rb +135 -0
  52. data/test-unit/lib/test/unit/collector/objectspace.rb +34 -0
  53. data/test-unit/lib/test/unit/color-scheme.rb +86 -0
  54. data/test-unit/lib/test/unit/color.rb +96 -0
  55. data/test-unit/lib/test/unit/diff.rb +538 -0
  56. data/test-unit/lib/test/unit/error.rb +124 -0
  57. data/test-unit/lib/test/unit/exceptionhandler.rb +39 -0
  58. data/test-unit/lib/test/unit/failure.rb +110 -0
  59. data/test-unit/lib/test/unit/fixture.rb +176 -0
  60. data/test-unit/lib/test/unit/notification.rb +125 -0
  61. data/test-unit/lib/test/unit/omission.rb +143 -0
  62. data/test-unit/lib/test/unit/pending.rb +146 -0
  63. data/test-unit/lib/test/unit/priority.rb +161 -0
  64. data/test-unit/lib/test/unit/runner/console.rb +52 -0
  65. data/test-unit/lib/test/unit/runner/emacs.rb +8 -0
  66. data/test-unit/lib/test/unit/testcase.rb +360 -0
  67. data/test-unit/lib/test/unit/testresult.rb +89 -0
  68. data/test-unit/lib/test/unit/testsuite.rb +110 -0
  69. data/test-unit/lib/test/unit/ui/console/outputlevel.rb +14 -0
  70. data/test-unit/lib/test/unit/ui/console/testrunner.rb +220 -0
  71. data/test-unit/lib/test/unit/ui/emacs/testrunner.rb +49 -0
  72. data/test-unit/lib/test/unit/ui/testrunner.rb +20 -0
  73. data/test-unit/lib/test/unit/ui/testrunnermediator.rb +77 -0
  74. data/test-unit/lib/test/unit/ui/testrunnerutilities.rb +41 -0
  75. data/test-unit/lib/test/unit/util/backtracefilter.rb +41 -0
  76. data/test-unit/lib/test/unit/util/method-owner-finder.rb +28 -0
  77. data/test-unit/lib/test/unit/util/observable.rb +90 -0
  78. data/test-unit/lib/test/unit/util/procwrapper.rb +48 -0
  79. data/test-unit/lib/test/unit/version.rb +7 -0
  80. data/test-unit/sample/adder.rb +13 -0
  81. data/test-unit/sample/subtracter.rb +12 -0
  82. data/test-unit/sample/tc_adder.rb +18 -0
  83. data/test-unit/sample/tc_subtracter.rb +18 -0
  84. data/test-unit/sample/test_user.rb +22 -0
  85. data/test-unit/sample/ts_examples.rb +7 -0
  86. data/test-unit/test/collector/test-descendant.rb +135 -0
  87. data/test-unit/test/collector/test-load.rb +333 -0
  88. data/test-unit/test/collector/test_dir.rb +406 -0
  89. data/test-unit/test/collector/test_objectspace.rb +98 -0
  90. data/test-unit/test/run-test.rb +13 -0
  91. data/test-unit/test/test-attribute.rb +86 -0
  92. data/test-unit/test/test-color-scheme.rb +56 -0
  93. data/test-unit/test/test-color.rb +47 -0
  94. data/test-unit/test/test-diff.rb +477 -0
  95. data/test-unit/test/test-emacs-runner.rb +60 -0
  96. data/test-unit/test/test-fixture.rb +287 -0
  97. data/test-unit/test/test-notification.rb +33 -0
  98. data/test-unit/test/test-omission.rb +81 -0
  99. data/test-unit/test/test-pending.rb +70 -0
  100. data/test-unit/test/test-priority.rb +119 -0
  101. data/test-unit/test/test_assertions.rb +1082 -0
  102. data/test-unit/test/test_error.rb +26 -0
  103. data/test-unit/test/test_failure.rb +33 -0
  104. data/test-unit/test/test_testcase.rb +478 -0
  105. data/test-unit/test/test_testresult.rb +113 -0
  106. data/test-unit/test/test_testsuite.rb +129 -0
  107. data/test-unit/test/testunit-test-util.rb +14 -0
  108. data/test-unit/test/ui/test_testrunmediator.rb +20 -0
  109. data/test-unit/test/util/test-method-owner-finder.rb +38 -0
  110. data/test-unit/test/util/test_backtracefilter.rb +41 -0
  111. data/test-unit/test/util/test_observable.rb +102 -0
  112. data/test-unit/test/util/test_procwrapper.rb +36 -0
  113. data/test/active-groonga-test-utils.rb +234 -0
  114. data/test/fixtures/bookmark.rb +2 -0
  115. data/test/fixtures/task.rb +2 -0
  116. data/test/fixtures/user.rb +2 -0
  117. data/test/run-test.rb +51 -0
  118. data/test/test-associations.rb +24 -0
  119. data/test/test-base.rb +194 -0
  120. data/test/test-schema.rb +49 -0
  121. metadata +192 -0
@@ -0,0 +1,25 @@
1
+ # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+
16
+ module ActiveGroonga
17
+ module Associations
18
+ class BelongsToAssociation < ActiveRecord::Associations::BelongsToAssociation
19
+ def find_target
20
+ @reflection.klass.find(@owner.id,
21
+ :readonly => @reflection.options[:readonly])
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+
16
+ module ActiveGroonga
17
+ module AttributeMethods
18
+ def self.included(base)
19
+ base.module_eval do
20
+ include ActiveRecord::AttributeMethods
21
+ extend AttributeMethods::ClassMethods
22
+ end
23
+ end
24
+
25
+ module ClassMethods
26
+ def instance_method_already_implemented?(method_name)
27
+ method_name = method_name.to_s
28
+ return true if method_name =~ /^id(=$|\?$|$)/
29
+ @_defined_class_methods ||= ancestors.first(ancestors.index(ActiveGroonga::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map(&:to_s).to_set
30
+ @@_defined_activegroonga_methods ||= (ActiveGroonga::Base.public_instance_methods(false) | ActiveGroonga::Base.private_instance_methods(false) | ActiveGroonga::Base.protected_instance_methods(false)).map(&:to_s).to_set
31
+ raise DangerousAttributeError, "#{method_name} is defined by ActiveGroonga" if @@_defined_activegroonga_methods.include?(method_name)
32
+ @_defined_class_methods.include?(method_name)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,1579 @@
1
+ # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+
16
+ # This library includes ActiveRecord based codes temporary.
17
+ # Here is their copyright and license:
18
+ #
19
+ # Copyright (c) 2004-2009 David Heinemeier Hansson
20
+ #
21
+ # Permission is hereby granted, free of charge, to any person obtaining
22
+ # a copy of this software and associated documentation files (the
23
+ # "Software"), to deal in the Software without restriction, including
24
+ # without limitation the rights to use, copy, modify, merge, publish,
25
+ # distribute, sublicense, and/or sell copies of the Software, and to
26
+ # permit persons to whom the Software is furnished to do so, subject to
27
+ # the following conditions:
28
+ #
29
+ # The above copyright notice and this permission notice shall be
30
+ # included in all copies or substantial portions of the Software.
31
+ #
32
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39
+
40
+ require 'active_record/base'
41
+
42
+ module ActiveGroonga
43
+ # Generic ActiveGroonga exception class.
44
+ class ActiveGroongaError < StandardError
45
+ end
46
+
47
+ # Raised when ActiveGroonga cannot find record by given id or set of ids.
48
+ class RecordNotFound < ActiveGroongaError
49
+ end
50
+
51
+ # Raised when database not specified (or configuration file <tt>config/groonga.yml</tt> misses database field).
52
+ class DatabaseNotSpecified < ActiveGroongaError
53
+ end
54
+
55
+ class Base
56
+ ##
57
+ # :singleton-method:
58
+ # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
59
+ # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
60
+ cattr_accessor :logger, :instance_writer => false
61
+
62
+ ##
63
+ # :singleton-method:
64
+ # Contains the groonga configuration - as is typically stored in config/groonga.yml -
65
+ # as a Hash.
66
+ #
67
+ # For example, the following groonga.yml...
68
+ #
69
+ # development:
70
+ # database: db/development.groonga
71
+ #
72
+ # production:
73
+ # adapter: groonga
74
+ # database: db/production.groonga
75
+ #
76
+ # ...would result in ActiveGroonga::Base.configurations to look like this:
77
+ #
78
+ # {
79
+ # 'development' => {
80
+ # 'database' => 'db/development.groonga'
81
+ # },
82
+ # 'production' => {
83
+ # 'database' => 'db/production.groonga'
84
+ # }
85
+ # }
86
+ cattr_accessor :configurations, :instance_writer => false
87
+ @@configurations = {}
88
+
89
+ ##
90
+ # :singleton-method:
91
+ # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
92
+ # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
93
+ # for tables in a shared database. By default, the prefix is the empty string.
94
+ cattr_accessor :table_name_prefix, :instance_writer => false
95
+ @@table_name_prefix = ""
96
+
97
+ ##
98
+ # :singleton-method:
99
+ # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
100
+ # "people_basecamp"). By default, the suffix is the empty string.
101
+ cattr_accessor :table_name_suffix, :instance_writer => false
102
+ @@table_name_suffix = ""
103
+
104
+ ##
105
+ # :singleton-method:
106
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
107
+ # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
108
+ # See table_name for the full rules on table/class naming. This is true, by default.
109
+ cattr_accessor :pluralize_table_names, :instance_writer => false
110
+ @@pluralize_table_names = true
111
+
112
+ ##
113
+ # :singleton-method:
114
+ # Determines whether to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors
115
+ # make it much easier to overview things during debugging (when used through a reader like +tail+ and on a black background), but
116
+ # may complicate matters if you use software like syslog. This is true, by default.
117
+ cattr_accessor :colorize_logging, :instance_writer => false
118
+ @@colorize_logging = true
119
+
120
+ ##
121
+ # :singleton-method:
122
+ # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database.
123
+ # This is set to :local by default.
124
+ cattr_accessor :default_timezone, :instance_writer => false
125
+ @@default_timezone = :local
126
+
127
+ # Determine whether to store the full constant name including namespace when using STI
128
+ superclass_delegating_accessor :store_full_sti_class
129
+ self.store_full_sti_class = false
130
+
131
+ # Stores the default scope for the class
132
+ class_inheritable_accessor :default_scoping, :instance_writer => false
133
+ self.default_scoping = []
134
+
135
+ ##
136
+ # :singleton-method:
137
+ # Specifies the format to use when dumping the database schema with Rails'
138
+ # Rakefile. If :sql, the schema is dumped as (potentially database-
139
+ # specific) SQL statements. If :ruby, the schema is dumped as an
140
+ # ActiveRecord::Schema file which can be loaded into any database that
141
+ # supports migrations. Use :ruby if you want to have different database
142
+ # adapters for, e.g., your development and test environments.
143
+ cattr_accessor :schema_format , :instance_writer => false
144
+ @@schema_format = :ruby
145
+
146
+ cattr_accessor :database_directory, :instance_writer => false
147
+ @@database_directory = nil
148
+
149
+ class << self
150
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
151
+ # The resulting object is returned whether the object was saved successfully to the database or not.
152
+ #
153
+ # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
154
+ # attributes on the objects that are to be created.
155
+ #
156
+ # ==== Examples
157
+ # # Create a single new object
158
+ # User.create(:first_name => 'Jamie')
159
+ #
160
+ # # Create an Array of new objects
161
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
162
+ #
163
+ # # Create a single object and pass it into a block to set other attributes.
164
+ # User.create(:first_name => 'Jamie') do |u|
165
+ # u.is_admin = false
166
+ # end
167
+ #
168
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
169
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
170
+ # u.is_admin = false
171
+ # end
172
+ def create(attributes = nil, &block)
173
+ if attributes.is_a?(Array)
174
+ attributes.collect { |attr| create(attr, &block) }
175
+ else
176
+ object = new(attributes)
177
+ yield(object) if block_given?
178
+ object.save
179
+ object
180
+ end
181
+ end
182
+
183
+ # Attributes named in this macro are protected from mass-assignment,
184
+ # such as <tt>new(attributes)</tt>,
185
+ # <tt>update_attributes(attributes)</tt>, or
186
+ # <tt>attributes=(attributes)</tt>.
187
+ #
188
+ # Mass-assignment to these attributes will simply be ignored, to assign
189
+ # to them you can use direct writer methods. This is meant to protect
190
+ # sensitive attributes from being overwritten by malicious users
191
+ # tampering with URLs or forms.
192
+ #
193
+ # class Customer < ActiveRecord::Base
194
+ # attr_protected :credit_rating
195
+ # end
196
+ #
197
+ # customer = Customer.new("name" => David, "credit_rating" => "Excellent")
198
+ # customer.credit_rating # => nil
199
+ # customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
200
+ # customer.credit_rating # => nil
201
+ #
202
+ # customer.credit_rating = "Average"
203
+ # customer.credit_rating # => "Average"
204
+ #
205
+ # To start from an all-closed default and enable attributes as needed,
206
+ # have a look at +attr_accessible+.
207
+ def attr_protected(*attributes)
208
+ write_inheritable_attribute(:attr_protected, Set.new(attributes.map(&:to_s)) + (protected_attributes || []))
209
+ end
210
+
211
+ # Returns an array of all the attributes that have been protected from mass-assignment.
212
+ def protected_attributes # :nodoc:
213
+ read_inheritable_attribute(:attr_protected)
214
+ end
215
+
216
+ # Specifies a white list of model attributes that can be set via
217
+ # mass-assignment, such as <tt>new(attributes)</tt>,
218
+ # <tt>update_attributes(attributes)</tt>, or
219
+ # <tt>attributes=(attributes)</tt>
220
+ #
221
+ # This is the opposite of the +attr_protected+ macro: Mass-assignment
222
+ # will only set attributes in this list, to assign to the rest of
223
+ # attributes you can use direct writer methods. This is meant to protect
224
+ # sensitive attributes from being overwritten by malicious users
225
+ # tampering with URLs or forms. If you'd rather start from an all-open
226
+ # default and restrict attributes as needed, have a look at
227
+ # +attr_protected+.
228
+ #
229
+ # class Customer < ActiveRecord::Base
230
+ # attr_accessible :name, :nickname
231
+ # end
232
+ #
233
+ # customer = Customer.new(:name => "David", :nickname => "Dave", :credit_rating => "Excellent")
234
+ # customer.credit_rating # => nil
235
+ # customer.attributes = { :name => "Jolly fellow", :credit_rating => "Superb" }
236
+ # customer.credit_rating # => nil
237
+ #
238
+ # customer.credit_rating = "Average"
239
+ # customer.credit_rating # => "Average"
240
+ def attr_accessible(*attributes)
241
+ write_inheritable_attribute(:attr_accessible, Set.new(attributes.map(&:to_s)) + (accessible_attributes || []))
242
+ end
243
+
244
+ # Returns an array of all the attributes that have been made accessible to mass-assignment.
245
+ def accessible_attributes # :nodoc:
246
+ read_inheritable_attribute(:attr_accessible)
247
+ end
248
+
249
+ # Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.
250
+ def attr_readonly(*attributes)
251
+ write_inheritable_attribute(:attr_readonly, Set.new(attributes.map(&:to_s)) + (readonly_attributes || []))
252
+ end
253
+
254
+ # Returns an array of all the attributes that have been specified as readonly.
255
+ def readonly_attributes
256
+ read_inheritable_attribute(:attr_readonly)
257
+ end
258
+
259
+
260
+ # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
261
+ # then specify the name of that attribute using this method and it will be handled automatically.
262
+ # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
263
+ # class on retrieval or SerializationTypeMismatch will be raised.
264
+ #
265
+ # ==== Parameters
266
+ #
267
+ # * +attr_name+ - The field name that should be serialized.
268
+ # * +class_name+ - Optional, class name that the object type should be equal to.
269
+ #
270
+ # ==== Example
271
+ # # Serialize a preferences attribute
272
+ # class User
273
+ # serialize :preferences
274
+ # end
275
+ def serialize(attr_name, class_name = Object)
276
+ serialized_attributes[attr_name.to_s] = class_name
277
+ end
278
+
279
+ # Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values.
280
+ def serialized_attributes
281
+ read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {})
282
+ end
283
+
284
+ # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
285
+ # directly from ActiveGroonga::Base. So if the hierarchy looks like: Reply < Message < ActiveGroonga::Base, then Message is used
286
+ # to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class
287
+ # in Active Support, which knows almost all common English inflections. You can add new inflections in config/initializers/inflections.rb.
288
+ #
289
+ # Nested classes are given table names prefixed by the singular form of
290
+ # the parent's table name. Enclosing modules are not considered.
291
+ #
292
+ # ==== Examples
293
+ #
294
+ # class Invoice < ActiveGroonga::Base; end;
295
+ # file class table_name
296
+ # invoice.rb Invoice invoices
297
+ #
298
+ # class Invoice < ActiveGroonga::Base; class Lineitem < ActiveGroonga::Base; end; end;
299
+ # file class table_name
300
+ # invoice.rb Invoice::Lineitem invoice_lineitems
301
+ #
302
+ # module Invoice; class Lineitem < ActiveGroonga::Base; end; end;
303
+ # file class table_name
304
+ # invoice/lineitem.rb Invoice::Lineitem lineitems
305
+ #
306
+ # Additionally, the class-level +table_name_prefix+ is prepended and the
307
+ # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
308
+ # the table name guess for an Invoice class becomes "myapp_invoices".
309
+ # Invoice::Lineitem becomes "myapp_invoice_lineitems".
310
+ #
311
+ # You can also overwrite this class method to allow for unguessable
312
+ # links, such as a Mouse class with a link to a "mice" table. Example:
313
+ #
314
+ # class Mouse < ActiveGroonga::Base
315
+ # set_table_name "mice"
316
+ # end
317
+ def table_name
318
+ reset_table_name
319
+ end
320
+
321
+ def reset_table_name #:nodoc:
322
+ base = base_class
323
+
324
+ name =
325
+ # STI subclasses always use their superclass' table.
326
+ unless self == base
327
+ base.table_name
328
+ else
329
+ # Nested classes are prefixed with singular parent table name.
330
+ if parent < ActiveGroonga::Base && !parent.abstract_class?
331
+ contained = parent.table_name
332
+ contained = contained.singularize if parent.pluralize_table_names
333
+ contained << '_'
334
+ end
335
+ name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
336
+ end
337
+
338
+ set_table_name(name)
339
+ name
340
+ end
341
+
342
+ # Defines the column name for use with single table inheritance
343
+ # -- can be set in subclasses like so: self.inheritance_column = "type_id"
344
+ def inheritance_column
345
+ @inheritance_column ||= "type".freeze
346
+ end
347
+
348
+ # Sets the table name to use to the given value, or (if the value
349
+ # is nil or false) to the value returned by the given block.
350
+ #
351
+ # class Project < ActiveGroonga::Base
352
+ # set_table_name "project"
353
+ # end
354
+ def set_table_name(value = nil, &block)
355
+ define_attr_method :table_name, value, &block
356
+ end
357
+ alias :table_name= :set_table_name
358
+
359
+ # Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
360
+ def class_name(table_name = table_name) # :nodoc:
361
+ # remove any prefix and/or suffix from the table name
362
+ class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
363
+ class_name = class_name.singularize if pluralize_table_names
364
+ class_name
365
+ end
366
+
367
+ # Indicates whether the table associated with this class exists
368
+ def table_exists?
369
+ not table.nil?
370
+ end
371
+
372
+ def primary_key
373
+ "id"
374
+ end
375
+
376
+ # Returns an array of column objects for the table associated with this class.
377
+ def columns
378
+ @columns ||= table.columns.collect do |column|
379
+ Column.new(column)
380
+ end
381
+ end
382
+
383
+ # Returns a hash of column objects for the table associated with this class.
384
+ def columns_hash
385
+ @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
386
+ end
387
+
388
+ # Returns an array of column names as strings.
389
+ def column_names
390
+ @column_names ||= columns.map { |column| column.name }
391
+ end
392
+
393
+ # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
394
+ # and columns used for single table inheritance have been removed.
395
+ def content_columns
396
+ @content_columns ||= columns.reject do |c|
397
+ c.primary || c.type == :references || c.name == inheritance_column
398
+ end
399
+ end
400
+
401
+ # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
402
+ # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
403
+ # is available.
404
+ def column_methods_hash #:nodoc:
405
+ @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
406
+ attr_name = attr.to_s
407
+ methods[attr.to_sym] = attr_name
408
+ methods["#{attr}=".to_sym] = attr_name
409
+ methods["#{attr}?".to_sym] = attr_name
410
+ methods["#{attr}_before_type_cast".to_sym] = attr_name
411
+ methods
412
+ end
413
+ end
414
+
415
+ # True if this isn't a concrete subclass needing a STI type condition.
416
+ def descends_from_active_groonga?
417
+ if superclass.abstract_class?
418
+ superclass.descends_from_active_groonga?
419
+ else
420
+ superclass == Base || !columns_hash.include?(inheritance_column)
421
+ end
422
+ end
423
+
424
+ # Returns a string like 'Post id:integer, title:string, body:text'
425
+ def inspect
426
+ if self == Base
427
+ super
428
+ elsif abstract_class?
429
+ "#{super}(abstract)"
430
+ elsif table_exists?
431
+ attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
432
+ "#{super}(#{attr_list})"
433
+ else
434
+ "#{super}(Table doesn't exist)"
435
+ end
436
+ end
437
+
438
+ # Log and benchmark multiple statements in a single block. Example:
439
+ #
440
+ # Project.benchmark("Creating project") do
441
+ # project = Project.create("name" => "stuff")
442
+ # project.create_manager("name" => "David")
443
+ # project.milestones << Milestone.find(:all)
444
+ # end
445
+ #
446
+ # The benchmark is only recorded if the current level of the logger is less than or equal to the <tt>log_level</tt>,
447
+ # which makes it easy to include benchmarking statements in production software that will remain inexpensive because
448
+ # the benchmark will only be conducted if the log level is low enough.
449
+ #
450
+ # The logging of the multiple statements is turned off unless <tt>use_silence</tt> is set to false.
451
+ def benchmark(title, log_level=Logger::DEBUG, use_silence=true)
452
+ if logger && logger.level <= log_level
453
+ result = nil
454
+ ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
455
+ logger.add(log_level, '%s (%.1fms)' % [title, ms])
456
+ result
457
+ else
458
+ yield
459
+ end
460
+ end
461
+
462
+ # Overwrite the default class equality method to provide support for association proxies.
463
+ def ===(object)
464
+ object.is_a?(self)
465
+ end
466
+
467
+ # Returns the base AR subclass that this class descends from. If A
468
+ # extends AR::Base, A.base_class will return A. If B descends from A
469
+ # through some arbitrarily deep hierarchy, B.base_class will return A.
470
+ def base_class
471
+ class_of_active_groonga_descendant(self)
472
+ end
473
+
474
+ # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
475
+ attr_accessor :abstract_class
476
+
477
+ # Returns whether this class is a base AR class. If A is a base class and
478
+ # B descends from A, then B.base_class will return B.
479
+ def abstract_class?
480
+ defined?(@abstract_class) && @abstract_class == true
481
+ end
482
+
483
+ def find(*args)
484
+ options = args.extract_options!
485
+ validate_find_options(options)
486
+ set_readonly_option!(options)
487
+
488
+ case args.first
489
+ when :first
490
+ find_initial(options)
491
+ when :last
492
+ find_last(options)
493
+ when :all
494
+ find_every(options)
495
+ else
496
+ find_from_ids(args, options)
497
+ end
498
+ end
499
+
500
+ # A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass in all the
501
+ # same arguments to this method as you can to <tt>find(:first)</tt>.
502
+ def first(*args)
503
+ find(:first, *args)
504
+ end
505
+
506
+ # A convenience wrapper for <tt>find(:last, *args)</tt>. You can pass in all the
507
+ # same arguments to this method as you can to <tt>find(:last)</tt>.
508
+ def last(*args)
509
+ find(:last, *args)
510
+ end
511
+
512
+ # This is an alias for find(:all). You can pass in all the same arguments to this method as you can
513
+ # to find(:all)
514
+ def all(*args)
515
+ find(:all, *args)
516
+ end
517
+
518
+ def context
519
+ Groonga::Context.default
520
+ end
521
+
522
+ def database
523
+ context.database
524
+ end
525
+
526
+ def table
527
+ context[groonga_table_name]
528
+ end
529
+
530
+ def groonga_table_name(name=nil)
531
+ "<table:#{name || table_name}>"
532
+ end
533
+
534
+ def groonga_metadata_table_name(name)
535
+ "<metadata:#{name}>"
536
+ end
537
+
538
+ # Defines an "attribute" method (like +inheritance_column+ or
539
+ # +table_name+). A new (class) method will be created with the
540
+ # given name. If a value is specified, the new method will
541
+ # return that value (as a string). Otherwise, the given block
542
+ # will be used to compute the value of the method.
543
+ #
544
+ # The original method will be aliased, with the new name being
545
+ # prefixed with "original_". This allows the new method to
546
+ # access the original value.
547
+ #
548
+ # Example:
549
+ #
550
+ # class A < ActiveRecord::Base
551
+ # define_attr_method :primary_key, "sysid"
552
+ # define_attr_method( :inheritance_column ) do
553
+ # original_inheritance_column + "_id"
554
+ # end
555
+ # end
556
+ def define_attr_method(name, value=nil, &block)
557
+ sing = class << self; self; end
558
+ sing.send :alias_method, "original_#{name}", name
559
+ if block_given?
560
+ sing.send :define_method, name, &block
561
+ else
562
+ # use eval instead of a block to work around a memory leak in dev
563
+ # mode in fcgi
564
+ sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
565
+ end
566
+ end
567
+
568
+ def setup_database(spec=nil)
569
+ case spec
570
+ when nil
571
+ raise DatabaseNotSpecified unless defined? RAILS_ENV
572
+ setup_database(RAILS_ENV)
573
+ when Symbol, String
574
+ if configuration = configurations[spec.to_s]
575
+ setup_database(configuration)
576
+ else
577
+ raise DatabaseNotSpecified, "#{spec} database is not configured"
578
+ end
579
+ else
580
+ spec = spec.symbolize_keys
581
+ unless spec.key?(:database)
582
+ raise DatabaseNotSpecified, "groonga configuration does not specify database"
583
+ end
584
+ database_directory = spec[:database]
585
+
586
+ Groonga::Context.default = nil
587
+ Groonga::Context.default_options = {:encoding => spec[:encoding]}
588
+ unless File.exist?(database_directory)
589
+ FileUtils.mkdir_p(database_directory)
590
+ end
591
+ database_file = File.join(database_directory, "database.groonga")
592
+ if File.exist?(database_file)
593
+ Groonga::Database.new(database_file)
594
+ else
595
+ Groonga::Database.create(:path => database_file)
596
+ end
597
+ self.database_directory = database_directory
598
+ end
599
+ end
600
+
601
+ def tables_directory
602
+ directory = File.join(database_directory, "tables")
603
+ FileUtils.mkdir_p(directory) unless File.exist?(directory)
604
+ directory
605
+ end
606
+
607
+ def columns_directory(table_name)
608
+ directory = File.join(tables_directory, table_name.to_s, "columns")
609
+ FileUtils.mkdir_p(directory) unless File.exist?(directory)
610
+ directory
611
+ end
612
+
613
+ def metadata_directory
614
+ directory = File.join(database_directory, "metadata")
615
+ FileUtils.mkdir_p(directory) unless File.exist?(directory)
616
+ directory
617
+ end
618
+
619
+ def count
620
+ table.size
621
+ end
622
+
623
+ private
624
+ def find_initial(options)
625
+ options.update(:limit => 1)
626
+ find_every(options).first
627
+ end
628
+
629
+ def find_every(options)
630
+ limit = options[:limit] ||= 0
631
+ conditions = (options[:conditions] || {}).stringify_keys
632
+ include_associations = merge_includes(scope(:find, :include), options[:include])
633
+
634
+ if include_associations.any? && references_eager_loaded_tables?(options)
635
+ records = find_with_associations(options)
636
+ else
637
+ records = []
638
+ target_records = []
639
+ original_table = table
640
+ index_records = nil
641
+ Schema.indexes(table_name).each do |index_definition|
642
+ if conditions.has_key?(index_definition.column)
643
+ index_column_name =
644
+ "#{index_definition.table}/#{index_definition.column}"
645
+ index = Schema.index_table.column(index_column_name)
646
+ key = conditions.delete(index_definition.column)
647
+ index_records = index.search(key, :result => index_records)
648
+ end
649
+ end
650
+ if index_records
651
+ sorted_records = index_records.sort([".:score"], :limit => limit)
652
+ limit = sorted_records.size
653
+ target_records = sorted_records.records(:order => :ascending).collect do |record|
654
+ index_record_id = record.value.unpack("i")[0]
655
+ index_record = Groonga::Record.new(index_records, index_record_id)
656
+ target_record = index_record.key
657
+ target_record.instance_variable_set("@score", index_record.score)
658
+ def target_record.score
659
+ @score
660
+ end
661
+ target_record
662
+ end
663
+ else
664
+ target_records = original_table.records
665
+ limit = target_records.size if limit.zero?
666
+ end
667
+ target_records.each_with_index do |record, i|
668
+ break if records.size >= limit
669
+ unless conditions.all? do |name, value|
670
+ record[name] == value or
671
+ (record.reference_column?(name) and record[name].id == value)
672
+ end
673
+ next
674
+ end
675
+ records << instantiate(record)
676
+ end
677
+ if include_associations.any?
678
+ preload_associations(records, include_associations)
679
+ end
680
+ end
681
+
682
+ records.each {|record| record.readonly!} if options[:readonly]
683
+
684
+ records
685
+ end
686
+
687
+ def find_from_ids(ids, options)
688
+ expects_array = ids.first.kind_of?(Array)
689
+ return ids.first if expects_array && ids.first.empty?
690
+
691
+ ids = ids.flatten.compact.uniq
692
+
693
+ case ids.size
694
+ when 0
695
+ raise RecordNotFound, "Couldn't find #{name} without an ID"
696
+ when 1
697
+ result = find_one(ids.first, options)
698
+ expects_array ? [result] : result
699
+ else
700
+ find_some(ids, options)
701
+ end
702
+ end
703
+
704
+ def find_one(id, options)
705
+ if id.is_a?(Groonga::Record)
706
+ record = id
707
+ else
708
+ if id.is_a?(ActiveGroonga::Base)
709
+ id = id.id
710
+ else
711
+ id = Integer(id)
712
+ end
713
+ record = Groonga::Record.new(table, id)
714
+ end
715
+ result = instantiate(record)
716
+ if result.nil?
717
+ raise RecordNotFound, "Couldn't find #{name} with ID=#{id}"
718
+ end
719
+ result
720
+ end
721
+
722
+ def find_some(ids, options)
723
+ result = ids.collect do |id|
724
+ context[id]
725
+ end
726
+ n_not_found_ids = result.count(nil)
727
+ if n_not_found_ids.zero?
728
+ result
729
+ else
730
+ raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids}) (found #{result.compact.size} results, but was looking for #{ids.size})"
731
+ end
732
+ end
733
+
734
+ def merge_includes(first, second)
735
+ (safe_to_array(first) + safe_to_array(second)).uniq
736
+ end
737
+
738
+ # ugly. derived from Active Record. FIXME: remove it.
739
+ def safe_to_array(o)
740
+ case o
741
+ when NilClass
742
+ []
743
+ when Array
744
+ o
745
+ else
746
+ [o]
747
+ end
748
+ end
749
+
750
+ VALID_FIND_OPTIONS = [:conditions, :readonly, :limit]
751
+ def validate_find_options(options)
752
+ options.assert_valid_keys(VALID_FIND_OPTIONS)
753
+ end
754
+
755
+ def set_readonly_option!(options) #:nodoc:
756
+ # Inherit :readonly from finder scope if set. Otherwise,
757
+ # if :joins is not blank then :readonly defaults to true.
758
+ unless options.has_key?(:readonly)
759
+ if scoped_readonly = scope(:find, :readonly)
760
+ options[:readonly] = scoped_readonly
761
+ elsif !options[:joins].blank? && !options[:select]
762
+ options[:readonly] = true
763
+ end
764
+ end
765
+ end
766
+
767
+ # Guesses the table name, but does not decorate it with prefix and suffix information.
768
+ def undecorated_table_name(class_name = base_class.name)
769
+ table_name = class_name.to_s.demodulize.underscore
770
+ table_name = table_name.pluralize if pluralize_table_names
771
+ table_name
772
+ end
773
+
774
+ # Finder methods must instantiate through this method to work with the
775
+ # single-table inheritance model that makes it possible to create
776
+ # objects of different types from the same table.
777
+ def instantiate(record)
778
+ object =
779
+ if subclass_name = record[inheritance_column]
780
+ # No type given.
781
+ if subclass_name.empty?
782
+ allocate
783
+
784
+ else
785
+ # Ignore type if no column is present since it was probably
786
+ # pulled in from a sloppy join.
787
+ unless columns_hash.include?(inheritance_column)
788
+ allocate
789
+
790
+ else
791
+ begin
792
+ compute_type(subclass_name).allocate
793
+ rescue NameError
794
+ raise SubclassNotFound,
795
+ "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
796
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
797
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
798
+ "or overwrite #{self.to_s}.inheritance_column to use another column for that information."
799
+ end
800
+ end
801
+ end
802
+ else
803
+ allocate
804
+ end
805
+
806
+ object.instance_variable_set("@id", record.id)
807
+ object.instance_variable_set("@score", record.score)
808
+ attributes = {}
809
+ record.table.columns.each do |column|
810
+ _, column_name = column.name.split(/\A#{record.table.name}\./, 2)
811
+ attributes[column_name] = column[record.id]
812
+ end
813
+ object.instance_variable_set("@attributes", attributes)
814
+ object.instance_variable_set("@attributes_cache", Hash.new)
815
+
816
+ if object.respond_to_without_attributes?(:after_find)
817
+ object.send(:callback, :after_find)
818
+ end
819
+
820
+ if object.respond_to_without_attributes?(:after_initialize)
821
+ object.send(:callback, :after_initialize)
822
+ end
823
+
824
+ object
825
+ end
826
+
827
+ # Enables dynamic finders like <tt>find_by_user_name(user_name)</tt> and <tt>find_by_user_name_and_password(user_name, password)</tt>
828
+ # that are turned into <tt>find(:first, :conditions => ["user_name = ?", user_name])</tt> and
829
+ # <tt>find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt> respectively. Also works for
830
+ # <tt>find(:all)</tt> by using <tt>find_all_by_amount(50)</tt> that is turned into <tt>find(:all, :conditions => ["amount = ?", 50])</tt>.
831
+ #
832
+ # It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+
833
+ # is actually <tt>find_all_by_amount(amount, options)</tt>.
834
+ #
835
+ # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
836
+ # are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password])
837
+ # respectively.
838
+ #
839
+ # Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
840
+ # attempts to use it do not run through method_missing.
841
+ def method_missing(method_id, *arguments, &block)
842
+ if match = ActiveRecord::DynamicFinderMatch.match(method_id)
843
+ attribute_names = match.attribute_names
844
+ super unless all_attributes_exists?(attribute_names)
845
+ if match.finder?
846
+ finder = match.finder
847
+ bang = match.bang?
848
+ # def self.find_by_login_and_activated(*args)
849
+ # options = args.extract_options!
850
+ # attributes = construct_attributes_from_arguments(
851
+ # [:login,:activated],
852
+ # args
853
+ # )
854
+ # finder_options = { :conditions => attributes }
855
+ # validate_find_options(options)
856
+ # set_readonly_option!(options)
857
+ #
858
+ # if options[:conditions]
859
+ # with_scope(:find => finder_options) do
860
+ # find(:first, options)
861
+ # end
862
+ # else
863
+ # find(:first, options.merge(finder_options))
864
+ # end
865
+ # end
866
+ self.class_eval <<-EOC, __FILE__, __LINE__
867
+ def self.#{method_id}(*args)
868
+ options = args.extract_options!
869
+ attributes = construct_attributes_from_arguments(
870
+ [:#{attribute_names.join(',:')}],
871
+ args
872
+ )
873
+ finder_options = { :conditions => attributes }
874
+ validate_find_options(options)
875
+ set_readonly_option!(options)
876
+
877
+ #{'result = ' if bang}if options[:conditions]
878
+ with_scope(:find => finder_options) do
879
+ find(:#{finder}, options)
880
+ end
881
+ else
882
+ find(:#{finder}, options.merge(finder_options))
883
+ end
884
+ #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang}
885
+ end
886
+ EOC
887
+ send(method_id, *arguments)
888
+ elsif match.instantiator?
889
+ instantiator = match.instantiator
890
+ # def self.find_or_create_by_user_id(*args)
891
+ # guard_protected_attributes = false
892
+ #
893
+ # if args[0].is_a?(Hash)
894
+ # guard_protected_attributes = true
895
+ # attributes = args[0].with_indifferent_access
896
+ # find_attributes = attributes.slice(*[:user_id])
897
+ # else
898
+ # find_attributes = attributes = construct_attributes_from_arguments([:user_id], args)
899
+ # end
900
+ #
901
+ # options = { :conditions => find_attributes }
902
+ # set_readonly_option!(options)
903
+ #
904
+ # record = find(:first, options)
905
+ #
906
+ # if record.nil?
907
+ # record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
908
+ # yield(record) if block_given?
909
+ # record.save
910
+ # record
911
+ # else
912
+ # record
913
+ # end
914
+ # end
915
+ self.class_eval <<-EOC, __FILE__, __LINE__
916
+ def self.#{method_id}(*args)
917
+ guard_protected_attributes = false
918
+
919
+ if args[0].is_a?(Hash)
920
+ guard_protected_attributes = true
921
+ attributes = args[0].with_indifferent_access
922
+ find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}])
923
+ else
924
+ find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
925
+ end
926
+
927
+ options = { :conditions => find_attributes }
928
+ set_readonly_option!(options)
929
+
930
+ record = find(:first, options)
931
+
932
+ if record.nil?
933
+ record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
934
+ #{'yield(record) if block_given?'}
935
+ #{'record.save' if instantiator == :create}
936
+ record
937
+ else
938
+ record
939
+ end
940
+ end
941
+ EOC
942
+ send(method_id, *arguments, &block)
943
+ end
944
+ elsif match = ActiveRecord::DynamicScopeMatch.match(method_id)
945
+ attribute_names = match.attribute_names
946
+ super unless all_attributes_exists?(attribute_names)
947
+ if match.scope?
948
+ self.class_eval <<-EOC, __FILE__, __LINE__
949
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
950
+ options = args.extract_options! # options = args.extract_options!
951
+ attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
952
+ [:#{attribute_names.join(',:')}], args # [:user_name, :password], args
953
+ ) # )
954
+ #
955
+ scoped(:conditions => attributes) # scoped(:conditions => attributes)
956
+ end # end
957
+ EOC
958
+ send(method_id, *arguments)
959
+ end
960
+ else
961
+ super
962
+ end
963
+ end
964
+
965
+ def construct_attributes_from_arguments(attribute_names, arguments)
966
+ attributes = {}
967
+ attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
968
+ attributes
969
+ end
970
+
971
+ # Similar in purpose to +expand_hash_conditions_for_aggregates+.
972
+ def expand_attribute_names_for_aggregates(attribute_names)
973
+ expanded_attribute_names = []
974
+ attribute_names.each do |attribute_name|
975
+ unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
976
+ aggregate_mapping(aggregation).each do |field_attr, aggregate_attr|
977
+ expanded_attribute_names << field_attr
978
+ end
979
+ else
980
+ expanded_attribute_names << attribute_name
981
+ end
982
+ end
983
+ expanded_attribute_names
984
+ end
985
+
986
+ def all_attributes_exists?(attribute_names)
987
+ attribute_names = expand_attribute_names_for_aggregates(attribute_names)
988
+ attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
989
+ end
990
+
991
+ # Nest the type name in the same module as this class.
992
+ # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo
993
+ def type_name_with_module(type_name)
994
+ if store_full_sti_class
995
+ type_name
996
+ else
997
+ (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}"
998
+ end
999
+ end
1000
+
1001
+ # Test whether the given method and optional key are scoped.
1002
+ def scoped?(method, key = nil) #:nodoc:
1003
+ if current_scoped_methods && (scope = current_scoped_methods[method])
1004
+ !key || !scope[key].nil?
1005
+ end
1006
+ end
1007
+
1008
+ # Retrieve the scope for the given method and optional key.
1009
+ def scope(method, key = nil) #:nodoc:
1010
+ if current_scoped_methods && (scope = current_scoped_methods[method])
1011
+ key ? scope[key] : scope
1012
+ end
1013
+ end
1014
+
1015
+ def scoped_methods #:nodoc:
1016
+ Thread.current[:"#{self}_scoped_methods"] ||= default_scoping.dup
1017
+ end
1018
+
1019
+ def current_scoped_methods #:nodoc:
1020
+ scoped_methods.last
1021
+ end
1022
+
1023
+ # Returns the class type of the record using the current module as a prefix. So descendants of
1024
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
1025
+ def compute_type(type_name)
1026
+ modularized_name = type_name_with_module(type_name)
1027
+ silence_warnings do
1028
+ begin
1029
+ class_eval(modularized_name, __FILE__, __LINE__)
1030
+ rescue NameError
1031
+ class_eval(type_name, __FILE__, __LINE__)
1032
+ end
1033
+ end
1034
+ end
1035
+
1036
+ # Returns the class descending directly from ActiveGroonga::Base or an
1037
+ # abstract class, if any, in the inheritance hierarchy.
1038
+ def class_of_active_groonga_descendant(klass)
1039
+ if klass.superclass == Base || klass.superclass.abstract_class?
1040
+ klass
1041
+ elsif klass.superclass.nil?
1042
+ raise ActiveGroongaError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
1043
+ else
1044
+ class_of_active_record_descendant(klass.superclass)
1045
+ end
1046
+ end
1047
+ end
1048
+
1049
+ def initialize(attributes=nil)
1050
+ @id = nil
1051
+ @score = nil
1052
+ @attributes = attributes_from_column_definition
1053
+ @attributes_cache = {}
1054
+ @new_record = true
1055
+ ensure_proper_type
1056
+ self.attributes = attributes unless attributes.nil?
1057
+ self.class.send(:scope, :create).each { |att,value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create)
1058
+ result = yield self if block_given?
1059
+ callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
1060
+ result
1061
+ end
1062
+
1063
+ # A model instance's primary key is always available as model.id
1064
+ # whether you name it the default 'id' or set it to something else.
1065
+ def id
1066
+ @id
1067
+ end
1068
+
1069
+ def score
1070
+ @score
1071
+ end
1072
+
1073
+ # Returns a String, which Action Pack uses for constructing an URL to this
1074
+ # object. The default implementation returns this record's id as a String,
1075
+ # or nil if this record's unsaved.
1076
+ #
1077
+ # For example, suppose that you have a User model, and that you have a
1078
+ # <tt>map.resources :users</tt> route. Normally, +user_path+ will
1079
+ # construct a path with the user object's 'id' in it:
1080
+ #
1081
+ # user = User.find_by_name('Phusion')
1082
+ # user_path(user) # => "/users/1"
1083
+ #
1084
+ # You can override +to_param+ in your model to make +user_path+ construct
1085
+ # a path using the user's name instead of the user's id:
1086
+ #
1087
+ # class User < ActiveRecord::Base
1088
+ # def to_param # overridden
1089
+ # name
1090
+ # end
1091
+ # end
1092
+ #
1093
+ # user = User.find_by_name('Phusion')
1094
+ # user_path(user) # => "/users/Phusion"
1095
+ def to_param
1096
+ # We can't use alias_method here, because method 'id' optimizes itself on the fly.
1097
+ (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
1098
+ end
1099
+
1100
+ # Sets the primary ID.
1101
+ def id=(value)
1102
+ @id = value
1103
+ end
1104
+
1105
+ # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet; otherwise, returns false.
1106
+ def new_record?
1107
+ @new_record || false
1108
+ end
1109
+
1110
+ # :call-seq:
1111
+ # save(perform_validation = true)
1112
+ #
1113
+ # Saves the model.
1114
+ #
1115
+ # If the model is new a record gets created in the database, otherwise
1116
+ # the existing record gets updated.
1117
+ #
1118
+ # If +perform_validation+ is true validations run. If any of them fail
1119
+ # the action is cancelled and +save+ returns +false+. If the flag is
1120
+ # false validations are bypassed altogether. See
1121
+ # ActiveRecord::Validations for more information.
1122
+ #
1123
+ # There's a series of callbacks associated with +save+. If any of the
1124
+ # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
1125
+ # +save+ returns +false+. See ActiveRecord::Callbacks for further
1126
+ # details.
1127
+ def save
1128
+ create_or_update
1129
+ end
1130
+
1131
+ # Saves the model.
1132
+ #
1133
+ # If the model is new a record gets created in the database, otherwise
1134
+ # the existing record gets updated.
1135
+ #
1136
+ # With <tt>save!</tt> validations always run. If any of them fail
1137
+ # ActiveGroonga::RecordInvalid gets raised. See ActiveRecord::Validations
1138
+ # for more information.
1139
+ #
1140
+ # There's a series of callbacks associated with <tt>save!</tt>. If any of
1141
+ # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
1142
+ # and <tt>save!</tt> raises ActiveGroonga::RecordNotSaved. See
1143
+ # ActiveRecord::Callbacks for further details.
1144
+ def save!
1145
+ create_or_update || raise(RecordNotSaved)
1146
+ end
1147
+
1148
+ # Deletes the record in the database and freezes this instance to
1149
+ # reflect that no changes should be made (since they can't be
1150
+ # persisted). Returns the frozen instance.
1151
+ #
1152
+ # The row is simply removed with a SQL +DELETE+ statement on the
1153
+ # record's primary key, and no callbacks are executed.
1154
+ #
1155
+ # To enforce the object's +before_destroy+ and +after_destroy+
1156
+ # callbacks, Observer methods, or any <tt>:dependent</tt> association
1157
+ # options, use <tt>#destroy</tt>.
1158
+ def delete
1159
+ self.class.delete(id) unless new_record?
1160
+ freeze
1161
+ end
1162
+
1163
+ # Deletes the record in the database and freezes this instance to reflect that no changes should
1164
+ # be made (since they can't be persisted).
1165
+ def destroy
1166
+ self.class.table.delete(id) unless new_record?
1167
+ freeze
1168
+ end
1169
+
1170
+ # Updates a single attribute and saves the record without going through the normal validation procedure.
1171
+ # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
1172
+ # in Base is replaced with this when the validations module is mixed in, which it is by default.
1173
+ def update_attribute(name, value)
1174
+ send(name.to_s + '=', value)
1175
+ save(false)
1176
+ end
1177
+
1178
+ # Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
1179
+ # fail and false will be returned.
1180
+ def update_attributes(attributes)
1181
+ self.attributes = attributes
1182
+ save
1183
+ end
1184
+
1185
+ # Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.
1186
+ def update_attributes!(attributes)
1187
+ self.attributes = attributes
1188
+ save!
1189
+ end
1190
+
1191
+ # Reloads the attributes of this object from the database.
1192
+ # The optional options argument is passed to find when reloading so you
1193
+ # may do e.g. record.reload(:lock => true) to reload the same record with
1194
+ # an exclusive row lock.
1195
+ def reload(options = nil)
1196
+ clear_aggregation_cache
1197
+ clear_association_cache
1198
+ @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
1199
+ @attributes_cache = {}
1200
+ self
1201
+ end
1202
+
1203
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
1204
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
1205
+ # (Alias for the protected read_attribute method).
1206
+ def [](attr_name)
1207
+ read_attribute(attr_name)
1208
+ end
1209
+
1210
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
1211
+ # (Alias for the protected write_attribute method).
1212
+ def []=(attr_name, value)
1213
+ write_attribute(attr_name, value)
1214
+ end
1215
+
1216
+ # Allows you to set all the attributes at once by passing in a hash with keys
1217
+ # matching the attribute names (which again matches the column names).
1218
+ #
1219
+ # If +guard_protected_attributes+ is true (the default), then sensitive
1220
+ # attributes can be protected from this form of mass-assignment by using
1221
+ # the +attr_protected+ macro. Or you can alternatively specify which
1222
+ # attributes *can* be accessed with the +attr_accessible+ macro. Then all the
1223
+ # attributes not included in that won't be allowed to be mass-assigned.
1224
+ #
1225
+ # class User < ActiveGroonga::Base
1226
+ # attr_protected :is_admin
1227
+ # end
1228
+ #
1229
+ # user = User.new
1230
+ # user.attributes = { :username => 'Phusion', :is_admin => true }
1231
+ # user.username # => "Phusion"
1232
+ # user.is_admin? # => false
1233
+ #
1234
+ # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
1235
+ # user.is_admin? # => true
1236
+ def attributes=(new_attributes, guard_protected_attributes = true)
1237
+ return if new_attributes.nil?
1238
+ attributes = new_attributes.dup
1239
+ attributes.stringify_keys!
1240
+
1241
+ multi_parameter_attributes = []
1242
+ attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes
1243
+
1244
+ attributes.each do |k, v|
1245
+ if k.include?("(")
1246
+ multi_parameter_attributes << [ k, v ]
1247
+ else
1248
+ respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
1249
+ end
1250
+ end
1251
+
1252
+ assign_multiparameter_attributes(multi_parameter_attributes)
1253
+ end
1254
+
1255
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
1256
+ def attributes
1257
+ self.attribute_names.inject({}) do |attrs, name|
1258
+ attrs[name] = read_attribute(name)
1259
+ attrs
1260
+ end
1261
+ end
1262
+
1263
+ # Returns a hash of attributes before typecasting and deserialization.
1264
+ def attributes_before_type_cast
1265
+ self.attribute_names.inject({}) do |attrs, name|
1266
+ attrs[name] = read_attribute_before_type_cast(name)
1267
+ attrs
1268
+ end
1269
+ end
1270
+
1271
+ # Returns an <tt>#inspect</tt>-like string for the value of the
1272
+ # attribute +attr_name+. String attributes are elided after 50
1273
+ # characters, and Date and Time attributes are returned in the
1274
+ # <tt>:db</tt> format. Other attributes return the value of
1275
+ # <tt>#inspect</tt> without modification.
1276
+ #
1277
+ # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
1278
+ #
1279
+ # person.attribute_for_inspect(:name)
1280
+ # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
1281
+ #
1282
+ # person.attribute_for_inspect(:created_at)
1283
+ # # => '"2009-01-12 04:48:57"'
1284
+ def attribute_for_inspect(attr_name)
1285
+ value = read_attribute(attr_name)
1286
+
1287
+ if value.is_a?(String) && value.length > 50
1288
+ "#{value[0..50]}...".inspect
1289
+ elsif value.is_a?(Date) || value.is_a?(Time)
1290
+ %("#{value.to_s(:db)}")
1291
+ else
1292
+ value.inspect
1293
+ end
1294
+ end
1295
+
1296
+ # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
1297
+ # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
1298
+ def attribute_present?(attribute)
1299
+ value = read_attribute(attribute)
1300
+ !value.blank?
1301
+ end
1302
+
1303
+ # Returns true if the given attribute is in the attributes hash
1304
+ def has_attribute?(attr_name)
1305
+ @attributes.has_key?(attr_name.to_s)
1306
+ end
1307
+
1308
+ # Returns an array of names for the attributes available on this object sorted alphabetically.
1309
+ def attribute_names
1310
+ @attributes.keys.sort
1311
+ end
1312
+
1313
+ # Returns the column object for the named attribute.
1314
+ def column_for_attribute(name)
1315
+ self.class.columns_hash[name.to_s]
1316
+ end
1317
+
1318
+ # Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
1319
+ def ==(comparison_object)
1320
+ comparison_object.equal?(self) ||
1321
+ (comparison_object.instance_of?(self.class) &&
1322
+ comparison_object.id == id &&
1323
+ !comparison_object.new_record?)
1324
+ end
1325
+
1326
+ # Delegates to ==
1327
+ def eql?(comparison_object)
1328
+ self == (comparison_object)
1329
+ end
1330
+
1331
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
1332
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
1333
+ def hash
1334
+ id.hash
1335
+ end
1336
+
1337
+ # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
1338
+ def freeze
1339
+ @attributes.freeze; self
1340
+ end
1341
+
1342
+ # Returns +true+ if the attributes hash has been frozen.
1343
+ def frozen?
1344
+ @attributes.frozen?
1345
+ end
1346
+
1347
+ # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
1348
+ # attributes will be marked as read only since they cannot be saved.
1349
+ def readonly?
1350
+ defined?(@readonly) && @readonly == true
1351
+ end
1352
+
1353
+ # Marks this record as read only.
1354
+ def readonly!
1355
+ @readonly = true
1356
+ end
1357
+
1358
+ # Returns the contents of the record as a nicely formatted string.
1359
+ def inspect
1360
+ attributes_as_nice_string = self.class.column_names.collect { |name|
1361
+ if has_attribute?(name) || new_record?
1362
+ "#{name}: #{attribute_for_inspect(name)}"
1363
+ end
1364
+ }.compact.join(", ")
1365
+ "#<#{self.class} #{attributes_as_nice_string}>"
1366
+ end
1367
+
1368
+ private
1369
+ def create_or_update
1370
+ raise ReadOnlyRecord if readonly?
1371
+ result = new_record? ? create : update
1372
+ result != false
1373
+ end
1374
+
1375
+ # Updates the associated record with values matching those of the instance attributes.
1376
+ # Returns the number of affected rows.
1377
+ def update(attribute_names=@attributes.keys)
1378
+ attribute_names = remove_readonly_attributes(attribute_names)
1379
+ table = self.class.table
1380
+ indexes = Schema.indexes(table)
1381
+ quoted_attributes = attributes_with_quotes(false, attribute_names)
1382
+ quoted_attributes.each do |name, value|
1383
+ column = table.column(name)
1384
+ next if column.nil?
1385
+ column[id] = value
1386
+ end
1387
+ end
1388
+
1389
+ # Creates a record with values matching those of the instance attributes
1390
+ # and returns its id.
1391
+ def create
1392
+ table = self.class.table
1393
+ record = table.add
1394
+ indexes = Schema.indexes(table)
1395
+ quoted_attributes = attributes_with_quotes
1396
+ quoted_attributes.each do |name, value|
1397
+ record[name] = value
1398
+ end
1399
+ self.id = record.id
1400
+ @new_record = false
1401
+ id
1402
+ end
1403
+
1404
+ # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendant.
1405
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to do Reply.new without having to
1406
+ # set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself. No such attribute would be set for objects of the
1407
+ # Message class in that example.
1408
+ def ensure_proper_type
1409
+ unless self.class.descends_from_active_groonga?
1410
+ write_attribute(self.class.inheritance_column, self.class.sti_name)
1411
+ end
1412
+ end
1413
+
1414
+ def convert_number_column_value(value)
1415
+ if value == false
1416
+ 0
1417
+ elsif value == true
1418
+ 1
1419
+ elsif value.is_a?(String) && value.blank?
1420
+ nil
1421
+ else
1422
+ value
1423
+ end
1424
+ end
1425
+
1426
+ def remove_attributes_protected_from_mass_assignment(attributes)
1427
+ safe_attributes =
1428
+ if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
1429
+ attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
1430
+ elsif self.class.protected_attributes.nil?
1431
+ attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
1432
+ elsif self.class.accessible_attributes.nil?
1433
+ attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
1434
+ else
1435
+ raise "Declare either attr_protected or attr_accessible for #{self.class}, but not both."
1436
+ end
1437
+
1438
+ removed_attributes = attributes.keys - safe_attributes.keys
1439
+
1440
+ if removed_attributes.any?
1441
+ log_protected_attribute_removal(removed_attributes)
1442
+ end
1443
+
1444
+ safe_attributes
1445
+ end
1446
+
1447
+ # Removes attributes which have been marked as readonly.
1448
+ def remove_readonly_attributes(attributes)
1449
+ unless self.class.readonly_attributes.nil?
1450
+ attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,"")) }
1451
+ else
1452
+ attributes
1453
+ end
1454
+ end
1455
+
1456
+ def log_protected_attribute_removal(*attributes)
1457
+ logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}"
1458
+ end
1459
+
1460
+ # The primary key and inheritance column can never be set by mass-assignment for security reasons.
1461
+ def attributes_protected_by_default
1462
+ default = [ self.class.primary_key, self.class.inheritance_column ]
1463
+ default << 'id' unless self.class.primary_key.eql? 'id'
1464
+ default
1465
+ end
1466
+
1467
+ # Initializes the attributes array with keys matching the columns from the linked table and
1468
+ # the values matching the corresponding default value of that column, so
1469
+ # that a new instance, or one populated from a passed-in Hash, still has all the attributes
1470
+ # that instances loaded from the database would.
1471
+ def attributes_from_column_definition
1472
+ self.class.columns.inject({}) do |attributes, column|
1473
+ attributes[column.name] = column.default
1474
+ attributes
1475
+ end
1476
+ end
1477
+
1478
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
1479
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
1480
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
1481
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
1482
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
1483
+ # s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.
1484
+ def assign_multiparameter_attributes(pairs)
1485
+ execute_callstack_for_multiparameter_attributes(
1486
+ extract_callstack_for_multiparameter_attributes(pairs)
1487
+ )
1488
+ end
1489
+
1490
+ def execute_callstack_for_multiparameter_attributes(callstack)
1491
+ errors = []
1492
+ callstack.each do |name, values|
1493
+ klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
1494
+ if values.empty?
1495
+ send(name + "=", nil)
1496
+ else
1497
+ begin
1498
+ value = if Time == klass
1499
+ instantiate_time_object(name, values)
1500
+ elsif Date == klass
1501
+ begin
1502
+ Date.new(*values)
1503
+ rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
1504
+ instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
1505
+ end
1506
+ else
1507
+ klass.new(*values)
1508
+ end
1509
+
1510
+ send(name + "=", value)
1511
+ rescue => ex
1512
+ errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
1513
+ end
1514
+ end
1515
+ end
1516
+ unless errors.empty?
1517
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
1518
+ end
1519
+ end
1520
+
1521
+ def extract_callstack_for_multiparameter_attributes(pairs)
1522
+ attributes = { }
1523
+
1524
+ for pair in pairs
1525
+ multiparameter_name, value = pair
1526
+ attribute_name = multiparameter_name.split("(").first
1527
+ attributes[attribute_name] = [] unless attributes.include?(attribute_name)
1528
+
1529
+ unless value.empty?
1530
+ attributes[attribute_name] <<
1531
+ [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
1532
+ end
1533
+ end
1534
+
1535
+ attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
1536
+ end
1537
+
1538
+ # Returns a copy of the attributes hash where all the values have been safely quoted for use in
1539
+ # an SQL statement.
1540
+ def attributes_with_quotes(include_readonly_attributes=true, attribute_names=@attributes.keys)
1541
+ quoted = {}
1542
+ attribute_names.each do |name|
1543
+ column = column_for_attribute(name)
1544
+ next if column.nil?
1545
+
1546
+ value = read_attribute(name)
1547
+ # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
1548
+ if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
1549
+ value = value.to_yaml
1550
+ end
1551
+ quoted[name] = column.quote(value)
1552
+ end
1553
+ include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
1554
+ end
1555
+
1556
+ # Quote strings appropriately for SQL statements.
1557
+ def quote_value(value, column=nil)
1558
+ if column
1559
+ column.quote(value)
1560
+ else
1561
+ value
1562
+ end
1563
+ end
1564
+
1565
+ def clone_attribute_value(reader_method, attribute_name)
1566
+ value = send(reader_method, attribute_name)
1567
+ value.duplicable? ? value.clone : value
1568
+ rescue TypeError, NoMethodError
1569
+ value
1570
+ end
1571
+
1572
+ include Validations
1573
+ include AttributeMethods
1574
+ include Dirty
1575
+ include Timestamp
1576
+ include Associations
1577
+ include Aggregations, Reflection
1578
+ end
1579
+ end