activerecord 4.1.0 → 4.2.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 +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -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 +14 -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 +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -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 +15 -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 +97 -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 +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- 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 +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -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 +17 -0
- data/lib/active_record/type/integer.rb +55 -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 +56 -0
- data/lib/active_record/type/string.rb +36 -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 +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
data/lib/active_record/result.rb
CHANGED
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
class Result
|
32
32
|
include Enumerable
|
33
33
|
|
34
|
-
IDENTITY_TYPE =
|
34
|
+
IDENTITY_TYPE = Type::Value.new # :nodoc:
|
35
35
|
|
36
36
|
attr_reader :columns, :rows, :column_types
|
37
37
|
|
@@ -42,12 +42,8 @@ module ActiveRecord
|
|
42
42
|
@column_types = column_types
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
def column_type(name)
|
50
|
-
@column_types[name] || identity_type
|
45
|
+
def length
|
46
|
+
@rows.length
|
51
47
|
end
|
52
48
|
|
53
49
|
def each
|
@@ -82,6 +78,15 @@ module ActiveRecord
|
|
82
78
|
hash_rows.last
|
83
79
|
end
|
84
80
|
|
81
|
+
def cast_values(type_overrides = {}) # :nodoc:
|
82
|
+
types = columns.map { |name| column_type(name, type_overrides) }
|
83
|
+
result = rows.map do |values|
|
84
|
+
types.zip(values).map { |type, value| type.type_cast_from_database(value) }
|
85
|
+
end
|
86
|
+
|
87
|
+
columns.one? ? result.map!(&:first) : result
|
88
|
+
end
|
89
|
+
|
85
90
|
def initialize_copy(other)
|
86
91
|
@columns = columns.dup
|
87
92
|
@rows = rows.dup
|
@@ -91,6 +96,12 @@ module ActiveRecord
|
|
91
96
|
|
92
97
|
private
|
93
98
|
|
99
|
+
def column_type(name, type_overrides = {})
|
100
|
+
type_overrides.fetch(name) do
|
101
|
+
column_types.fetch(name, IDENTITY_TYPE)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
94
105
|
def hash_rows
|
95
106
|
@hash_rows ||=
|
96
107
|
begin
|
@@ -87,12 +87,15 @@ module ActiveRecord
|
|
87
87
|
# { address: Address.new("123 abc st.", "chicago") }
|
88
88
|
# # => "address_street='123 abc st.' and address_city='chicago'"
|
89
89
|
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
|
90
|
+
ActiveSupport::Deprecation.warn(<<-EOWARN)
|
91
|
+
sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
|
92
|
+
EOWARN
|
90
93
|
attrs = PredicateBuilder.resolve_column_aliases self, attrs
|
91
94
|
attrs = expand_hash_conditions_for_aggregates(attrs)
|
92
95
|
|
93
96
|
table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
|
94
97
|
PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
|
95
|
-
connection.visitor.
|
98
|
+
connection.visitor.compile b
|
96
99
|
}.join(' AND ')
|
97
100
|
end
|
98
101
|
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
|
@@ -107,6 +110,13 @@ module ActiveRecord
|
|
107
110
|
end.join(', ')
|
108
111
|
end
|
109
112
|
|
113
|
+
# Sanitizes a +string+ so that it is safe to use within an SQL
|
114
|
+
# LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
|
115
|
+
def sanitize_sql_like(string, escape_character = "\\")
|
116
|
+
pattern = Regexp.union(escape_character, "%", "_")
|
117
|
+
string.gsub(pattern) { |x| [escape_character, x].join }
|
118
|
+
end
|
119
|
+
|
110
120
|
# Accepts an array of conditions. The array has each value
|
111
121
|
# sanitized and interpolated into the SQL statement.
|
112
122
|
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
|
@@ -127,7 +137,7 @@ module ActiveRecord
|
|
127
137
|
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
|
128
138
|
bound = values.dup
|
129
139
|
c = connection
|
130
|
-
statement.gsub(
|
140
|
+
statement.gsub(/\?/) do
|
131
141
|
replace_bind_variable(bound.shift, c)
|
132
142
|
end
|
133
143
|
end
|
data/lib/active_record/schema.rb
CHANGED
@@ -91,16 +91,17 @@ HEADER
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def tables(stream)
|
94
|
-
@connection.tables.sort
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
94
|
+
sorted_tables = @connection.tables.sort
|
95
|
+
|
96
|
+
sorted_tables.each do |table_name|
|
97
|
+
table(table_name, stream) unless ignored?(table_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
# dump foreign keys at the end to make sure all dependent tables exist.
|
101
|
+
if @connection.supports_foreign_keys?
|
102
|
+
sorted_tables.each do |tbl|
|
103
|
+
foreign_keys(tbl, stream) unless ignored?(tbl)
|
102
104
|
end
|
103
|
-
table(tbl, stream)
|
104
105
|
end
|
105
106
|
end
|
106
107
|
|
@@ -110,17 +111,15 @@ HEADER
|
|
110
111
|
tbl = StringIO.new
|
111
112
|
|
112
113
|
# first dump primary key column
|
113
|
-
|
114
|
-
pk, _ = @connection.pk_and_sequence_for(table)
|
115
|
-
elsif @connection.respond_to?(:primary_key)
|
116
|
-
pk = @connection.primary_key(table)
|
117
|
-
end
|
114
|
+
pk = @connection.primary_key(table)
|
118
115
|
|
119
116
|
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
|
120
117
|
pkcol = columns.detect { |c| c.name == pk }
|
121
118
|
if pkcol
|
122
119
|
if pk != 'id'
|
123
120
|
tbl.print %Q(, primary_key: "#{pk}")
|
121
|
+
elsif pkcol.sql_type == 'bigint'
|
122
|
+
tbl.print ", id: :bigserial"
|
124
123
|
elsif pkcol.sql_type == 'uuid'
|
125
124
|
tbl.print ", id: :uuid"
|
126
125
|
tbl.print %Q(, default: "#{pkcol.default_function}") if pkcol.default_function
|
@@ -128,7 +127,7 @@ HEADER
|
|
128
127
|
else
|
129
128
|
tbl.print ", id: false"
|
130
129
|
end
|
131
|
-
tbl.print ", force:
|
130
|
+
tbl.print ", force: :cascade"
|
132
131
|
tbl.puts " do |t|"
|
133
132
|
|
134
133
|
# then dump all non-primary key columns
|
@@ -186,34 +185,67 @@ HEADER
|
|
186
185
|
if (indexes = @connection.indexes(table)).any?
|
187
186
|
add_index_statements = indexes.map do |index|
|
188
187
|
statement_parts = [
|
189
|
-
|
188
|
+
"add_index #{remove_prefix_and_suffix(index.table).inspect}",
|
190
189
|
index.columns.inspect,
|
191
|
-
|
190
|
+
"name: #{index.name.inspect}",
|
192
191
|
]
|
193
192
|
statement_parts << 'unique: true' if index.unique
|
194
193
|
|
195
194
|
index_lengths = (index.lengths || []).compact
|
196
|
-
statement_parts <<
|
195
|
+
statement_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
|
197
196
|
|
198
|
-
index_orders =
|
199
|
-
statement_parts <<
|
197
|
+
index_orders = index.orders || {}
|
198
|
+
statement_parts << "order: #{index.orders.inspect}" if index_orders.any?
|
199
|
+
statement_parts << "where: #{index.where.inspect}" if index.where
|
200
|
+
statement_parts << "using: #{index.using.inspect}" if index.using
|
201
|
+
statement_parts << "type: #{index.type.inspect}" if index.type
|
200
202
|
|
201
|
-
statement_parts
|
203
|
+
" #{statement_parts.join(', ')}"
|
204
|
+
end
|
202
205
|
|
203
|
-
|
206
|
+
stream.puts add_index_statements.sort.join("\n")
|
207
|
+
stream.puts
|
208
|
+
end
|
209
|
+
end
|
204
210
|
|
205
|
-
|
211
|
+
def foreign_keys(table, stream)
|
212
|
+
if (foreign_keys = @connection.foreign_keys(table)).any?
|
213
|
+
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|
214
|
+
parts = [
|
215
|
+
"add_foreign_key #{remove_prefix_and_suffix(foreign_key.from_table).inspect}",
|
216
|
+
remove_prefix_and_suffix(foreign_key.to_table).inspect,
|
217
|
+
]
|
206
218
|
|
207
|
-
|
219
|
+
if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
|
220
|
+
parts << "column: #{foreign_key.column.inspect}"
|
221
|
+
end
|
222
|
+
|
223
|
+
if foreign_key.custom_primary_key?
|
224
|
+
parts << "primary_key: #{foreign_key.primary_key.inspect}"
|
225
|
+
end
|
226
|
+
|
227
|
+
if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
|
228
|
+
parts << "name: #{foreign_key.name.inspect}"
|
229
|
+
end
|
230
|
+
|
231
|
+
parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
|
232
|
+
parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
|
233
|
+
|
234
|
+
" #{parts.join(', ')}"
|
208
235
|
end
|
209
236
|
|
210
|
-
stream.puts
|
211
|
-
stream.puts
|
237
|
+
stream.puts add_foreign_key_statements.sort.join("\n")
|
212
238
|
end
|
213
239
|
end
|
214
240
|
|
215
241
|
def remove_prefix_and_suffix(table)
|
216
242
|
table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
|
217
243
|
end
|
244
|
+
|
245
|
+
def ignored?(table_name)
|
246
|
+
['schema_migrations', ignore_tables].flatten.any? do |ignored|
|
247
|
+
ignored === remove_prefix_and_suffix(table_name)
|
248
|
+
end
|
249
|
+
end
|
218
250
|
end
|
219
251
|
end
|
@@ -5,6 +5,9 @@ require 'active_record/base'
|
|
5
5
|
module ActiveRecord
|
6
6
|
class SchemaMigration < ActiveRecord::Base
|
7
7
|
class << self
|
8
|
+
def primary_key
|
9
|
+
nil
|
10
|
+
end
|
8
11
|
|
9
12
|
def table_name
|
10
13
|
"#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
|
@@ -36,6 +39,14 @@ module ActiveRecord
|
|
36
39
|
connection.drop_table(table_name)
|
37
40
|
end
|
38
41
|
end
|
42
|
+
|
43
|
+
def normalize_migration_number(number)
|
44
|
+
"%.3d" % number.to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
def normalized_versions
|
48
|
+
pluck(:version).map { |v| normalize_migration_number v }
|
49
|
+
end
|
39
50
|
end
|
40
51
|
|
41
52
|
def version
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
# Returns a scope for the model without the
|
14
|
+
# Returns a scope for the model without the previously set scopes.
|
15
15
|
#
|
16
16
|
# class Post < ActiveRecord::Base
|
17
17
|
# def self.default_scope
|
@@ -19,11 +19,12 @@ module ActiveRecord
|
|
19
19
|
# end
|
20
20
|
# end
|
21
21
|
#
|
22
|
-
# Post.all
|
23
|
-
# Post.unscoped.all
|
22
|
+
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
|
23
|
+
# Post.unscoped.all # Fires "SELECT * FROM posts"
|
24
|
+
# Post.where(published: false).unscoped.all # Fires "SELECT * FROM posts"
|
24
25
|
#
|
25
26
|
# This method also accepts a block. All queries inside the block will
|
26
|
-
# not use the
|
27
|
+
# not use the previously set scopes.
|
27
28
|
#
|
28
29
|
# Post.unscoped {
|
29
30
|
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
|
@@ -93,14 +94,14 @@ module ActiveRecord
|
|
93
94
|
self.default_scopes += [scope]
|
94
95
|
end
|
95
96
|
|
96
|
-
def build_default_scope # :nodoc:
|
97
|
+
def build_default_scope(base_rel = relation) # :nodoc:
|
97
98
|
if !Base.is_a?(method(:default_scope).owner)
|
98
99
|
# The user has defined their own default scope method, so call that
|
99
100
|
evaluate_default_scope { default_scope }
|
100
101
|
elsif default_scopes.any?
|
101
102
|
evaluate_default_scope do
|
102
|
-
default_scopes.inject(
|
103
|
-
default_scope.merge(
|
103
|
+
default_scopes.inject(base_rel) do |default_scope, scope|
|
104
|
+
default_scope.merge(base_rel.scoping { scope.call })
|
104
105
|
end
|
105
106
|
end
|
106
107
|
end
|
@@ -139,6 +139,10 @@ module ActiveRecord
|
|
139
139
|
# Article.published.featured.latest_article
|
140
140
|
# Article.featured.titles
|
141
141
|
def scope(name, body, &block)
|
142
|
+
unless body.respond_to?(:call)
|
143
|
+
raise ArgumentError, 'The scope body needs to be callable.'
|
144
|
+
end
|
145
|
+
|
142
146
|
if dangerous_class_method?(name)
|
143
147
|
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
|
144
148
|
"on the model \"#{self.name}\", but Active Record already defined " \
|
@@ -180,13 +180,9 @@ module ActiveRecord #:nodoc:
|
|
180
180
|
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
|
181
181
|
def compute_type
|
182
182
|
klass = @serializable.class
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
klass.columns_hash[name].type
|
187
|
-
else
|
188
|
-
NilClass
|
189
|
-
end
|
183
|
+
column = klass.columns_hash[name] || Type::Value.new
|
184
|
+
|
185
|
+
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name] || column.type
|
190
186
|
|
191
187
|
{ :text => :string,
|
192
188
|
:time => :datetime }[type] || type
|
@@ -1,26 +1,111 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
|
3
3
|
# Statement cache is used to cache a single statement in order to avoid creating the AST again.
|
4
|
-
# Initializing the cache is done by passing the statement in the
|
4
|
+
# Initializing the cache is done by passing the statement in the create block:
|
5
5
|
#
|
6
|
-
# cache =
|
7
|
-
# Book.where(name: "my book").
|
6
|
+
# cache = StatementCache.create(Book.connection) do |params|
|
7
|
+
# Book.where(name: "my book").where("author_id > 3")
|
8
8
|
# end
|
9
9
|
#
|
10
10
|
# The cached statement is executed by using the +execute+ method:
|
11
11
|
#
|
12
|
-
# cache.execute
|
12
|
+
# cache.execute([], Book, Book.connection)
|
13
13
|
#
|
14
14
|
# The relation returned by the block is cached, and for each +execute+ call the cached relation gets duped.
|
15
15
|
# Database is queried when +to_a+ is called on the relation.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
#
|
17
|
+
# If you want to cache the statement without the values you can use the +bind+ method of the
|
18
|
+
# block parameter.
|
19
|
+
#
|
20
|
+
# cache = StatementCache.create(Book.connection) do |params|
|
21
|
+
# Book.where(name: params.bind)
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# And pass the bind values as the first argument of +execute+ call.
|
25
|
+
#
|
26
|
+
# cache.execute(["my book"], Book, Book.connection)
|
27
|
+
class StatementCache # :nodoc:
|
28
|
+
class Substitute; end # :nodoc:
|
29
|
+
|
30
|
+
class Query # :nodoc:
|
31
|
+
def initialize(sql)
|
32
|
+
@sql = sql
|
33
|
+
end
|
34
|
+
|
35
|
+
def sql_for(binds, connection)
|
36
|
+
@sql
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class PartialQuery < Query # :nodoc:
|
41
|
+
def initialize values
|
42
|
+
@values = values
|
43
|
+
@indexes = values.each_with_index.find_all { |thing,i|
|
44
|
+
Arel::Nodes::BindParam === thing
|
45
|
+
}.map(&:last)
|
46
|
+
end
|
47
|
+
|
48
|
+
def sql_for(binds, connection)
|
49
|
+
val = @values.dup
|
50
|
+
binds = binds.dup
|
51
|
+
@indexes.each { |i| val[i] = connection.quote(*binds.shift.reverse) }
|
52
|
+
val.join
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.query(visitor, ast)
|
57
|
+
Query.new visitor.accept(ast, Arel::Collectors::SQLString.new).value
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.partial_query(visitor, ast, collector)
|
61
|
+
collected = visitor.accept(ast, collector).value
|
62
|
+
PartialQuery.new collected
|
20
63
|
end
|
21
64
|
|
22
|
-
|
23
|
-
|
65
|
+
class Params # :nodoc:
|
66
|
+
def bind; Substitute.new; end
|
67
|
+
end
|
68
|
+
|
69
|
+
class BindMap # :nodoc:
|
70
|
+
def initialize(bind_values)
|
71
|
+
@indexes = []
|
72
|
+
@bind_values = bind_values
|
73
|
+
|
74
|
+
bind_values.each_with_index do |(_, value), i|
|
75
|
+
if Substitute === value
|
76
|
+
@indexes << i
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def bind(values)
|
82
|
+
bvs = @bind_values.map { |pair| pair.dup }
|
83
|
+
@indexes.each_with_index { |offset,i| bvs[offset][1] = values[i] }
|
84
|
+
bvs
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
attr_reader :bind_map, :query_builder
|
89
|
+
|
90
|
+
def self.create(connection, block = Proc.new)
|
91
|
+
relation = block.call Params.new
|
92
|
+
bind_map = BindMap.new relation.bind_values
|
93
|
+
query_builder = connection.cacheable_query relation.arel
|
94
|
+
new query_builder, bind_map
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize(query_builder, bind_map)
|
98
|
+
@query_builder = query_builder
|
99
|
+
@bind_map = bind_map
|
100
|
+
end
|
101
|
+
|
102
|
+
def execute(params, klass, connection)
|
103
|
+
bind_values = bind_map.bind params
|
104
|
+
|
105
|
+
sql = query_builder.sql_for bind_values, connection
|
106
|
+
|
107
|
+
klass.find_by_sql sql, bind_values
|
24
108
|
end
|
109
|
+
alias :call :execute
|
25
110
|
end
|
26
111
|
end
|
data/lib/active_record/store.rb
CHANGED
@@ -66,8 +66,9 @@ module ActiveRecord
|
|
66
66
|
extend ActiveSupport::Concern
|
67
67
|
|
68
68
|
included do
|
69
|
-
|
70
|
-
|
69
|
+
class << self
|
70
|
+
attr_accessor :local_stored_attributes
|
71
|
+
end
|
71
72
|
end
|
72
73
|
|
73
74
|
module ClassMethods
|
@@ -93,18 +94,26 @@ module ActiveRecord
|
|
93
94
|
|
94
95
|
# assign new store attribute and create new hash to ensure that each class in the hierarchy
|
95
96
|
# has its own hash of stored attributes.
|
96
|
-
self.
|
97
|
-
self.
|
98
|
-
self.
|
97
|
+
self.local_stored_attributes ||= {}
|
98
|
+
self.local_stored_attributes[store_attribute] ||= []
|
99
|
+
self.local_stored_attributes[store_attribute] |= keys
|
99
100
|
end
|
100
101
|
|
101
|
-
def _store_accessors_module
|
102
|
+
def _store_accessors_module # :nodoc:
|
102
103
|
@_store_accessors_module ||= begin
|
103
104
|
mod = Module.new
|
104
105
|
include mod
|
105
106
|
mod
|
106
107
|
end
|
107
108
|
end
|
109
|
+
|
110
|
+
def stored_attributes
|
111
|
+
parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
|
112
|
+
if self.local_stored_attributes
|
113
|
+
parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
|
114
|
+
end
|
115
|
+
parent
|
116
|
+
end
|
108
117
|
end
|
109
118
|
|
110
119
|
protected
|
@@ -120,10 +129,10 @@ module ActiveRecord
|
|
120
129
|
|
121
130
|
private
|
122
131
|
def store_accessor_for(store_attribute)
|
123
|
-
|
132
|
+
type_for_attribute(store_attribute.to_s).accessor
|
124
133
|
end
|
125
134
|
|
126
|
-
class HashAccessor
|
135
|
+
class HashAccessor # :nodoc:
|
127
136
|
def self.read(object, attribute, key)
|
128
137
|
prepare(object, attribute)
|
129
138
|
object.public_send(attribute)[key]
|
@@ -142,7 +151,7 @@ module ActiveRecord
|
|
142
151
|
end
|
143
152
|
end
|
144
153
|
|
145
|
-
class StringKeyedHashAccessor < HashAccessor
|
154
|
+
class StringKeyedHashAccessor < HashAccessor # :nodoc:
|
146
155
|
def self.read(object, attribute, key)
|
147
156
|
super object, attribute, key.to_s
|
148
157
|
end
|
@@ -152,7 +161,7 @@ module ActiveRecord
|
|
152
161
|
end
|
153
162
|
end
|
154
163
|
|
155
|
-
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor
|
164
|
+
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
|
156
165
|
def self.prepare(object, store_attribute)
|
157
166
|
attribute = object.send(store_attribute)
|
158
167
|
unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/string/filters'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Tasks # :nodoc:
|
3
5
|
class DatabaseAlreadyExists < StandardError; end # :nodoc:
|
@@ -6,7 +8,7 @@ module ActiveRecord
|
|
6
8
|
# <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
|
7
9
|
# logic behind common tasks used to manage database and migrations.
|
8
10
|
#
|
9
|
-
# The tasks defined here are used
|
11
|
+
# The tasks defined here are used with Rake tasks provided by Active Record.
|
10
12
|
#
|
11
13
|
# In order to use DatabaseTasks, a few config values need to be set. All the needed
|
12
14
|
# config values are set by Rails already, so it's necessary to do it only if you
|
@@ -14,7 +16,6 @@ module ActiveRecord
|
|
14
16
|
# (in such case after configuring the database tasks, you can also use the rake tasks
|
15
17
|
# defined in Active Record).
|
16
18
|
#
|
17
|
-
#
|
18
19
|
# The possible config values are:
|
19
20
|
#
|
20
21
|
# * +env+: current environment (like Rails.env).
|
@@ -28,7 +29,7 @@ module ActiveRecord
|
|
28
29
|
# Example usage of +DatabaseTasks+ outside Rails could look as such:
|
29
30
|
#
|
30
31
|
# include ActiveRecord::Tasks
|
31
|
-
# DatabaseTasks.database_configuration = YAML.
|
32
|
+
# DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
|
32
33
|
# DatabaseTasks.db_dir = 'db'
|
33
34
|
# # other settings...
|
34
35
|
#
|
@@ -59,7 +60,11 @@ module ActiveRecord
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def fixtures_path
|
62
|
-
@fixtures_path ||=
|
63
|
+
@fixtures_path ||= if ENV['FIXTURES_PATH']
|
64
|
+
File.join(root, ENV['FIXTURES_PATH'])
|
65
|
+
else
|
66
|
+
File.join(root, 'test', 'fixtures')
|
67
|
+
end
|
63
68
|
end
|
64
69
|
|
65
70
|
def root
|
@@ -107,6 +112,8 @@ module ActiveRecord
|
|
107
112
|
def drop(*arguments)
|
108
113
|
configuration = arguments.first
|
109
114
|
class_for_adapter(configuration['adapter']).new(*arguments).drop
|
115
|
+
rescue ActiveRecord::NoDatabaseError
|
116
|
+
$stderr.puts "Database '#{configuration['database']}' does not exist"
|
110
117
|
rescue Exception => error
|
111
118
|
$stderr.puts error, *(error.backtrace)
|
112
119
|
$stderr.puts "Couldn't drop #{configuration['database']}"
|
@@ -122,6 +129,18 @@ module ActiveRecord
|
|
122
129
|
}
|
123
130
|
end
|
124
131
|
|
132
|
+
def migrate
|
133
|
+
verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
134
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
135
|
+
scope = ENV['SCOPE']
|
136
|
+
verbose_was, Migration.verbose = Migration.verbose, verbose
|
137
|
+
Migrator.migrate(Migrator.migrations_paths, version) do |migration|
|
138
|
+
scope.blank? || scope == migration.scope
|
139
|
+
end
|
140
|
+
ensure
|
141
|
+
Migration.verbose = verbose_was
|
142
|
+
end
|
143
|
+
|
125
144
|
def charset_current(environment = env)
|
126
145
|
charset ActiveRecord::Base.configurations[environment]
|
127
146
|
end
|
@@ -144,6 +163,19 @@ module ActiveRecord
|
|
144
163
|
class_for_adapter(configuration['adapter']).new(configuration).purge
|
145
164
|
end
|
146
165
|
|
166
|
+
def purge_all
|
167
|
+
each_local_configuration { |configuration|
|
168
|
+
purge configuration
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
def purge_current(environment = env)
|
173
|
+
each_current_configuration(environment) { |configuration|
|
174
|
+
purge configuration
|
175
|
+
}
|
176
|
+
ActiveRecord::Base.establish_connection(environment.to_sym)
|
177
|
+
end
|
178
|
+
|
147
179
|
def structure_dump(*arguments)
|
148
180
|
configuration = arguments.first
|
149
181
|
filename = arguments.delete_at 1
|
@@ -157,20 +189,54 @@ module ActiveRecord
|
|
157
189
|
end
|
158
190
|
|
159
191
|
def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
|
192
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
193
|
+
This method will act on a specific connection in the future.
|
194
|
+
To act on the current connection, use `load_schema_current` instead.
|
195
|
+
MSG
|
196
|
+
|
197
|
+
load_schema_current(format, file)
|
198
|
+
end
|
199
|
+
|
200
|
+
def schema_file(format = ActiveSupport::Base.schema_format)
|
201
|
+
case format
|
202
|
+
when :ruby
|
203
|
+
File.join(db_dir, "schema.rb")
|
204
|
+
when :sql
|
205
|
+
File.join(db_dir, "structure.sql")
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# This method is the successor of +load_schema+. We should rename it
|
210
|
+
# after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
|
211
|
+
def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
|
212
|
+
file ||= schema_file(format)
|
213
|
+
|
160
214
|
case format
|
161
215
|
when :ruby
|
162
|
-
file ||= File.join(db_dir, "schema.rb")
|
163
216
|
check_schema_file(file)
|
217
|
+
ActiveRecord::Base.establish_connection(configuration)
|
164
218
|
load(file)
|
165
219
|
when :sql
|
166
|
-
file ||= File.join(db_dir, "structure.sql")
|
167
220
|
check_schema_file(file)
|
168
|
-
structure_load(
|
221
|
+
structure_load(configuration, file)
|
169
222
|
else
|
170
223
|
raise ArgumentError, "unknown format #{format.inspect}"
|
171
224
|
end
|
172
225
|
end
|
173
226
|
|
227
|
+
def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
228
|
+
if File.exist?(file || schema_file(format))
|
229
|
+
load_schema_current(format, file, environment)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
234
|
+
each_current_configuration(environment) { |configuration|
|
235
|
+
load_schema_for configuration, format, file
|
236
|
+
}
|
237
|
+
ActiveRecord::Base.establish_connection(environment.to_sym)
|
238
|
+
end
|
239
|
+
|
174
240
|
def check_schema_file(filename)
|
175
241
|
unless File.exist?(filename)
|
176
242
|
message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
|