activerecord 4.0.13 → 4.1.0.beta1
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 +4 -4
- data/CHANGELOG.md +745 -2700
- data/README.rdoc +2 -2
- data/examples/performance.rb +30 -18
- data/examples/simple.rb +4 -4
- data/lib/active_record.rb +2 -6
- data/lib/active_record/aggregations.rb +2 -1
- data/lib/active_record/association_relation.rb +0 -4
- data/lib/active_record/associations.rb +87 -43
- data/lib/active_record/associations/alias_tracker.rb +1 -3
- data/lib/active_record/associations/association.rb +8 -16
- data/lib/active_record/associations/association_scope.rb +5 -16
- data/lib/active_record/associations/belongs_to_association.rb +34 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +78 -54
- data/lib/active_record/associations/builder/belongs_to.rb +91 -58
- data/lib/active_record/associations/builder/collection_association.rb +47 -45
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
- data/lib/active_record/associations/builder/has_many.rb +2 -2
- data/lib/active_record/associations/builder/has_one.rb +5 -7
- data/lib/active_record/associations/builder/singular_association.rb +6 -7
- data/lib/active_record/associations/collection_association.rb +68 -105
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/has_many_association.rb +11 -9
- data/lib/active_record/associations/has_many_through_association.rb +16 -12
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +204 -165
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
- data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
- data/lib/active_record/associations/join_helper.rb +2 -11
- data/lib/active_record/associations/preloader.rb +89 -34
- data/lib/active_record/associations/preloader/association.rb +43 -25
- data/lib/active_record/associations/preloader/collection_association.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +58 -26
- data/lib/active_record/associations/singular_association.rb +6 -5
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +5 -2
- data/lib/active_record/attribute_methods.rb +45 -40
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
- data/lib/active_record/attribute_methods/dirty.rb +8 -22
- data/lib/active_record/attribute_methods/primary_key.rb +1 -7
- data/lib/active_record/attribute_methods/read.rb +55 -28
- data/lib/active_record/attribute_methods/serialization.rb +12 -33
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
- data/lib/active_record/attribute_methods/write.rb +37 -12
- data/lib/active_record/autosave_association.rb +207 -207
- data/lib/active_record/base.rb +5 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
- data/lib/active_record/connection_adapters/column.rb +1 -35
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
- data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
- data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
- data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
- data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
- data/lib/active_record/connection_handling.rb +2 -2
- data/lib/active_record/core.rb +22 -43
- data/lib/active_record/counter_cache.rb +7 -7
- data/lib/active_record/enum.rb +100 -0
- data/lib/active_record/errors.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +2 -1
- data/lib/active_record/fixtures.rb +171 -74
- data/lib/active_record/inheritance.rb +16 -22
- data/lib/active_record/integration.rb +52 -1
- data/lib/active_record/locking/optimistic.rb +7 -2
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -12
- data/lib/active_record/migration.rb +62 -46
- data/lib/active_record/migration/command_recorder.rb +7 -13
- data/lib/active_record/model_schema.rb +7 -14
- data/lib/active_record/nested_attributes.rb +10 -8
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +3 -3
- data/lib/active_record/persistence.rb +16 -34
- data/lib/active_record/querying.rb +14 -12
- data/lib/active_record/railtie.rb +0 -50
- data/lib/active_record/railties/databases.rake +12 -15
- data/lib/active_record/readonly_attributes.rb +0 -6
- data/lib/active_record/reflection.rb +189 -75
- data/lib/active_record/relation.rb +69 -94
- data/lib/active_record/relation/batches.rb +57 -23
- data/lib/active_record/relation/calculations.rb +36 -43
- data/lib/active_record/relation/delegation.rb +54 -39
- data/lib/active_record/relation/finder_methods.rb +107 -62
- data/lib/active_record/relation/merger.rb +7 -20
- data/lib/active_record/relation/predicate_builder.rb +57 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +110 -98
- data/lib/active_record/relation/spawn_methods.rb +1 -2
- data/lib/active_record/result.rb +45 -6
- data/lib/active_record/runtime_registry.rb +5 -0
- data/lib/active_record/sanitization.rb +6 -8
- data/lib/active_record/schema_dumper.rb +16 -5
- data/lib/active_record/schema_migration.rb +24 -25
- data/lib/active_record/scoping/default.rb +5 -18
- data/lib/active_record/scoping/named.rb +8 -29
- data/lib/active_record/store.rb +56 -28
- data/lib/active_record/tasks/database_tasks.rb +8 -4
- data/lib/active_record/timestamp.rb +4 -4
- data/lib/active_record/transactions.rb +8 -10
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -6
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +2 -8
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
- metadata +32 -45
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
- data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
- data/lib/active_record/test_case.rb +0 -102
@@ -64,8 +64,7 @@ module ActiveRecord
|
|
64
64
|
private
|
65
65
|
|
66
66
|
def relation_with(values) # :nodoc:
|
67
|
-
result = Relation.
|
68
|
-
result.default_scoped = default_scoped
|
67
|
+
result = Relation.create(klass, table, values)
|
69
68
|
result.extend(*extending_values) if extending_values.any?
|
70
69
|
result
|
71
70
|
end
|
data/lib/active_record/result.rb
CHANGED
@@ -3,11 +3,36 @@ module ActiveRecord
|
|
3
3
|
# This class encapsulates a Result returned from calling +exec_query+ on any
|
4
4
|
# database connection adapter. For example:
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
|
7
|
+
# result # => #<ActiveRecord::Result:0xdeadbeef>
|
8
|
+
#
|
9
|
+
# # Get the column names of the result:
|
10
|
+
# result.columns
|
11
|
+
# # => ["id", "title", "body"]
|
12
|
+
#
|
13
|
+
# # Get the record values of the result:
|
14
|
+
# result.rows
|
15
|
+
# # => [[1, "title_1", "body_1"],
|
16
|
+
# [2, "title_2", "body_2"],
|
17
|
+
# ...
|
18
|
+
# ]
|
19
|
+
#
|
20
|
+
# # Get an array of hashes representing the result (column => value):
|
21
|
+
# result.to_hash
|
22
|
+
# # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
|
23
|
+
# {"id" => 2, "title" => "title_2", "body" => "body_2"},
|
24
|
+
# ...
|
25
|
+
# ]
|
26
|
+
#
|
27
|
+
# # ActiveRecord::Result also includes Enumerable.
|
28
|
+
# result.each do |row|
|
29
|
+
# puts row['title'] + " " + row['body']
|
30
|
+
# end
|
8
31
|
class Result
|
9
32
|
include Enumerable
|
10
33
|
|
34
|
+
IDENTITY_TYPE = Class.new { def type_cast(v); v; end }.new # :nodoc:
|
35
|
+
|
11
36
|
attr_reader :columns, :rows, :column_types
|
12
37
|
|
13
38
|
def initialize(columns, rows, column_types = {})
|
@@ -17,8 +42,20 @@ module ActiveRecord
|
|
17
42
|
@column_types = column_types
|
18
43
|
end
|
19
44
|
|
45
|
+
def identity_type # :nodoc:
|
46
|
+
IDENTITY_TYPE
|
47
|
+
end
|
48
|
+
|
49
|
+
def column_type(name)
|
50
|
+
@column_types[name] || identity_type
|
51
|
+
end
|
52
|
+
|
20
53
|
def each
|
21
|
-
|
54
|
+
if block_given?
|
55
|
+
hash_rows.each { |row| yield row }
|
56
|
+
else
|
57
|
+
hash_rows.to_enum
|
58
|
+
end
|
22
59
|
end
|
23
60
|
|
24
61
|
def to_hash
|
@@ -46,12 +83,14 @@ module ActiveRecord
|
|
46
83
|
end
|
47
84
|
|
48
85
|
def initialize_copy(other)
|
49
|
-
@columns
|
50
|
-
@rows
|
51
|
-
@
|
86
|
+
@columns = columns.dup
|
87
|
+
@rows = rows.dup
|
88
|
+
@column_types = column_types.dup
|
89
|
+
@hash_rows = nil
|
52
90
|
end
|
53
91
|
|
54
92
|
private
|
93
|
+
|
55
94
|
def hash_rows
|
56
95
|
@hash_rows ||=
|
57
96
|
begin
|
@@ -13,5 +13,10 @@ module ActiveRecord
|
|
13
13
|
extend ActiveSupport::PerThreadRegistry
|
14
14
|
|
15
15
|
attr_accessor :connection_handler, :sql_runtime, :connection_id
|
16
|
+
|
17
|
+
[:connection_handler, :sql_runtime, :connection_id].each do |val|
|
18
|
+
class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
|
19
|
+
class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
|
20
|
+
end
|
16
21
|
end
|
17
22
|
end
|
@@ -3,8 +3,8 @@ module ActiveRecord
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
-
def quote_value(value, column
|
7
|
-
connection.quote(value,column)
|
6
|
+
def quote_value(value, column) #:nodoc:
|
7
|
+
connection.quote(value, column)
|
8
8
|
end
|
9
9
|
|
10
10
|
# Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
|
@@ -86,6 +86,7 @@ module ActiveRecord
|
|
86
86
|
# { address: Address.new("123 abc st.", "chicago") }
|
87
87
|
# # => "address_street='123 abc st.' and address_city='chicago'"
|
88
88
|
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
|
89
|
+
attrs = PredicateBuilder.resolve_column_aliases self, attrs
|
89
90
|
attrs = expand_hash_conditions_for_aggregates(attrs)
|
90
91
|
|
91
92
|
table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
|
@@ -99,9 +100,8 @@ module ActiveRecord
|
|
99
100
|
# { status: nil, group_id: 1 }
|
100
101
|
# # => "status = NULL , group_id = 1"
|
101
102
|
def sanitize_sql_hash_for_assignment(attrs, table)
|
102
|
-
c = connection
|
103
103
|
attrs.map do |attr, value|
|
104
|
-
"#{
|
104
|
+
"#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}"
|
105
105
|
end.join(', ')
|
106
106
|
end
|
107
107
|
|
@@ -152,10 +152,8 @@ module ActiveRecord
|
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
155
|
-
def quote_bound_value(value, c = connection
|
156
|
-
if
|
157
|
-
c.quote(value, column)
|
158
|
-
elsif value.respond_to?(:map) && !value.acts_like?(:string)
|
155
|
+
def quote_bound_value(value, c = connection) #:nodoc:
|
156
|
+
if value.respond_to?(:map) && !value.acts_like?(:string)
|
159
157
|
if value.respond_to?(:empty?) && value.empty?
|
160
158
|
c.quote(nil)
|
161
159
|
else
|
@@ -17,9 +17,19 @@ module ActiveRecord
|
|
17
17
|
cattr_accessor :ignore_tables
|
18
18
|
@@ignore_tables = []
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
class << self
|
21
|
+
def dump(connection=ActiveRecord::Base.connection, stream=STDOUT, config = ActiveRecord::Base)
|
22
|
+
new(connection, generate_options(config)).dump(stream)
|
23
|
+
stream
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def generate_options(config)
|
28
|
+
{
|
29
|
+
table_name_prefix: config.table_name_prefix,
|
30
|
+
table_name_suffix: config.table_name_suffix
|
31
|
+
}
|
32
|
+
end
|
23
33
|
end
|
24
34
|
|
25
35
|
def dump(stream)
|
@@ -32,10 +42,11 @@ module ActiveRecord
|
|
32
42
|
|
33
43
|
private
|
34
44
|
|
35
|
-
def initialize(connection)
|
45
|
+
def initialize(connection, options = {})
|
36
46
|
@connection = connection
|
37
47
|
@types = @connection.native_database_types
|
38
48
|
@version = Migrator::current_version rescue nil
|
49
|
+
@options = options
|
39
50
|
end
|
40
51
|
|
41
52
|
def header(stream)
|
@@ -202,7 +213,7 @@ HEADER
|
|
202
213
|
end
|
203
214
|
|
204
215
|
def remove_prefix_and_suffix(table)
|
205
|
-
table.gsub(/^(#{
|
216
|
+
table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
|
206
217
|
end
|
207
218
|
end
|
208
219
|
end
|
@@ -4,41 +4,40 @@ require 'active_record/base'
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
class SchemaMigration < ActiveRecord::Base
|
7
|
-
|
8
|
-
nil
|
9
|
-
end
|
7
|
+
class << self
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
def table_name
|
10
|
+
"#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
|
11
|
+
end
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
def index_name
|
14
|
+
"#{table_name_prefix}unique_#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
|
15
|
+
end
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
version_options[:limit] = limit if limit
|
17
|
+
def table_exists?
|
18
|
+
connection.table_exists?(table_name)
|
19
|
+
end
|
23
20
|
|
24
|
-
|
25
|
-
|
21
|
+
def create_table(limit=nil)
|
22
|
+
unless table_exists?
|
23
|
+
version_options = {null: false}
|
24
|
+
version_options[:limit] = limit if limit
|
25
|
+
|
26
|
+
connection.create_table(table_name, id: false) do |t|
|
27
|
+
t.column :version, :string, version_options
|
28
|
+
end
|
29
|
+
connection.add_index table_name, :version, unique: true, name: index_name
|
26
30
|
end
|
27
|
-
connection.add_index table_name, :version, unique: true, name: index_name
|
28
31
|
end
|
29
|
-
end
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
def drop_table
|
34
|
+
if table_exists?
|
35
|
+
connection.remove_index table_name, name: index_name
|
36
|
+
connection.drop_table(table_name)
|
37
|
+
end
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
38
|
-
def self.normalize_migration_number(number)
|
39
|
-
"%.3d" % number.to_i
|
40
|
-
end
|
41
|
-
|
42
41
|
def version
|
43
42
|
super.to_i
|
44
43
|
end
|
@@ -8,14 +8,6 @@ module ActiveRecord
|
|
8
8
|
class_attribute :default_scopes, instance_writer: false, instance_predicate: false
|
9
9
|
|
10
10
|
self.default_scopes = []
|
11
|
-
|
12
|
-
def self.default_scopes?
|
13
|
-
ActiveSupport::Deprecation.warn(
|
14
|
-
"#default_scopes? is deprecated. Do something like #default_scopes.empty? instead."
|
15
|
-
)
|
16
|
-
|
17
|
-
!!self.default_scopes
|
18
|
-
end
|
19
11
|
end
|
20
12
|
|
21
13
|
module ClassMethods
|
@@ -91,29 +83,24 @@ module ActiveRecord
|
|
91
83
|
scope = Proc.new if block_given?
|
92
84
|
|
93
85
|
if scope.is_a?(Relation) || !scope.respond_to?(:call)
|
94
|
-
|
95
|
-
"
|
86
|
+
raise ArgumentError,
|
87
|
+
"Support for calling #default_scope without a block is removed. For example instead " \
|
96
88
|
"of `default_scope where(color: 'red')`, please use " \
|
97
89
|
"`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
|
98
90
|
"self.default_scope.)"
|
99
|
-
)
|
100
91
|
end
|
101
92
|
|
102
93
|
self.default_scopes += [scope]
|
103
94
|
end
|
104
95
|
|
105
|
-
def build_default_scope
|
96
|
+
def build_default_scope # :nodoc:
|
106
97
|
if !Base.is_a?(method(:default_scope).owner)
|
107
98
|
# The user has defined their own default scope method, so call that
|
108
99
|
evaluate_default_scope { default_scope }
|
109
100
|
elsif default_scopes.any?
|
110
101
|
evaluate_default_scope do
|
111
|
-
default_scopes.inject(
|
112
|
-
|
113
|
-
default_scope.merge(base_rel.scoping { scope.call })
|
114
|
-
else
|
115
|
-
default_scope.merge(scope)
|
116
|
-
end
|
102
|
+
default_scopes.inject(relation) do |default_scope, scope|
|
103
|
+
default_scope.merge(unscoped { scope.call })
|
117
104
|
end
|
118
105
|
end
|
119
106
|
end
|
@@ -25,22 +25,18 @@ module ActiveRecord
|
|
25
25
|
if current_scope
|
26
26
|
current_scope.clone
|
27
27
|
else
|
28
|
-
|
29
|
-
scope.default_scoped = true
|
30
|
-
scope
|
28
|
+
default_scoped
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
32
|
+
def default_scoped # :nodoc:
|
33
|
+
relation.merge(build_default_scope)
|
34
|
+
end
|
35
|
+
|
34
36
|
# Collects attributes from scopes that should be applied when creating
|
35
37
|
# an AR instance for the particular class this is called on.
|
36
38
|
def scope_attributes # :nodoc:
|
37
|
-
|
38
|
-
current_scope.scope_for_create
|
39
|
-
else
|
40
|
-
scope = relation
|
41
|
-
scope.default_scoped = true
|
42
|
-
scope.scope_for_create
|
43
|
-
end
|
39
|
+
all.scope_for_create
|
44
40
|
end
|
45
41
|
|
46
42
|
# Are there default attributes associated with this scope?
|
@@ -145,26 +141,9 @@ module ActiveRecord
|
|
145
141
|
def scope(name, body, &block)
|
146
142
|
extension = Module.new(&block) if block
|
147
143
|
|
148
|
-
# Check body.is_a?(Relation) to prevent the relation actually being
|
149
|
-
# loaded by respond_to?
|
150
|
-
if body.is_a?(Relation) || !body.respond_to?(:call)
|
151
|
-
ActiveSupport::Deprecation.warn(
|
152
|
-
"Using #scope without passing a callable object is deprecated. For " \
|
153
|
-
"example `scope :red, where(color: 'red')` should be changed to " \
|
154
|
-
"`scope :red, -> { where(color: 'red') }`. There are numerous gotchas " \
|
155
|
-
"in the former usage and it makes the implementation more complicated " \
|
156
|
-
"and buggy. (If you prefer, you can just define a class method named " \
|
157
|
-
"`self.red`.)"
|
158
|
-
)
|
159
|
-
end
|
160
|
-
|
161
144
|
singleton_class.send(:define_method, name) do |*args|
|
162
|
-
|
163
|
-
|
164
|
-
scope = scope.extending(extension) if extension
|
165
|
-
else
|
166
|
-
scope = body
|
167
|
-
end
|
145
|
+
scope = all.scoping { body.call(*args) }
|
146
|
+
scope = scope.extending(extension) if extension
|
168
147
|
|
169
148
|
scope || all
|
170
149
|
end
|
data/lib/active_record/store.rb
CHANGED
@@ -15,6 +15,11 @@ module ActiveRecord
|
|
15
15
|
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
|
16
16
|
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
|
17
17
|
#
|
18
|
+
# NOTE - If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
|
19
|
+
# the serialization provided by +store+. Simply use +store_accessor+ instead to generate
|
20
|
+
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
|
21
|
+
# using a symbol.
|
22
|
+
#
|
18
23
|
# Examples:
|
19
24
|
#
|
20
25
|
# class User < ActiveRecord::Base
|
@@ -61,9 +66,8 @@ module ActiveRecord
|
|
61
66
|
extend ActiveSupport::Concern
|
62
67
|
|
63
68
|
included do
|
64
|
-
|
65
|
-
|
66
|
-
end
|
69
|
+
class_attribute :stored_attributes, instance_accessor: false
|
70
|
+
self.stored_attributes = {}
|
67
71
|
end
|
68
72
|
|
69
73
|
module ClassMethods
|
@@ -89,9 +93,9 @@ module ActiveRecord
|
|
89
93
|
|
90
94
|
# assign new store attribute and create new hash to ensure that each class in the hierarchy
|
91
95
|
# has its own hash of stored attributes.
|
92
|
-
self.
|
93
|
-
self.
|
94
|
-
self.
|
96
|
+
self.stored_attributes = {} if self.stored_attributes.blank?
|
97
|
+
self.stored_attributes[store_attribute] ||= []
|
98
|
+
self.stored_attributes[store_attribute] |= keys
|
95
99
|
end
|
96
100
|
|
97
101
|
def _store_accessors_module
|
@@ -101,38 +105,62 @@ module ActiveRecord
|
|
101
105
|
mod
|
102
106
|
end
|
103
107
|
end
|
104
|
-
|
105
|
-
def stored_attributes
|
106
|
-
parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
|
107
|
-
if self.local_stored_attributes
|
108
|
-
parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
|
109
|
-
end
|
110
|
-
parent
|
111
|
-
end
|
112
108
|
end
|
113
109
|
|
114
110
|
protected
|
115
111
|
def read_store_attribute(store_attribute, key)
|
116
|
-
|
117
|
-
|
112
|
+
accessor = store_accessor_for(store_attribute)
|
113
|
+
accessor.read(self, store_attribute, key)
|
118
114
|
end
|
119
115
|
|
120
116
|
def write_store_attribute(store_attribute, key, value)
|
121
|
-
|
122
|
-
|
123
|
-
send :"#{store_attribute}_will_change!"
|
124
|
-
attribute[key] = value
|
125
|
-
end
|
117
|
+
accessor = store_accessor_for(store_attribute)
|
118
|
+
accessor.write(self, store_attribute, key, value)
|
126
119
|
end
|
127
120
|
|
128
121
|
private
|
129
|
-
def
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
122
|
+
def store_accessor_for(store_attribute)
|
123
|
+
@column_types[store_attribute.to_s].accessor
|
124
|
+
end
|
125
|
+
|
126
|
+
class HashAccessor
|
127
|
+
def self.read(object, attribute, key)
|
128
|
+
prepare(object, attribute)
|
129
|
+
object.public_send(attribute)[key]
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.write(object, attribute, key, value)
|
133
|
+
prepare(object, attribute)
|
134
|
+
if value != read(object, attribute, key)
|
135
|
+
object.public_send :"#{attribute}_will_change!"
|
136
|
+
object.public_send(attribute)[key] = value
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.prepare(object, attribute)
|
141
|
+
object.public_send :"#{attribute}=", {} unless object.send(attribute)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class StringKeyedHashAccessor < HashAccessor
|
146
|
+
def self.read(object, attribute, key)
|
147
|
+
super object, attribute, key.to_s
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.write(object, attribute, key, value)
|
151
|
+
super object, attribute, key.to_s, value
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor
|
156
|
+
def self.prepare(object, store_attribute)
|
157
|
+
attribute = object.send(store_attribute)
|
158
|
+
unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
|
159
|
+
attribute = IndifferentCoder.as_indifferent_hash(attribute)
|
160
|
+
object.send :"#{store_attribute}=", attribute
|
161
|
+
end
|
162
|
+
attribute
|
134
163
|
end
|
135
|
-
attribute
|
136
164
|
end
|
137
165
|
|
138
166
|
class IndifferentCoder # :nodoc:
|
@@ -150,7 +178,7 @@ module ActiveRecord
|
|
150
178
|
end
|
151
179
|
|
152
180
|
def load(yaml)
|
153
|
-
self.class.as_indifferent_hash
|
181
|
+
self.class.as_indifferent_hash(@coder.load(yaml))
|
154
182
|
end
|
155
183
|
|
156
184
|
def self.as_indifferent_hash(obj)
|