activerecord 4.1.16 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +1162 -1801
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- 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/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- 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 +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -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 +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- 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 +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- 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 +71 -46
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +57 -25
- 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 +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- 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 +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +4 -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 +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- 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,26 +111,23 @@ 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
|
-
end
|
116
|
-
if !pk && @connection.respond_to?(:primary_key)
|
117
|
-
pk = @connection.primary_key(table)
|
118
|
-
end
|
114
|
+
pk = @connection.primary_key(table)
|
119
115
|
|
120
116
|
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
|
121
117
|
pkcol = columns.detect { |c| c.name == pk }
|
122
118
|
if pkcol
|
123
119
|
if pk != 'id'
|
124
120
|
tbl.print %Q(, primary_key: "#{pk}")
|
121
|
+
elsif pkcol.sql_type == 'bigint'
|
122
|
+
tbl.print ", id: :bigserial"
|
125
123
|
elsif pkcol.sql_type == 'uuid'
|
126
124
|
tbl.print ", id: :uuid"
|
127
|
-
tbl.print %Q(, default:
|
125
|
+
tbl.print %Q(, default: #{pkcol.default_function.inspect})
|
128
126
|
end
|
129
127
|
else
|
130
128
|
tbl.print ", id: false"
|
131
129
|
end
|
132
|
-
tbl.print ", force:
|
130
|
+
tbl.print ", force: :cascade"
|
133
131
|
tbl.puts " do |t|"
|
134
132
|
|
135
133
|
# then dump all non-primary key columns
|
@@ -187,34 +185,67 @@ HEADER
|
|
187
185
|
if (indexes = @connection.indexes(table)).any?
|
188
186
|
add_index_statements = indexes.map do |index|
|
189
187
|
statement_parts = [
|
190
|
-
|
188
|
+
"add_index #{remove_prefix_and_suffix(index.table).inspect}",
|
191
189
|
index.columns.inspect,
|
192
|
-
|
190
|
+
"name: #{index.name.inspect}",
|
193
191
|
]
|
194
192
|
statement_parts << 'unique: true' if index.unique
|
195
193
|
|
196
194
|
index_lengths = (index.lengths || []).compact
|
197
|
-
statement_parts <<
|
195
|
+
statement_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
|
196
|
+
|
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
|
202
|
+
|
203
|
+
" #{statement_parts.join(', ')}"
|
204
|
+
end
|
205
|
+
|
206
|
+
stream.puts add_index_statements.sort.join("\n")
|
207
|
+
stream.puts
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
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
|
+
]
|
198
218
|
|
199
|
-
|
200
|
-
|
219
|
+
if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
|
220
|
+
parts << "column: #{foreign_key.column.inspect}"
|
221
|
+
end
|
201
222
|
|
202
|
-
|
223
|
+
if foreign_key.custom_primary_key?
|
224
|
+
parts << "primary_key: #{foreign_key.primary_key.inspect}"
|
225
|
+
end
|
203
226
|
|
204
|
-
|
227
|
+
if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
|
228
|
+
parts << "name: #{foreign_key.name.inspect}"
|
229
|
+
end
|
205
230
|
|
206
|
-
|
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
|
207
233
|
|
208
|
-
|
234
|
+
" #{parts.join(', ')}"
|
209
235
|
end
|
210
236
|
|
211
|
-
stream.puts
|
212
|
-
stream.puts
|
237
|
+
stream.puts add_foreign_key_statements.sort.join("\n")
|
213
238
|
end
|
214
239
|
end
|
215
240
|
|
216
241
|
def remove_prefix_and_suffix(table)
|
217
242
|
table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
|
218
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
|
219
250
|
end
|
220
251
|
end
|
@@ -34,15 +34,16 @@ module ActiveRecord
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def drop_table
|
37
|
-
if table_exists?
|
38
|
-
connection.remove_index table_name, name: index_name
|
39
|
-
connection.drop_table(table_name)
|
40
|
-
end
|
37
|
+
connection.drop_table table_name if table_exists?
|
41
38
|
end
|
42
39
|
|
43
40
|
def normalize_migration_number(number)
|
44
41
|
"%.3d" % number.to_i
|
45
42
|
end
|
43
|
+
|
44
|
+
def normalized_versions
|
45
|
+
pluck(:version).map { |v| normalize_migration_number v }
|
46
|
+
end
|
46
47
|
end
|
47
48
|
|
48
49
|
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"
|
@@ -94,6 +95,7 @@ module ActiveRecord
|
|
94
95
|
end
|
95
96
|
|
96
97
|
def build_default_scope(base_rel = relation) # :nodoc:
|
98
|
+
return if abstract_class?
|
97
99
|
if !Base.is_a?(method(:default_scope).owner)
|
98
100
|
# The user has defined their own default scope method, so call that
|
99
101
|
evaluate_default_scope { default_scope }
|
@@ -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
@@ -99,7 +99,7 @@ module ActiveRecord
|
|
99
99
|
self.local_stored_attributes[store_attribute] |= keys
|
100
100
|
end
|
101
101
|
|
102
|
-
def _store_accessors_module
|
102
|
+
def _store_accessors_module # :nodoc:
|
103
103
|
@_store_accessors_module ||= begin
|
104
104
|
mod = Module.new
|
105
105
|
include mod
|
@@ -129,10 +129,10 @@ module ActiveRecord
|
|
129
129
|
|
130
130
|
private
|
131
131
|
def store_accessor_for(store_attribute)
|
132
|
-
|
132
|
+
type_for_attribute(store_attribute.to_s).accessor
|
133
133
|
end
|
134
134
|
|
135
|
-
class HashAccessor
|
135
|
+
class HashAccessor # :nodoc:
|
136
136
|
def self.read(object, attribute, key)
|
137
137
|
prepare(object, attribute)
|
138
138
|
object.public_send(attribute)[key]
|
@@ -151,7 +151,7 @@ module ActiveRecord
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
|
-
class StringKeyedHashAccessor < HashAccessor
|
154
|
+
class StringKeyedHashAccessor < HashAccessor # :nodoc:
|
155
155
|
def self.read(object, attribute, key)
|
156
156
|
super object, attribute, key.to_s
|
157
157
|
end
|
@@ -161,7 +161,7 @@ module ActiveRecord
|
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
-
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor
|
164
|
+
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
|
165
165
|
def self.prepare(object, store_attribute)
|
166
166
|
attribute = object.send(store_attribute)
|
167
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,21 @@ module ActiveRecord
|
|
122
129
|
}
|
123
130
|
end
|
124
131
|
|
132
|
+
def migrate
|
133
|
+
raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
|
134
|
+
|
135
|
+
verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
136
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
137
|
+
scope = ENV['SCOPE']
|
138
|
+
verbose_was, Migration.verbose = Migration.verbose, verbose
|
139
|
+
Migrator.migrate(migrations_paths, version) do |migration|
|
140
|
+
scope.blank? || scope == migration.scope
|
141
|
+
end
|
142
|
+
ActiveRecord::Base.clear_cache!
|
143
|
+
ensure
|
144
|
+
Migration.verbose = verbose_was
|
145
|
+
end
|
146
|
+
|
125
147
|
def charset_current(environment = env)
|
126
148
|
charset ActiveRecord::Base.configurations[environment]
|
127
149
|
end
|
@@ -144,6 +166,19 @@ module ActiveRecord
|
|
144
166
|
class_for_adapter(configuration['adapter']).new(configuration).purge
|
145
167
|
end
|
146
168
|
|
169
|
+
def purge_all
|
170
|
+
each_local_configuration { |configuration|
|
171
|
+
purge configuration
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
def purge_current(environment = env)
|
176
|
+
each_current_configuration(environment) { |configuration|
|
177
|
+
purge configuration
|
178
|
+
}
|
179
|
+
ActiveRecord::Base.establish_connection(environment.to_sym)
|
180
|
+
end
|
181
|
+
|
147
182
|
def structure_dump(*arguments)
|
148
183
|
configuration = arguments.first
|
149
184
|
filename = arguments.delete_at 1
|
@@ -157,20 +192,34 @@ module ActiveRecord
|
|
157
192
|
end
|
158
193
|
|
159
194
|
def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
|
195
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
196
|
+
This method will act on a specific connection in the future.
|
197
|
+
To act on the current connection, use `load_schema_current` instead.
|
198
|
+
MSG
|
199
|
+
|
160
200
|
load_schema_current(format, file)
|
161
201
|
end
|
162
202
|
|
203
|
+
def schema_file(format = ActiveRecord::Base.schema_format)
|
204
|
+
case format
|
205
|
+
when :ruby
|
206
|
+
File.join(db_dir, "schema.rb")
|
207
|
+
when :sql
|
208
|
+
File.join(db_dir, "structure.sql")
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
163
212
|
# This method is the successor of +load_schema+. We should rename it
|
164
213
|
# after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
|
165
214
|
def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
|
215
|
+
file ||= schema_file(format)
|
216
|
+
|
166
217
|
case format
|
167
218
|
when :ruby
|
168
|
-
file ||= File.join(db_dir, "schema.rb")
|
169
219
|
check_schema_file(file)
|
170
220
|
ActiveRecord::Base.establish_connection(configuration)
|
171
221
|
load(file)
|
172
222
|
when :sql
|
173
|
-
file ||= File.join(db_dir, "structure.sql")
|
174
223
|
check_schema_file(file)
|
175
224
|
structure_load(configuration, file)
|
176
225
|
else
|
@@ -178,6 +227,12 @@ module ActiveRecord
|
|
178
227
|
end
|
179
228
|
end
|
180
229
|
|
230
|
+
def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
231
|
+
if File.exist?(file || schema_file(format))
|
232
|
+
load_schema_current(format, file, environment)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
181
236
|
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
182
237
|
each_current_configuration(environment) { |configuration|
|
183
238
|
load_schema_for configuration, format, file
|