activerecord 4.2.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1537 -789
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +27 -14
- data/lib/active_record/associations/preloader.rb +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
- data/lib/active_record/attribute_methods/write.rb +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +176 -115
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -17
- data/lib/active_record/scoping/default.rb +24 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +159 -67
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +21 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +60 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -101
data/MIT-LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2004-
|
1
|
+
Copyright (c) 2004-2016 David Heinemeier Hansson
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -26,13 +26,13 @@ The Product class is automatically mapped to the table named "products",
|
|
26
26
|
which might look like this:
|
27
27
|
|
28
28
|
CREATE TABLE products (
|
29
|
-
id int
|
29
|
+
id int NOT NULL auto_increment,
|
30
30
|
name varchar(255),
|
31
31
|
PRIMARY KEY (id)
|
32
32
|
);
|
33
33
|
|
34
|
-
This would also define the following accessors:
|
35
|
-
|
34
|
+
This would also define the following accessors: <tt>Product#name</tt> and
|
35
|
+
<tt>Product#name=(new_name)</tt>.
|
36
36
|
|
37
37
|
|
38
38
|
* Associations between objects defined by simple class methods.
|
@@ -125,7 +125,7 @@ This would also define the following accessors: `Product#name` and
|
|
125
125
|
)
|
126
126
|
|
127
127
|
{Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
|
128
|
-
MySQL[link:classes/ActiveRecord/ConnectionAdapters/
|
128
|
+
MySQL[link:classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
|
129
129
|
PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
|
130
130
|
SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
|
131
131
|
|
@@ -138,7 +138,7 @@ This would also define the following accessors: `Product#name` and
|
|
138
138
|
|
139
139
|
* Database agnostic schema management with Migrations.
|
140
140
|
|
141
|
-
class AddSystemSettings < ActiveRecord::Migration
|
141
|
+
class AddSystemSettings < ActiveRecord::Migration[5.0]
|
142
142
|
def up
|
143
143
|
create_table :system_settings do |t|
|
144
144
|
t.string :name
|
@@ -188,11 +188,11 @@ Admit the Database:
|
|
188
188
|
|
189
189
|
The latest version of Active Record can be installed with RubyGems:
|
190
190
|
|
191
|
-
|
191
|
+
$ gem install activerecord
|
192
192
|
|
193
193
|
Source code can be downloaded as part of the Rails project on GitHub:
|
194
194
|
|
195
|
-
* https://github.com/rails/rails/tree/
|
195
|
+
* https://github.com/rails/rails/tree/master/activerecord
|
196
196
|
|
197
197
|
|
198
198
|
== License
|
@@ -215,4 +215,3 @@ Bug reports can be filed for the Ruby on Rails project here:
|
|
215
215
|
Feature requests should be discussed on the rails-core mailing list here:
|
216
216
|
|
217
217
|
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
218
|
-
|
data/examples/performance.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require File.expand_path('../../../load_paths', __FILE__)
|
2
1
|
require "active_record"
|
3
2
|
require 'benchmark/ips'
|
4
3
|
|
@@ -39,8 +38,8 @@ class Exhibit < ActiveRecord::Base
|
|
39
38
|
where("notes IS NOT NULL")
|
40
39
|
end
|
41
40
|
|
42
|
-
def self.look(exhibits) exhibits.each
|
43
|
-
def self.feel(exhibits) exhibits.each
|
41
|
+
def self.look(exhibits) exhibits.each(&:look) end
|
42
|
+
def self.feel(exhibits) exhibits.each(&:feel) end
|
44
43
|
end
|
45
44
|
|
46
45
|
def progress_bar(int); print "." if (int%100).zero? ; end
|
data/examples/simple.rb
CHANGED
@@ -1,14 +1,31 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
#
|
3
|
-
module Aggregations
|
2
|
+
# See ActiveRecord::Aggregations::ClassMethods for documentation
|
3
|
+
module Aggregations
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
def
|
7
|
-
@aggregation_cache
|
6
|
+
def initialize_dup(*) # :nodoc:
|
7
|
+
@aggregation_cache = {}
|
8
|
+
super
|
8
9
|
end
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
def reload(*) # :nodoc:
|
12
|
+
clear_aggregation_cache
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def clear_aggregation_cache # :nodoc:
|
19
|
+
@aggregation_cache.clear if persisted?
|
20
|
+
end
|
21
|
+
|
22
|
+
def init_internals # :nodoc:
|
23
|
+
@aggregation_cache = {}
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# Active Record implements aggregation through a macro-like class method called #composed_of
|
28
|
+
# for representing attributes as value objects. It expresses relationships like "Account [is]
|
12
29
|
# composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
|
13
30
|
# to the macro adds a description of how the value objects are created from the attributes of
|
14
31
|
# the entity object (when the entity is initialized either as a new object or from finding an
|
@@ -16,7 +33,7 @@ module ActiveRecord
|
|
16
33
|
# the database).
|
17
34
|
#
|
18
35
|
# class Customer < ActiveRecord::Base
|
19
|
-
# composed_of :balance, class_name: "Money", mapping: %w(
|
36
|
+
# composed_of :balance, class_name: "Money", mapping: %w(amount currency)
|
20
37
|
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
|
21
38
|
# end
|
22
39
|
#
|
@@ -87,11 +104,6 @@ module ActiveRecord
|
|
87
104
|
# customer.address_city = "Copenhagen"
|
88
105
|
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
|
89
106
|
#
|
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
|
-
#
|
95
107
|
# customer.address = Address.new("May Street", "Chicago")
|
96
108
|
# customer.address_street # => "May Street"
|
97
109
|
# customer.address_city # => "Chicago"
|
@@ -108,12 +120,12 @@ module ActiveRecord
|
|
108
120
|
#
|
109
121
|
# It's also important to treat the value objects as immutable. Don't allow the Money object to have
|
110
122
|
# 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
|
123
|
+
# <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
|
112
124
|
# its own values. Active Record won't persist value objects that have been changed through means
|
113
125
|
# other than the writer method.
|
114
126
|
#
|
115
127
|
# The immutable requirement is enforced by Active Record by freezing any object assigned as a value
|
116
|
-
# object. Attempting to change it afterwards will result in a RuntimeError
|
128
|
+
# object. Attempting to change it afterwards will result in a +RuntimeError+.
|
117
129
|
#
|
118
130
|
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
|
119
131
|
# keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
|
@@ -122,17 +134,17 @@ module ActiveRecord
|
|
122
134
|
#
|
123
135
|
# By default value objects are initialized by calling the <tt>new</tt> constructor of the value
|
124
136
|
# class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
|
125
|
-
# option, as arguments. If the value class doesn't support this convention then
|
137
|
+
# option, as arguments. If the value class doesn't support this convention then #composed_of allows
|
126
138
|
# a custom constructor to be specified.
|
127
139
|
#
|
128
140
|
# When a new value is assigned to the value object, the default assumption is that the new value
|
129
141
|
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
|
130
142
|
# converted to an instance of value class if necessary.
|
131
143
|
#
|
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.
|
144
|
+
# For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
|
145
|
+
# aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
|
134
146
|
# 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
|
147
|
+
# New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
|
136
148
|
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
|
137
149
|
# these requirements:
|
138
150
|
#
|
@@ -161,7 +173,7 @@ module ActiveRecord
|
|
161
173
|
#
|
162
174
|
# == Finding records by a value object
|
163
175
|
#
|
164
|
-
# Once a
|
176
|
+
# Once a #composed_of relationship is specified for a model, records can be loaded from the database
|
165
177
|
# by specifying an instance of the value object in the conditions hash. The following example
|
166
178
|
# finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
|
167
179
|
#
|
@@ -174,7 +186,7 @@ module ActiveRecord
|
|
174
186
|
# Options are:
|
175
187
|
# * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
|
176
188
|
# can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
|
177
|
-
# to the Address class, but if the real class name is CompanyAddress
|
189
|
+
# to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
|
178
190
|
# with this option.
|
179
191
|
# * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
|
180
192
|
# object. Each mapping is represented as an array where the first item is the name of the
|
@@ -244,14 +256,16 @@ module ActiveRecord
|
|
244
256
|
def writer_method(name, class_name, mapping, allow_nil, converter)
|
245
257
|
define_method("#{name}=") do |part|
|
246
258
|
klass = class_name.constantize
|
247
|
-
if part.is_a?(Hash)
|
248
|
-
part = klass.new(*part.values)
|
249
|
-
end
|
250
259
|
|
251
260
|
unless part.is_a?(klass) || converter.nil? || part.nil?
|
252
261
|
part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
|
253
262
|
end
|
254
263
|
|
264
|
+
if part.is_a?(Hash)
|
265
|
+
raise ArgumentError unless part.size == part.keys.max
|
266
|
+
part = klass.new(*part.sort.map(&:last))
|
267
|
+
end
|
268
|
+
|
255
269
|
if part.nil? && allow_nil
|
256
270
|
mapping.each { |key, _| self[key] = nil }
|
257
271
|
@aggregation_cache[name] = nil
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class AssociationRelation < Relation
|
3
|
-
def initialize(klass, table, association)
|
4
|
-
super(klass, table)
|
3
|
+
def initialize(klass, table, predicate_builder, association)
|
4
|
+
super(klass, table, predicate_builder)
|
5
5
|
@association = association
|
6
6
|
end
|
7
7
|
|
@@ -10,7 +10,20 @@ module ActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def ==(other)
|
13
|
-
other ==
|
13
|
+
other == records
|
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) }
|
14
27
|
end
|
15
28
|
|
16
29
|
private
|
@@ -2,23 +2,25 @@ require 'active_support/core_ext/string/conversions'
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
|
-
# Keeps track of table aliases for ActiveRecord::Associations::
|
6
|
-
# ActiveRecord::Associations::ThroughAssociationScope
|
5
|
+
# Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
|
7
6
|
class AliasTracker # :nodoc:
|
8
|
-
attr_reader :aliases
|
7
|
+
attr_reader :aliases
|
9
8
|
|
10
|
-
def self.
|
11
|
-
|
9
|
+
def self.create(connection, initial_table, type_caster)
|
10
|
+
aliases = Hash.new(0)
|
11
|
+
aliases[initial_table] = 1
|
12
|
+
new connection, aliases, type_caster
|
12
13
|
end
|
13
14
|
|
14
|
-
def self.
|
15
|
-
if
|
16
|
-
|
15
|
+
def self.create_with_joins(connection, initial_table, joins, type_caster)
|
16
|
+
if joins.empty?
|
17
|
+
create(connection, initial_table, type_caster)
|
17
18
|
else
|
18
|
-
aliases = Hash.new { |h,k|
|
19
|
-
h[k] = initial_count_for(connection, k,
|
19
|
+
aliases = Hash.new { |h, k|
|
20
|
+
h[k] = initial_count_for(connection, k, joins)
|
20
21
|
}
|
21
|
-
|
22
|
+
aliases[initial_table] = 1
|
23
|
+
new connection, aliases, type_caster
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
@@ -51,19 +53,20 @@ module ActiveRecord
|
|
51
53
|
end
|
52
54
|
|
53
55
|
# table_joins is an array of arel joins which might conflict with the aliases we assign here
|
54
|
-
def initialize(connection, aliases)
|
56
|
+
def initialize(connection, aliases, type_caster)
|
55
57
|
@aliases = aliases
|
56
58
|
@connection = connection
|
59
|
+
@type_caster = type_caster
|
57
60
|
end
|
58
61
|
|
59
62
|
def aliased_table_for(table_name, aliased_name)
|
60
63
|
if aliases[table_name].zero?
|
61
64
|
# If it's zero, we can have our table_name
|
62
65
|
aliases[table_name] = 1
|
63
|
-
Arel::Table.new(table_name)
|
66
|
+
Arel::Table.new(table_name, type_caster: @type_caster)
|
64
67
|
else
|
65
68
|
# Otherwise, we need to use an alias
|
66
|
-
aliased_name = connection.table_alias_for(aliased_name)
|
69
|
+
aliased_name = @connection.table_alias_for(aliased_name)
|
67
70
|
|
68
71
|
# Update the count
|
69
72
|
aliases[aliased_name] += 1
|
@@ -73,14 +76,14 @@ module ActiveRecord
|
|
73
76
|
else
|
74
77
|
aliased_name
|
75
78
|
end
|
76
|
-
Arel::Table.new(table_name).alias(table_alias)
|
79
|
+
Arel::Table.new(table_name, type_caster: @type_caster).alias(table_alias)
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
80
83
|
private
|
81
84
|
|
82
85
|
def truncate(name)
|
83
|
-
name.slice(0, connection.table_alias_length - 2)
|
86
|
+
name.slice(0, @connection.table_alias_length - 2)
|
84
87
|
end
|
85
88
|
end
|
86
89
|
end
|
@@ -8,12 +8,12 @@ module ActiveRecord
|
|
8
8
|
#
|
9
9
|
# Association
|
10
10
|
# SingularAssociation
|
11
|
-
# HasOneAssociation
|
11
|
+
# HasOneAssociation + ForeignAssociation
|
12
12
|
# HasOneThroughAssociation + ThroughAssociation
|
13
13
|
# BelongsToAssociation
|
14
14
|
# BelongsToPolymorphicAssociation
|
15
15
|
# CollectionAssociation
|
16
|
-
# HasManyAssociation
|
16
|
+
# HasManyAssociation + ForeignAssociation
|
17
17
|
# HasManyThroughAssociation + ThroughAssociation
|
18
18
|
class Association #:nodoc:
|
19
19
|
attr_reader :owner, :target, :reflection
|
@@ -121,7 +121,7 @@ module ActiveRecord
|
|
121
121
|
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
|
122
122
|
# through association's scope)
|
123
123
|
def target_scope
|
124
|
-
AssociationRelation.create(klass, klass.arel_table, self).merge!(klass.all)
|
124
|
+
AssociationRelation.create(klass, klass.arel_table, klass.predicate_builder, self).merge!(klass.all)
|
125
125
|
end
|
126
126
|
|
127
127
|
# Loads the \target if needed and returns it.
|
@@ -163,9 +163,12 @@ module ActiveRecord
|
|
163
163
|
@reflection = @owner.class._reflect_on_association(reflection_name)
|
164
164
|
end
|
165
165
|
|
166
|
-
def initialize_attributes(record) #:nodoc:
|
166
|
+
def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
|
167
|
+
except_from_scope_attributes ||= {}
|
167
168
|
skip_assign = [reflection.foreign_key, reflection.type].compact
|
168
|
-
|
169
|
+
assigned_keys = record.changed
|
170
|
+
assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
|
171
|
+
attributes = create_scope.except(*(assigned_keys - skip_assign))
|
169
172
|
record.assign_attributes(attributes)
|
170
173
|
set_inverse_instance(record)
|
171
174
|
end
|
@@ -211,9 +214,12 @@ module ActiveRecord
|
|
211
214
|
# the kind of the class of the associated objects. Meant to be used as
|
212
215
|
# a sanity check when you are about to assign an associated record.
|
213
216
|
def raise_on_type_mismatch!(record)
|
214
|
-
unless record.is_a?(reflection.klass)
|
215
|
-
|
216
|
-
|
217
|
+
unless record.is_a?(reflection.klass)
|
218
|
+
fresh_class = reflection.class_name.safe_constantize
|
219
|
+
unless fresh_class && record.is_a?(fresh_class)
|
220
|
+
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
|
221
|
+
raise ActiveRecord::AssociationTypeMismatch, message
|
222
|
+
end
|
217
223
|
end
|
218
224
|
end
|
219
225
|
|
@@ -245,9 +251,17 @@ module ActiveRecord
|
|
245
251
|
|
246
252
|
def build_record(attributes)
|
247
253
|
reflection.build_association(attributes) do |record|
|
248
|
-
initialize_attributes(record)
|
254
|
+
initialize_attributes(record, attributes)
|
249
255
|
end
|
250
256
|
end
|
257
|
+
|
258
|
+
# Returns true if statement cache should be skipped on the association reader.
|
259
|
+
def skip_statement_cache?
|
260
|
+
reflection.has_scope? ||
|
261
|
+
scope.eager_loading? ||
|
262
|
+
klass.scope_attributes? ||
|
263
|
+
reflection.source_reflection.active_record.default_scopes.any?
|
264
|
+
end
|
251
265
|
end
|
252
266
|
end
|
253
267
|
end
|
@@ -2,41 +2,30 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class AssociationScope #:nodoc:
|
4
4
|
def self.scope(association, connection)
|
5
|
-
INSTANCE.scope
|
6
|
-
end
|
7
|
-
|
8
|
-
class BindSubstitution
|
9
|
-
def initialize(block)
|
10
|
-
@block = block
|
11
|
-
end
|
12
|
-
|
13
|
-
def bind_value(scope, column, value, alias_tracker)
|
14
|
-
substitute = alias_tracker.connection.substitute_at(column)
|
15
|
-
scope.bind_values += [[column, @block.call(value)]]
|
16
|
-
substitute
|
17
|
-
end
|
5
|
+
INSTANCE.scope(association, connection)
|
18
6
|
end
|
19
7
|
|
20
8
|
def self.create(&block)
|
21
|
-
block
|
22
|
-
new
|
9
|
+
block ||= lambda { |val| val }
|
10
|
+
new(block)
|
23
11
|
end
|
24
12
|
|
25
|
-
def initialize(
|
26
|
-
@
|
13
|
+
def initialize(value_transformation)
|
14
|
+
@value_transformation = value_transformation
|
27
15
|
end
|
28
16
|
|
29
17
|
INSTANCE = create
|
30
18
|
|
31
19
|
def scope(association, connection)
|
32
|
-
klass
|
33
|
-
reflection
|
34
|
-
scope
|
35
|
-
owner
|
36
|
-
alias_tracker = AliasTracker.
|
20
|
+
klass = association.klass
|
21
|
+
reflection = association.reflection
|
22
|
+
scope = klass.unscoped
|
23
|
+
owner = association.owner
|
24
|
+
alias_tracker = AliasTracker.create connection, association.klass.table_name, klass.type_caster
|
25
|
+
chain_head, chain_tail = get_chain(reflection, association, alias_tracker)
|
37
26
|
|
38
27
|
scope.extending! Array(reflection.options[:extend])
|
39
|
-
add_constraints(scope, owner, klass, reflection,
|
28
|
+
add_constraints(scope, owner, klass, reflection, chain_head, chain_tail)
|
40
29
|
end
|
41
30
|
|
42
31
|
def join_type
|
@@ -60,132 +49,115 @@ module ActiveRecord
|
|
60
49
|
binds
|
61
50
|
end
|
62
51
|
|
63
|
-
|
52
|
+
protected
|
64
53
|
|
65
|
-
|
66
|
-
chain.map do |reflection|
|
67
|
-
alias_tracker.aliased_table_for(
|
68
|
-
table_name_for(reflection, klass, refl),
|
69
|
-
table_alias_for(reflection, refl, reflection != refl)
|
70
|
-
)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def table_alias_for(reflection, refl, join = false)
|
75
|
-
name = "#{reflection.plural_name}_#{alias_suffix(refl)}"
|
76
|
-
name << "_join" if join
|
77
|
-
name
|
78
|
-
end
|
54
|
+
attr_reader :value_transformation
|
79
55
|
|
56
|
+
private
|
80
57
|
def join(table, constraint)
|
81
58
|
table.create_join(table, table.create_on(constraint), join_type)
|
82
59
|
end
|
83
60
|
|
84
|
-
def
|
85
|
-
|
86
|
-
columns[column_name]
|
87
|
-
end
|
88
|
-
|
89
|
-
def bind_value(scope, column, value, alias_tracker)
|
90
|
-
@bind_substitution.bind_value scope, column, value, alias_tracker
|
91
|
-
end
|
92
|
-
|
93
|
-
def bind(scope, table_name, column_name, value, tracker)
|
94
|
-
column = column_for table_name, column_name, tracker
|
95
|
-
bind_value scope, column, value, tracker
|
96
|
-
end
|
97
|
-
|
98
|
-
def last_chain_scope(scope, table, reflection, owner, tracker, assoc_klass)
|
99
|
-
join_keys = reflection.join_keys(assoc_klass)
|
61
|
+
def last_chain_scope(scope, table, reflection, owner, association_klass)
|
62
|
+
join_keys = reflection.join_keys(association_klass)
|
100
63
|
key = join_keys.key
|
101
64
|
foreign_key = join_keys.foreign_key
|
102
65
|
|
103
|
-
|
104
|
-
scope
|
66
|
+
value = transform_value(owner[foreign_key])
|
67
|
+
scope = scope.where(table.name => { key => value })
|
105
68
|
|
106
69
|
if reflection.type
|
107
|
-
|
108
|
-
|
109
|
-
scope = scope.where(table[reflection.type].eq(bind_val))
|
110
|
-
else
|
111
|
-
scope
|
70
|
+
polymorphic_type = transform_value(owner.class.base_class.name)
|
71
|
+
scope = scope.where(table.name => { reflection.type => polymorphic_type })
|
112
72
|
end
|
73
|
+
|
74
|
+
scope
|
113
75
|
end
|
114
76
|
|
115
|
-
def
|
116
|
-
|
77
|
+
def transform_value(value)
|
78
|
+
value_transformation.call(value)
|
79
|
+
end
|
80
|
+
|
81
|
+
def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
|
82
|
+
join_keys = reflection.join_keys(association_klass)
|
117
83
|
key = join_keys.key
|
118
84
|
foreign_key = join_keys.foreign_key
|
119
85
|
|
120
86
|
constraint = table[key].eq(foreign_table[foreign_key])
|
121
87
|
|
122
88
|
if reflection.type
|
123
|
-
value
|
124
|
-
|
125
|
-
scope = scope.where(table[reflection.type].eq(bind_val))
|
89
|
+
value = transform_value(next_reflection.klass.base_class.name)
|
90
|
+
scope = scope.where(table.name => { reflection.type => value })
|
126
91
|
end
|
127
92
|
|
128
93
|
scope = scope.joins(join(foreign_table, constraint))
|
129
94
|
end
|
130
95
|
|
131
|
-
|
132
|
-
|
133
|
-
|
96
|
+
class ReflectionProxy < SimpleDelegator # :nodoc:
|
97
|
+
attr_accessor :next
|
98
|
+
attr_reader :alias_name
|
134
99
|
|
135
|
-
|
100
|
+
def initialize(reflection, alias_name)
|
101
|
+
super(reflection)
|
102
|
+
@alias_name = alias_name
|
103
|
+
end
|
136
104
|
|
137
|
-
|
138
|
-
|
139
|
-
scope = last_chain_scope(scope, table, owner_reflection, owner, tracker, assoc_klass)
|
105
|
+
def all_includes; nil; end
|
106
|
+
end
|
140
107
|
|
141
|
-
|
142
|
-
|
108
|
+
def get_chain(reflection, association, tracker)
|
109
|
+
name = reflection.name
|
110
|
+
runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
|
111
|
+
previous_reflection = runtime_reflection
|
112
|
+
reflection.chain.drop(1).each do |refl|
|
113
|
+
alias_name = tracker.aliased_table_for(refl.table_name, refl.alias_candidate(name))
|
114
|
+
proxy = ReflectionProxy.new(refl, alias_name)
|
115
|
+
previous_reflection.next = proxy
|
116
|
+
previous_reflection = proxy
|
117
|
+
end
|
118
|
+
[runtime_reflection, previous_reflection]
|
119
|
+
end
|
143
120
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
121
|
+
def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tail)
|
122
|
+
owner_reflection = chain_tail
|
123
|
+
table = owner_reflection.alias_name
|
124
|
+
scope = last_chain_scope(scope, table, owner_reflection, owner, association_klass)
|
148
125
|
|
149
|
-
|
150
|
-
|
126
|
+
reflection = chain_head
|
127
|
+
loop do
|
128
|
+
break unless reflection
|
129
|
+
table = reflection.alias_name
|
130
|
+
|
131
|
+
unless reflection == chain_tail
|
132
|
+
next_reflection = reflection.next
|
133
|
+
foreign_table = next_reflection.alias_name
|
134
|
+
scope = next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
|
135
|
+
end
|
151
136
|
|
152
137
|
# Exclude the scope of the association itself, because that
|
153
138
|
# was already merged in the #scope method.
|
154
|
-
|
155
|
-
item = eval_scope(klass, scope_chain_item, owner)
|
139
|
+
reflection.constraints.each do |scope_chain_item|
|
140
|
+
item = eval_scope(reflection.klass, scope_chain_item, owner)
|
156
141
|
|
157
142
|
if scope_chain_item == refl.scope
|
158
|
-
scope.merge! item.except(:where, :includes
|
143
|
+
scope.merge! item.except(:where, :includes)
|
159
144
|
end
|
160
145
|
|
161
|
-
|
146
|
+
reflection.all_includes do
|
162
147
|
scope.includes! item.includes_values
|
163
148
|
end
|
164
149
|
|
165
|
-
scope.
|
166
|
-
scope.
|
150
|
+
scope.unscope!(*item.unscope_values)
|
151
|
+
scope.where_clause += item.where_clause
|
167
152
|
scope.order_values |= item.order_values
|
168
153
|
end
|
154
|
+
|
155
|
+
reflection = reflection.next
|
169
156
|
end
|
170
157
|
|
171
158
|
scope
|
172
159
|
end
|
173
160
|
|
174
|
-
def alias_suffix(refl)
|
175
|
-
refl.name
|
176
|
-
end
|
177
|
-
|
178
|
-
def table_name_for(reflection, klass, refl)
|
179
|
-
if reflection == refl
|
180
|
-
# If this is a polymorphic belongs_to, we want to get the klass from the
|
181
|
-
# association because it depends on the polymorphic_type attribute of
|
182
|
-
# the owner
|
183
|
-
klass.table_name
|
184
|
-
else
|
185
|
-
reflection.table_name
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
161
|
def eval_scope(klass, scope, owner)
|
190
162
|
klass.unscoped.instance_exec(owner, &scope)
|
191
163
|
end
|