activerecord 3.1.10 → 4.2.11
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +6 -6
- data/CHANGELOG.md +1837 -338
- data/MIT-LICENSE +1 -1
- data/README.rdoc +39 -43
- data/examples/performance.rb +51 -20
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +57 -43
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -39
- data/lib/active_record/associations/association.rb +71 -85
- data/lib/active_record/associations/association_scope.rb +138 -89
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
- data/lib/active_record/associations/builder/association.rb +125 -29
- data/lib/active_record/associations/builder/belongs_to.rb +91 -60
- data/lib/active_record/associations/builder/collection_association.rb +69 -49
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +12 -52
- data/lib/active_record/associations/builder/singular_association.rb +22 -29
- data/lib/active_record/associations/collection_association.rb +294 -187
- data/lib/active_record/associations/collection_proxy.rb +961 -94
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +118 -23
- data/lib/active_record/associations/has_many_through_association.rb +115 -45
- data/lib/active_record/associations/has_one_association.rb +57 -24
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +230 -156
- data/lib/active_record/associations/preloader/association.rb +96 -55
- data/lib/active_record/associations/preloader/collection_association.rb +3 -3
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +61 -32
- data/lib/active_record/associations/preloader.rb +113 -87
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +37 -19
- data/lib/active_record/associations.rb +505 -371
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +141 -51
- data/lib/active_record/attribute_methods/primary_key.rb +87 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +74 -117
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
- data/lib/active_record/attribute_methods/write.rb +60 -21
- data/lib/active_record/attribute_methods.rb +409 -48
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +279 -232
- data/lib/active_record/base.rb +84 -1969
- data/lib/active_record/callbacks.rb +66 -28
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +422 -243
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +273 -170
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
- data/lib/active_record/connection_adapters/column.rb +33 -221
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
- data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +445 -902
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +159 -102
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +102 -34
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +318 -260
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +80 -52
- data/lib/active_record/locking/pessimistic.rb +27 -5
- data/lib/active_record/log_subscriber.rb +25 -18
- data/lib/active_record/migration/command_recorder.rb +130 -38
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +532 -201
- data/lib/active_record/model_schema.rb +342 -0
- data/lib/active_record/nested_attributes.rb +229 -139
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +304 -99
- data/lib/active_record/query_cache.rb +25 -43
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +86 -45
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +7 -4
- data/lib/active_record/railties/databases.rake +198 -377
- data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +516 -165
- data/lib/active_record/relation/batches.rb +96 -45
- data/lib/active_record/relation/calculations.rb +221 -144
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +362 -243
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +135 -41
- data/lib/active_record/relation/query_methods.rb +982 -155
- data/lib/active_record/relation/spawn_methods.rb +50 -110
- data/lib/active_record/relation.rb +371 -180
- data/lib/active_record/result.rb +109 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +111 -61
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +135 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/serialization.rb +7 -45
- data/lib/active_record/serializers/xml_serializer.rb +14 -65
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- data/lib/active_record/tasks/database_tasks.rb +299 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +35 -14
- data/lib/active_record/transactions.rb +141 -74
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +27 -18
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +125 -66
- data/lib/active_record/validations.rb +37 -30
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +80 -25
- data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +132 -53
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
- data/lib/active_record/dynamic_finder_match.rb +0 -56
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/identity_map.rb +0 -163
- data/lib/active_record/named_scope.rb +0 -200
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -358
- data/lib/active_record/test_case.rb +0 -69
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Active Record -- Object-relational mapping
|
1
|
+
= Active Record -- Object-relational mapping in Rails
|
2
2
|
|
3
3
|
Active Record connects classes to relational database tables to establish an
|
4
4
|
almost zero-configuration persistence layer for applications. The library
|
@@ -20,8 +20,10 @@ A short rundown of some of the major features:
|
|
20
20
|
class Product < ActiveRecord::Base
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
{Learn more}[link:classes/ActiveRecord/Base.html]
|
24
|
+
|
25
|
+
The Product class is automatically mapped to the table named "products",
|
26
|
+
which might look like this:
|
25
27
|
|
26
28
|
CREATE TABLE products (
|
27
29
|
id int(11) NOT NULL auto_increment,
|
@@ -29,10 +31,8 @@ A short rundown of some of the major features:
|
|
29
31
|
PRIMARY KEY (id)
|
30
32
|
);
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
{Learn more}[link:classes/ActiveRecord/Base.html]
|
34
|
+
This would also define the following accessors: `Product#name` and
|
35
|
+
`Product#name=(new_name)`.
|
36
36
|
|
37
37
|
|
38
38
|
* Associations between objects defined by simple class methods.
|
@@ -49,10 +49,10 @@ A short rundown of some of the major features:
|
|
49
49
|
* Aggregations of value objects.
|
50
50
|
|
51
51
|
class Account < ActiveRecord::Base
|
52
|
-
composed_of :balance, :
|
53
|
-
:
|
52
|
+
composed_of :balance, class_name: 'Money',
|
53
|
+
mapping: %w(balance amount)
|
54
54
|
composed_of :address,
|
55
|
-
:
|
55
|
+
mapping: [%w(address_street street), %w(address_city city)]
|
56
56
|
end
|
57
57
|
|
58
58
|
{Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
|
@@ -61,10 +61,10 @@ A short rundown of some of the major features:
|
|
61
61
|
* Validation rules that can differ for new or existing objects.
|
62
62
|
|
63
63
|
class Account < ActiveRecord::Base
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
validates :subdomain, :name, :email_address, :password, presence: true
|
65
|
+
validates :subdomain, uniqueness: true
|
66
|
+
validates :terms_of_service, acceptance: true, on: :create
|
67
|
+
validates :password, :email_address, confirmation: true, on: :create
|
68
68
|
end
|
69
69
|
|
70
70
|
{Learn more}[link:classes/ActiveRecord/Validations.html]
|
@@ -80,17 +80,6 @@ A short rundown of some of the major features:
|
|
80
80
|
{Learn more}[link:classes/ActiveRecord/Callbacks.html]
|
81
81
|
|
82
82
|
|
83
|
-
* Observers that react to changes in a model.
|
84
|
-
|
85
|
-
class CommentObserver < ActiveRecord::Observer
|
86
|
-
def after_create(comment) # is called just after Comment#save
|
87
|
-
CommentMailer.new_comment_email("david@loudthinking.com", comment).deliver
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
{Learn more}[link:classes/ActiveRecord/Observer.html]
|
92
|
-
|
93
|
-
|
94
83
|
* Inheritance hierarchies.
|
95
84
|
|
96
85
|
class Company < ActiveRecord::Base; end
|
@@ -124,15 +113,15 @@ A short rundown of some of the major features:
|
|
124
113
|
* Database abstraction through simple adapters.
|
125
114
|
|
126
115
|
# connect to SQLite3
|
127
|
-
ActiveRecord::Base.establish_connection(:
|
116
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'dbfile.sqlite3')
|
128
117
|
|
129
118
|
# connect to MySQL with authentication
|
130
119
|
ActiveRecord::Base.establish_connection(
|
131
|
-
:
|
132
|
-
:
|
133
|
-
:
|
134
|
-
:
|
135
|
-
:
|
120
|
+
adapter: 'mysql2',
|
121
|
+
host: 'localhost',
|
122
|
+
username: 'me',
|
123
|
+
password: 'secret',
|
124
|
+
database: 'activerecord'
|
136
125
|
)
|
137
126
|
|
138
127
|
{Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
|
@@ -141,10 +130,10 @@ A short rundown of some of the major features:
|
|
141
130
|
SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
|
142
131
|
|
143
132
|
|
144
|
-
* Logging support for Log4r[
|
133
|
+
* Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
|
145
134
|
|
146
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
147
|
-
ActiveRecord::Base.logger = Log4r::Logger.new(
|
135
|
+
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
136
|
+
ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
|
148
137
|
|
149
138
|
|
150
139
|
* Database agnostic schema management with Migrations.
|
@@ -159,7 +148,7 @@ A short rundown of some of the major features:
|
|
159
148
|
t.integer :position
|
160
149
|
end
|
161
150
|
|
162
|
-
SystemSetting.create :
|
151
|
+
SystemSetting.create name: 'notice', label: 'Use notice?', value: 1
|
163
152
|
end
|
164
153
|
|
165
154
|
def down
|
@@ -186,7 +175,7 @@ by relying on a number of conventions that make it easy for Active Record to inf
|
|
186
175
|
complex relations and structures from a minimal amount of explicit direction.
|
187
176
|
|
188
177
|
Convention over Configuration:
|
189
|
-
* No XML
|
178
|
+
* No XML files!
|
190
179
|
* Lots of reflection and run-time extension
|
191
180
|
* Magic is not inherently a bad word
|
192
181
|
|
@@ -197,26 +186,33 @@ Admit the Database:
|
|
197
186
|
|
198
187
|
== Download and installation
|
199
188
|
|
200
|
-
The latest version of Active Record can be installed with
|
189
|
+
The latest version of Active Record can be installed with RubyGems:
|
201
190
|
|
202
191
|
% [sudo] gem install activerecord
|
203
192
|
|
204
|
-
Source code can be downloaded as part of the Rails project on GitHub
|
193
|
+
Source code can be downloaded as part of the Rails project on GitHub:
|
205
194
|
|
206
|
-
* https://github.com/rails/rails/tree/
|
195
|
+
* https://github.com/rails/rails/tree/4-2-stable/activerecord
|
207
196
|
|
208
197
|
|
209
198
|
== License
|
210
199
|
|
211
|
-
Active Record is released under the MIT license
|
200
|
+
Active Record is released under the MIT license:
|
201
|
+
|
202
|
+
* http://www.opensource.org/licenses/MIT
|
212
203
|
|
213
204
|
|
214
205
|
== Support
|
215
206
|
|
216
|
-
API documentation is at
|
207
|
+
API documentation is at:
|
217
208
|
|
218
|
-
* http://api.rubyonrails.
|
209
|
+
* http://api.rubyonrails.org
|
219
210
|
|
220
|
-
Bug reports
|
211
|
+
Bug reports can be filed for the Ruby on Rails project here:
|
221
212
|
|
222
213
|
* https://github.com/rails/rails/issues
|
214
|
+
|
215
|
+
Feature requests should be discussed on the rails-core mailing list here:
|
216
|
+
|
217
|
+
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
218
|
+
|
data/examples/performance.rb
CHANGED
@@ -5,12 +5,12 @@ require 'benchmark/ips'
|
|
5
5
|
TIME = (ENV['BENCHMARK_TIME'] || 20).to_i
|
6
6
|
RECORDS = (ENV['BENCHMARK_RECORDS'] || TIME*1000).to_i
|
7
7
|
|
8
|
-
conn = { :
|
8
|
+
conn = { adapter: 'sqlite3', database: ':memory:' }
|
9
9
|
|
10
10
|
ActiveRecord::Base.establish_connection(conn)
|
11
11
|
|
12
12
|
class User < ActiveRecord::Base
|
13
|
-
connection.create_table :users, :
|
13
|
+
connection.create_table :users, force: true do |t|
|
14
14
|
t.string :name, :email
|
15
15
|
t.timestamps
|
16
16
|
end
|
@@ -19,7 +19,7 @@ class User < ActiveRecord::Base
|
|
19
19
|
end
|
20
20
|
|
21
21
|
class Exhibit < ActiveRecord::Base
|
22
|
-
connection.create_table :exhibits, :
|
22
|
+
connection.create_table :exhibits, force: true do |t|
|
23
23
|
t.belongs_to :user
|
24
24
|
t.string :name
|
25
25
|
t.text :notes
|
@@ -31,15 +31,32 @@ class Exhibit < ActiveRecord::Base
|
|
31
31
|
def look; attributes end
|
32
32
|
def feel; look; user.name end
|
33
33
|
|
34
|
+
def self.with_name
|
35
|
+
where("name IS NOT NULL")
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.with_notes
|
39
|
+
where("notes IS NOT NULL")
|
40
|
+
end
|
41
|
+
|
34
42
|
def self.look(exhibits) exhibits.each { |e| e.look } end
|
35
43
|
def self.feel(exhibits) exhibits.each { |e| e.feel } end
|
36
44
|
end
|
37
45
|
|
46
|
+
def progress_bar(int); print "." if (int%100).zero? ; end
|
47
|
+
|
38
48
|
puts 'Generating data...'
|
39
49
|
|
40
50
|
module ActiveRecord
|
41
51
|
class Faker
|
42
|
-
LOREM =
|
52
|
+
LOREM = %Q{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit.
|
53
|
+
Integer consequat tincidunt felis. Etiam non erat dolor. Vivamus imperdiet nibh sit amet diam eleifend id posuere diam malesuada. Mauris at accumsan sem.
|
54
|
+
Donec id lorem neque. Fusce erat lorem, ornare eu congue vitae, malesuada quis neque. Maecenas vel urna a velit pretium fermentum. Donec tortor enim,
|
55
|
+
tempor venenatis egestas a, tempor sed ipsum. Ut arcu justo, faucibus non imperdiet ac, interdum at diam. Pellentesque ipsum enim, venenatis ut iaculis vitae,
|
56
|
+
varius vitae sem. Sed rutrum quam ac elit euismod bibendum. Donec ultricies ultricies magna, at lacinia libero mollis aliquam. Sed ac arcu in tortor elementum
|
57
|
+
tincidunt vel interdum sem. Curabitur eget erat arcu. Praesent eget eros leo. Nam magna enim, sollicitudin vehicula scelerisque in, vulputate ut libero.
|
58
|
+
Praesent varius tincidunt commodo}.split
|
59
|
+
|
43
60
|
def self.name
|
44
61
|
LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join ' '
|
45
62
|
end
|
@@ -60,30 +77,32 @@ notes = ActiveRecord::Faker::LOREM.join ' '
|
|
60
77
|
today = Date.today
|
61
78
|
|
62
79
|
puts "Inserting #{RECORDS} users and exhibits..."
|
63
|
-
RECORDS.times do
|
80
|
+
RECORDS.times do |record|
|
64
81
|
user = User.create(
|
65
|
-
:
|
66
|
-
:
|
67
|
-
:
|
82
|
+
created_at: today,
|
83
|
+
name: ActiveRecord::Faker.name,
|
84
|
+
email: ActiveRecord::Faker.email
|
68
85
|
)
|
69
86
|
|
70
87
|
Exhibit.create(
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
88
|
+
created_at: today,
|
89
|
+
name: ActiveRecord::Faker.name,
|
90
|
+
user: user,
|
91
|
+
notes: notes
|
75
92
|
)
|
93
|
+
progress_bar(record)
|
76
94
|
end
|
95
|
+
puts "Done!\n"
|
77
96
|
|
78
97
|
Benchmark.ips(TIME) do |x|
|
79
98
|
ar_obj = Exhibit.find(1)
|
80
|
-
attrs = { :
|
81
|
-
attrs_first = { :
|
82
|
-
attrs_second = { :
|
99
|
+
attrs = { name: 'sam' }
|
100
|
+
attrs_first = { name: 'sam' }
|
101
|
+
attrs_second = { name: 'tom' }
|
83
102
|
exhibit = {
|
84
|
-
:
|
85
|
-
:
|
86
|
-
:
|
103
|
+
name: ActiveRecord::Faker.name,
|
104
|
+
notes: notes,
|
105
|
+
created_at: Date.today
|
87
106
|
}
|
88
107
|
|
89
108
|
x.report("Model#id") do
|
@@ -102,10 +121,18 @@ Benchmark.ips(TIME) do |x|
|
|
102
121
|
Exhibit.first.look
|
103
122
|
end
|
104
123
|
|
124
|
+
x.report 'Model.take' do
|
125
|
+
Exhibit.take
|
126
|
+
end
|
127
|
+
|
105
128
|
x.report("Model.all limit(100)") do
|
106
129
|
Exhibit.look Exhibit.limit(100)
|
107
130
|
end
|
108
131
|
|
132
|
+
x.report("Model.all take(100)") do
|
133
|
+
Exhibit.look Exhibit.take(100)
|
134
|
+
end
|
135
|
+
|
109
136
|
x.report "Model.all limit(100) with relationship" do
|
110
137
|
Exhibit.feel Exhibit.limit(100).includes(:user)
|
111
138
|
end
|
@@ -114,6 +141,10 @@ Benchmark.ips(TIME) do |x|
|
|
114
141
|
Exhibit.look Exhibit.limit(10000)
|
115
142
|
end
|
116
143
|
|
144
|
+
x.report 'Model.named_scope' do
|
145
|
+
Exhibit.limit(10).with_name.with_notes
|
146
|
+
end
|
147
|
+
|
117
148
|
x.report 'Model.create' do
|
118
149
|
Exhibit.create(exhibit)
|
119
150
|
end
|
@@ -124,7 +155,7 @@ Benchmark.ips(TIME) do |x|
|
|
124
155
|
end
|
125
156
|
|
126
157
|
x.report 'Resource#update' do
|
127
|
-
Exhibit.first.
|
158
|
+
Exhibit.first.update(name: 'bob')
|
128
159
|
end
|
129
160
|
|
130
161
|
x.report 'Resource#destroy' do
|
@@ -148,6 +179,6 @@ Benchmark.ips(TIME) do |x|
|
|
148
179
|
end
|
149
180
|
|
150
181
|
x.report "AR.execute(query)" do
|
151
|
-
ActiveRecord::Base.connection.execute("
|
182
|
+
ActiveRecord::Base.connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
|
152
183
|
end
|
153
184
|
end
|
data/examples/simple.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
|
1
|
+
require File.expand_path('../../../load_paths', __FILE__)
|
2
2
|
require 'active_record'
|
3
3
|
|
4
4
|
class Person < ActiveRecord::Base
|
5
|
-
establish_connection :
|
6
|
-
connection.create_table table_name, :
|
5
|
+
establish_connection adapter: 'sqlite3', database: 'foobar.db'
|
6
|
+
connection.create_table table_name, force: true do |t|
|
7
7
|
t.string :name
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
bob = Person.create!(:
|
11
|
+
bob = Person.create!(name: 'bob')
|
12
12
|
puts Person.all.inspect
|
13
13
|
bob.destroy
|
14
14
|
puts Person.all.inspect
|
@@ -10,14 +10,14 @@ module ActiveRecord
|
|
10
10
|
# Active Record implements aggregation through a macro-like class method called +composed_of+
|
11
11
|
# for representing attributes as value objects. It expresses relationships like "Account [is]
|
12
12
|
# composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
|
13
|
-
# to the macro adds a description of how the value objects
|
14
|
-
# the entity object (when the entity is initialized either
|
15
|
-
# existing object) and how it can be turned back into attributes
|
13
|
+
# to the macro adds a description of how the value objects are created from the attributes of
|
14
|
+
# the entity object (when the entity is initialized either as a new object or from finding an
|
15
|
+
# existing object) and how it can be turned back into attributes (when the entity is saved to
|
16
16
|
# the database).
|
17
17
|
#
|
18
18
|
# class Customer < ActiveRecord::Base
|
19
|
-
# composed_of :balance, :
|
20
|
-
# composed_of :address, :
|
19
|
+
# composed_of :balance, class_name: "Money", mapping: %w(balance amount)
|
20
|
+
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
|
21
21
|
# end
|
22
22
|
#
|
23
23
|
# The customer class now has the following methods to manipulate the value objects:
|
@@ -71,7 +71,7 @@ module ActiveRecord
|
|
71
71
|
# Now it's possible to access attributes from the database through the value objects instead. If
|
72
72
|
# you choose to name the composition the same as the attribute's name, it will be the only way to
|
73
73
|
# access that attribute. That's the case with our +balance+ attribute. You interact with the value
|
74
|
-
# objects just like you would any other attribute
|
74
|
+
# objects just like you would with any other attribute:
|
75
75
|
#
|
76
76
|
# customer.balance = Money.new(20) # sets the Money value object and the attribute
|
77
77
|
# customer.balance # => Money value object
|
@@ -86,6 +86,12 @@ module ActiveRecord
|
|
86
86
|
# customer.address_street = "Hyancintvej"
|
87
87
|
# customer.address_city = "Copenhagen"
|
88
88
|
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
|
89
|
+
#
|
90
|
+
# customer.address_street = "Vesterbrogade"
|
91
|
+
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
|
92
|
+
# customer.clear_aggregation_cache
|
93
|
+
# customer.address # => Address.new("Vesterbrogade", "Copenhagen")
|
94
|
+
#
|
89
95
|
# customer.address = Address.new("May Street", "Chicago")
|
90
96
|
# customer.address_street # => "May Street"
|
91
97
|
# customer.address_city # => "Chicago"
|
@@ -101,13 +107,13 @@ module ActiveRecord
|
|
101
107
|
# ActiveRecord::Base classes are entity objects.
|
102
108
|
#
|
103
109
|
# It's also important to treat the value objects as immutable. Don't allow the Money object to have
|
104
|
-
# its amount changed after creation. Create a new Money object with the new value instead.
|
105
|
-
# is
|
110
|
+
# its amount changed after creation. Create a new Money object with the new value instead. The
|
111
|
+
# Money#exchange_to method is an example of this. It returns a new value object instead of changing
|
106
112
|
# its own values. Active Record won't persist value objects that have been changed through means
|
107
113
|
# other than the writer method.
|
108
114
|
#
|
109
115
|
# The immutable requirement is enforced by Active Record by freezing any object assigned as a value
|
110
|
-
# object. Attempting to change it afterwards will result in a
|
116
|
+
# object. Attempting to change it afterwards will result in a RuntimeError.
|
111
117
|
#
|
112
118
|
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
|
113
119
|
# keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
|
@@ -119,28 +125,28 @@ module ActiveRecord
|
|
119
125
|
# option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
|
120
126
|
# a custom constructor to be specified.
|
121
127
|
#
|
122
|
-
# When a new value is assigned to the value object the default assumption is that the new value
|
128
|
+
# When a new value is assigned to the value object, the default assumption is that the new value
|
123
129
|
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
|
124
130
|
# converted to an instance of value class if necessary.
|
125
131
|
#
|
126
|
-
# For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
|
127
|
-
#
|
128
|
-
# for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
129
|
-
# values can be assigned to the value object using either another NetAddr::CIDR object, a string
|
132
|
+
# For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be
|
133
|
+
# aggregated using the NetAddr::CIDR value class (http://www.ruby-doc.org/gems/docs/n/netaddr-1.5.0/NetAddr/CIDR.html).
|
134
|
+
# The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
135
|
+
# New values can be assigned to the value object using either another NetAddr::CIDR object, a string
|
130
136
|
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
|
131
137
|
# these requirements:
|
132
138
|
#
|
133
139
|
# class NetworkResource < ActiveRecord::Base
|
134
140
|
# composed_of :cidr,
|
135
|
-
# :
|
136
|
-
# :
|
137
|
-
# :
|
138
|
-
# :
|
139
|
-
# :
|
141
|
+
# class_name: 'NetAddr::CIDR',
|
142
|
+
# mapping: [ %w(network_address network), %w(cidr_range bits) ],
|
143
|
+
# allow_nil: true,
|
144
|
+
# constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
|
145
|
+
# converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
|
140
146
|
# end
|
141
147
|
#
|
142
148
|
# # This calls the :constructor
|
143
|
-
# network_resource = NetworkResource.new(:
|
149
|
+
# network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
|
144
150
|
#
|
145
151
|
# # These assignments will both use the :converter
|
146
152
|
# network_resource.cidr = [ '192.168.2.1', 8 ]
|
@@ -159,7 +165,7 @@ module ActiveRecord
|
|
159
165
|
# by specifying an instance of the value object in the conditions hash. The following example
|
160
166
|
# finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
|
161
167
|
#
|
162
|
-
# Customer.where(:
|
168
|
+
# Customer.where(balance: Money.new(20, "USD"))
|
163
169
|
#
|
164
170
|
module ClassMethods
|
165
171
|
# Adds reader and writer methods for manipulating a value object:
|
@@ -176,7 +182,7 @@ module ActiveRecord
|
|
176
182
|
# order in which mappings are defined determines the order in which attributes are sent to the
|
177
183
|
# value class constructor.
|
178
184
|
# * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
|
179
|
-
# attributes are +nil+.
|
185
|
+
# attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
|
180
186
|
# mapped attributes.
|
181
187
|
# This defaults to +false+.
|
182
188
|
# * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
|
@@ -187,20 +193,21 @@ module ActiveRecord
|
|
187
193
|
# * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
|
188
194
|
# or a Proc that is called when a new value is assigned to the value object. The converter is
|
189
195
|
# passed the single value that is used in the assignment and is only called if the new value is
|
190
|
-
# not an instance of <tt>:class_name</tt>.
|
196
|
+
# not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
|
197
|
+
# can return nil to skip the assignment.
|
191
198
|
#
|
192
199
|
# Option examples:
|
193
|
-
# composed_of :temperature, :
|
194
|
-
# composed_of :balance, :
|
195
|
-
# :
|
196
|
-
# composed_of :address, :
|
200
|
+
# composed_of :temperature, mapping: %w(reading celsius)
|
201
|
+
# composed_of :balance, class_name: "Money", mapping: %w(balance amount),
|
202
|
+
# converter: Proc.new { |balance| balance.to_money }
|
203
|
+
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
|
197
204
|
# composed_of :gps_location
|
198
|
-
# composed_of :gps_location, :
|
205
|
+
# composed_of :gps_location, allow_nil: true
|
199
206
|
# composed_of :ip_address,
|
200
|
-
# :
|
201
|
-
# :
|
202
|
-
# :
|
203
|
-
# :
|
207
|
+
# class_name: 'IPAddr',
|
208
|
+
# mapping: %w(ip to_i),
|
209
|
+
# constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
|
210
|
+
# converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
|
204
211
|
#
|
205
212
|
def composed_of(part_id, options = {})
|
206
213
|
options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
|
@@ -216,14 +223,15 @@ module ActiveRecord
|
|
216
223
|
reader_method(name, class_name, mapping, allow_nil, constructor)
|
217
224
|
writer_method(name, class_name, mapping, allow_nil, converter)
|
218
225
|
|
219
|
-
|
226
|
+
reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
|
227
|
+
Reflection.add_aggregate_reflection self, part_id, reflection
|
220
228
|
end
|
221
229
|
|
222
230
|
private
|
223
231
|
def reader_method(name, class_name, mapping, allow_nil, constructor)
|
224
232
|
define_method(name) do
|
225
|
-
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|
|
226
|
-
attrs = mapping.collect {|
|
233
|
+
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !_read_attribute(key).nil? })
|
234
|
+
attrs = mapping.collect {|key, _| _read_attribute(key)}
|
227
235
|
object = constructor.respond_to?(:call) ?
|
228
236
|
constructor.call(*attrs) :
|
229
237
|
class_name.constantize.send(constructor, *attrs)
|
@@ -235,17 +243,23 @@ module ActiveRecord
|
|
235
243
|
|
236
244
|
def writer_method(name, class_name, mapping, allow_nil, converter)
|
237
245
|
define_method("#{name}=") do |part|
|
246
|
+
klass = class_name.constantize
|
247
|
+
|
248
|
+
unless part.is_a?(klass) || converter.nil? || part.nil?
|
249
|
+
part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
|
250
|
+
end
|
251
|
+
|
252
|
+
hash_from_multiparameter_assignment = part.is_a?(Hash) &&
|
253
|
+
part.each_key.all? { |k| k.is_a?(Integer) }
|
254
|
+
if hash_from_multiparameter_assignment
|
255
|
+
part = klass.new(*part.values)
|
256
|
+
end
|
257
|
+
|
238
258
|
if part.nil? && allow_nil
|
239
|
-
mapping.each { |
|
259
|
+
mapping.each { |key, _| self[key] = nil }
|
240
260
|
@aggregation_cache[name] = nil
|
241
261
|
else
|
242
|
-
|
243
|
-
part = converter.respond_to?(:call) ?
|
244
|
-
converter.call(part) :
|
245
|
-
class_name.constantize.send(converter, part)
|
246
|
-
end
|
247
|
-
|
248
|
-
mapping.each { |pair| self[pair.first] = part.send(pair.last) }
|
262
|
+
mapping.each { |key, value| self[key] = part.send(value) }
|
249
263
|
@aggregation_cache[name] = part.freeze
|
250
264
|
end
|
251
265
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class AssociationRelation < Relation
|
3
|
+
def initialize(klass, table, association)
|
4
|
+
super(klass, table)
|
5
|
+
@association = association
|
6
|
+
end
|
7
|
+
|
8
|
+
def proxy_association
|
9
|
+
@association
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
other == to_a
|
14
|
+
end
|
15
|
+
|
16
|
+
def build(*args, &block)
|
17
|
+
scoping { @association.build(*args, &block) }
|
18
|
+
end
|
19
|
+
alias new build
|
20
|
+
|
21
|
+
def create(*args, &block)
|
22
|
+
scoping { @association.create(*args, &block) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def create!(*args, &block)
|
26
|
+
scoping { @association.create!(*args, &block) }
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def exec_queries
|
32
|
+
super.each { |r| @association.set_inverse_instance r }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|