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.

Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +776 -1330
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/association_relation.rb +4 -0
  6. data/lib/active_record/associations/alias_tracker.rb +14 -13
  7. data/lib/active_record/associations/association.rb +2 -2
  8. data/lib/active_record/associations/association_scope.rb +83 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
  13. data/lib/active_record/associations/builder/has_many.rb +1 -1
  14. data/lib/active_record/associations/builder/has_one.rb +2 -2
  15. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  16. data/lib/active_record/associations/collection_association.rb +66 -29
  17. data/lib/active_record/associations/collection_proxy.rb +22 -26
  18. data/lib/active_record/associations/has_many_association.rb +65 -18
  19. data/lib/active_record/associations/has_many_through_association.rb +55 -27
  20. data/lib/active_record/associations/has_one_association.rb +0 -1
  21. data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
  22. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  23. data/lib/active_record/associations/join_dependency.rb +20 -12
  24. data/lib/active_record/associations/preloader/association.rb +34 -11
  25. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  26. data/lib/active_record/associations/preloader.rb +49 -59
  27. data/lib/active_record/associations/singular_association.rb +25 -4
  28. data/lib/active_record/associations/through_association.rb +23 -14
  29. data/lib/active_record/associations.rb +171 -42
  30. data/lib/active_record/attribute.rb +149 -0
  31. data/lib/active_record/attribute_assignment.rb +18 -10
  32. data/lib/active_record/attribute_decorators.rb +66 -0
  33. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  34. data/lib/active_record/attribute_methods/dirty.rb +98 -44
  35. data/lib/active_record/attribute_methods/primary_key.rb +14 -8
  36. data/lib/active_record/attribute_methods/query.rb +1 -1
  37. data/lib/active_record/attribute_methods/read.rb +22 -59
  38. data/lib/active_record/attribute_methods/serialization.rb +37 -147
  39. data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
  40. data/lib/active_record/attribute_methods/write.rb +14 -21
  41. data/lib/active_record/attribute_methods.rb +67 -94
  42. data/lib/active_record/attribute_set/builder.rb +86 -0
  43. data/lib/active_record/attribute_set.rb +77 -0
  44. data/lib/active_record/attributes.rb +139 -0
  45. data/lib/active_record/autosave_association.rb +45 -38
  46. data/lib/active_record/base.rb +10 -20
  47. data/lib/active_record/callbacks.rb +7 -7
  48. data/lib/active_record/coders/json.rb +13 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
  60. data/lib/active_record/connection_adapters/column.rb +28 -239
  61. data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
  62. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
  63. data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
  64. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  65. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  66. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
  67. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  96. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
  97. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  98. data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
  99. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  100. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
  101. data/lib/active_record/connection_handling.rb +3 -3
  102. data/lib/active_record/core.rb +143 -32
  103. data/lib/active_record/counter_cache.rb +60 -7
  104. data/lib/active_record/enum.rb +10 -11
  105. data/lib/active_record/errors.rb +49 -27
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixtures.rb +56 -70
  108. data/lib/active_record/gem_version.rb +2 -2
  109. data/lib/active_record/inheritance.rb +35 -10
  110. data/lib/active_record/integration.rb +4 -4
  111. data/lib/active_record/locking/optimistic.rb +35 -17
  112. data/lib/active_record/log_subscriber.rb +1 -1
  113. data/lib/active_record/migration/command_recorder.rb +19 -2
  114. data/lib/active_record/migration/join_table.rb +1 -1
  115. data/lib/active_record/migration.rb +52 -49
  116. data/lib/active_record/model_schema.rb +49 -57
  117. data/lib/active_record/nested_attributes.rb +7 -7
  118. data/lib/active_record/null_relation.rb +19 -5
  119. data/lib/active_record/persistence.rb +50 -31
  120. data/lib/active_record/query_cache.rb +3 -3
  121. data/lib/active_record/querying.rb +10 -7
  122. data/lib/active_record/railtie.rb +14 -11
  123. data/lib/active_record/railties/databases.rake +56 -54
  124. data/lib/active_record/readonly_attributes.rb +0 -1
  125. data/lib/active_record/reflection.rb +286 -102
  126. data/lib/active_record/relation/batches.rb +0 -1
  127. data/lib/active_record/relation/calculations.rb +39 -31
  128. data/lib/active_record/relation/delegation.rb +2 -2
  129. data/lib/active_record/relation/finder_methods.rb +80 -36
  130. data/lib/active_record/relation/merger.rb +25 -30
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
  132. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  133. data/lib/active_record/relation/predicate_builder.rb +11 -10
  134. data/lib/active_record/relation/query_methods.rb +141 -55
  135. data/lib/active_record/relation/spawn_methods.rb +3 -0
  136. data/lib/active_record/relation.rb +69 -30
  137. data/lib/active_record/result.rb +18 -7
  138. data/lib/active_record/sanitization.rb +12 -2
  139. data/lib/active_record/schema.rb +0 -1
  140. data/lib/active_record/schema_dumper.rb +58 -26
  141. data/lib/active_record/schema_migration.rb +11 -0
  142. data/lib/active_record/scoping/default.rb +8 -7
  143. data/lib/active_record/scoping/named.rb +4 -0
  144. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  145. data/lib/active_record/statement_cache.rb +95 -10
  146. data/lib/active_record/store.rb +19 -10
  147. data/lib/active_record/tasks/database_tasks.rb +73 -7
  148. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
  149. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  151. data/lib/active_record/timestamp.rb +11 -9
  152. data/lib/active_record/transactions.rb +37 -21
  153. data/lib/active_record/type/big_integer.rb +13 -0
  154. data/lib/active_record/type/binary.rb +50 -0
  155. data/lib/active_record/type/boolean.rb +30 -0
  156. data/lib/active_record/type/date.rb +46 -0
  157. data/lib/active_record/type/date_time.rb +43 -0
  158. data/lib/active_record/type/decimal.rb +40 -0
  159. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  160. data/lib/active_record/type/decorator.rb +14 -0
  161. data/lib/active_record/type/float.rb +19 -0
  162. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  163. data/lib/active_record/type/integer.rb +55 -0
  164. data/lib/active_record/type/mutable.rb +16 -0
  165. data/lib/active_record/type/numeric.rb +36 -0
  166. data/lib/active_record/type/serialized.rb +56 -0
  167. data/lib/active_record/type/string.rb +36 -0
  168. data/lib/active_record/type/text.rb +11 -0
  169. data/lib/active_record/type/time.rb +26 -0
  170. data/lib/active_record/type/time_value.rb +38 -0
  171. data/lib/active_record/type/type_map.rb +64 -0
  172. data/lib/active_record/type/unsigned_integer.rb +15 -0
  173. data/lib/active_record/type/value.rb +101 -0
  174. data/lib/active_record/type.rb +23 -0
  175. data/lib/active_record/validations/associated.rb +5 -3
  176. data/lib/active_record/validations/presence.rb +6 -4
  177. data/lib/active_record/validations/uniqueness.rb +11 -17
  178. data/lib/active_record/validations.rb +25 -19
  179. data/lib/active_record.rb +3 -0
  180. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  181. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
  182. data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +65 -10
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,4 +1,3 @@
1
-
2
1
  module ActiveRecord
3
2
  module ConnectionAdapters
4
3
  class SchemaCache
@@ -12,15 +11,15 @@ module ActiveRecord
12
11
  @columns_hash = {}
13
12
  @primary_keys = {}
14
13
  @tables = {}
15
- prepare_default_proc
16
14
  end
17
15
 
18
16
  def primary_keys(table_name)
19
- @primary_keys[table_name]
17
+ @primary_keys[table_name] ||= table_exists?(table_name) ? connection.primary_key(table_name) : nil
20
18
  end
21
19
 
22
20
  # A cached lookup for table existence.
23
21
  def table_exists?(name)
22
+ prepare_tables if @tables.empty?
24
23
  return @tables[name] if @tables.key? name
25
24
 
26
25
  @tables[name] = connection.table_exists?(name)
@@ -29,9 +28,9 @@ module ActiveRecord
29
28
  # Add internal cache for table with +table_name+.
30
29
  def add(table_name)
31
30
  if table_exists?(table_name)
32
- @primary_keys[table_name]
33
- @columns[table_name]
34
- @columns_hash[table_name]
31
+ primary_keys(table_name)
32
+ columns(table_name)
33
+ columns_hash(table_name)
35
34
  end
36
35
  end
37
36
 
@@ -40,14 +39,16 @@ module ActiveRecord
40
39
  end
41
40
 
42
41
  # Get the columns for a table
43
- def columns(table)
44
- @columns[table]
42
+ def columns(table_name)
43
+ @columns[table_name] ||= connection.columns(table_name)
45
44
  end
46
45
 
47
46
  # Get the columns for a table as a hash, key is the column name
48
47
  # value is the column object.
49
- def columns_hash(table)
50
- @columns_hash[table]
48
+ def columns_hash(table_name)
49
+ @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
50
+ [col.name, col]
51
+ }]
51
52
  end
52
53
 
53
54
  # Clears out internal caches
@@ -76,33 +77,18 @@ module ActiveRecord
76
77
  def marshal_dump
77
78
  # if we get current version during initialization, it happens stack over flow.
78
79
  @version = ActiveRecord::Migrator.current_version
79
- [@version] + [@columns, @columns_hash, @primary_keys, @tables].map { |val|
80
- Hash[val]
81
- }
80
+ [@version, @columns, @columns_hash, @primary_keys, @tables]
82
81
  end
83
82
 
84
83
  def marshal_load(array)
85
84
  @version, @columns, @columns_hash, @primary_keys, @tables = array
86
- prepare_default_proc
87
85
  end
88
86
 
89
87
  private
90
88
 
91
- def prepare_default_proc
92
- @columns.default_proc = Proc.new do |h, table_name|
93
- h[table_name] = connection.columns(table_name)
94
- end
95
-
96
- @columns_hash.default_proc = Proc.new do |h, table_name|
97
- h[table_name] = Hash[columns(table_name).map { |col|
98
- [col.name, col]
99
- }]
89
+ def prepare_tables
90
+ connection.tables.each { |table| @tables[table] = true }
100
91
  end
101
-
102
- @primary_keys.default_proc = Proc.new do |h, table_name|
103
- h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil
104
- end
105
- end
106
92
  end
107
93
  end
108
94
  end
@@ -14,9 +14,9 @@ module ActiveRecord
14
14
  raise ArgumentError, "No database file specified. Missing argument: database"
15
15
  end
16
16
 
17
- # Allow database path relative to Rails.root, but only if
18
- # the database path is not the special path that tells
19
- # Sqlite to build a database only in memory.
17
+ # Allow database path relative to Rails.root, but only if the database
18
+ # path is not the special path that tells sqlite to build a database only
19
+ # in memory.
20
20
  if ':memory:' != config[:database]
21
21
  config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
22
22
  dirname = File.dirname(config[:database])
@@ -30,24 +30,32 @@ module ActiveRecord
30
30
 
31
31
  db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
32
32
 
33
- ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
33
+ ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
34
34
  rescue Errno::ENOENT => error
35
35
  if error.message.include?("No such file or directory")
36
- raise ActiveRecord::NoDatabaseError.new(error.message)
36
+ raise ActiveRecord::NoDatabaseError.new(error.message, error)
37
37
  else
38
- raise error
38
+ raise
39
39
  end
40
40
  end
41
41
  end
42
42
 
43
43
  module ConnectionAdapters #:nodoc:
44
- class SQLite3Column < Column #:nodoc:
45
- class << self
46
- def binary_to_string(value)
47
- if value.encoding != Encoding::ASCII_8BIT
48
- value = value.force_encoding(Encoding::ASCII_8BIT)
49
- end
50
- value
44
+ class SQLite3Binary < Type::Binary # :nodoc:
45
+ def cast_value(value)
46
+ if value.encoding != Encoding::ASCII_8BIT
47
+ value = value.force_encoding(Encoding::ASCII_8BIT)
48
+ end
49
+ value
50
+ end
51
+ end
52
+
53
+ class SQLite3String < Type::String # :nodoc:
54
+ def type_cast_for_database(value)
55
+ if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT
56
+ value.encode(Encoding::UTF_8)
57
+ else
58
+ super
51
59
  end
52
60
  end
53
61
  end
@@ -59,17 +67,17 @@ module ActiveRecord
59
67
  #
60
68
  # * <tt>:database</tt> - Path to the database file.
61
69
  class SQLite3Adapter < AbstractAdapter
70
+ ADAPTER_NAME = 'SQLite'.freeze
62
71
  include Savepoints
63
72
 
64
73
  NATIVE_DATABASE_TYPES = {
65
74
  primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
66
- string: { name: "varchar", limit: 255 },
75
+ string: { name: "varchar" },
67
76
  text: { name: "text" },
68
77
  integer: { name: "integer" },
69
78
  float: { name: "float" },
70
79
  decimal: { name: "decimal" },
71
80
  datetime: { name: "datetime" },
72
- timestamp: { name: "datetime" },
73
81
  time: { name: "time" },
74
82
  date: { name: "date" },
75
83
  binary: { name: "blob" },
@@ -107,7 +115,7 @@ module ActiveRecord
107
115
  end
108
116
 
109
117
  def clear
110
- cache.values.each do |hash|
118
+ cache.each_value do |hash|
111
119
  dealloc hash[:stmt]
112
120
  end
113
121
  cache.clear
@@ -123,11 +131,7 @@ module ActiveRecord
123
131
  end
124
132
  end
125
133
 
126
- class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
127
- include Arel::Visitors::BindVisitor
128
- end
129
-
130
- def initialize(connection, logger, config)
134
+ def initialize(connection, logger, connection_options, config)
131
135
  super(connection, logger)
132
136
 
133
137
  @active = nil
@@ -135,18 +139,15 @@ module ActiveRecord
135
139
  self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
136
140
  @config = config
137
141
 
142
+ @visitor = Arel::Visitors::SQLite.new self
143
+
138
144
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
139
145
  @prepared_statements = true
140
- @visitor = Arel::Visitors::SQLite.new self
141
146
  else
142
- @visitor = unprepared_visitor
147
+ @prepared_statements = false
143
148
  end
144
149
  end
145
150
 
146
- def adapter_name #:nodoc:
147
- 'SQLite'
148
- end
149
-
150
151
  def supports_ddl_transactions?
151
152
  true
152
153
  end
@@ -178,7 +179,7 @@ module ActiveRecord
178
179
  true
179
180
  end
180
181
 
181
- def supports_add_column?
182
+ def supports_views?
182
183
  true
183
184
  end
184
185
 
@@ -225,10 +226,19 @@ module ActiveRecord
225
226
 
226
227
  # QUOTING ==================================================
227
228
 
228
- def quote(value, column = nil)
229
- if value.kind_of?(String) && column && column.type == :binary
230
- s = value.unpack("H*")[0]
231
- "x'#{s}'"
229
+ def _quote(value) # :nodoc:
230
+ case value
231
+ when Type::Binary::Data
232
+ "x'#{value.hex}'"
233
+ else
234
+ super
235
+ end
236
+ end
237
+
238
+ def _type_cast(value) # :nodoc:
239
+ case value
240
+ when BigDecimal
241
+ value.to_f
232
242
  else
233
243
  super
234
244
  end
@@ -256,24 +266,13 @@ module ActiveRecord
256
266
  end
257
267
  end
258
268
 
259
- def type_cast(value, column) # :nodoc:
260
- return value.to_f if BigDecimal === value
261
- return super unless String === value
262
- return super unless column && value
263
-
264
- value = super
265
- if column.type == :string && value.encoding == Encoding::ASCII_8BIT
266
- logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
267
- value = value.encode Encoding::UTF_8
268
- end
269
- value
270
- end
271
-
269
+ #--
272
270
  # DATABASE STATEMENTS ======================================
271
+ #++
273
272
 
274
273
  def explain(arel, binds = [])
275
274
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
276
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
275
+ ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
277
276
  end
278
277
 
279
278
  class ExplainPrettyPrinter
@@ -299,9 +298,12 @@ module ActiveRecord
299
298
  # Don't cache statements if they are not prepared
300
299
  if without_prepared_statement?(binds)
301
300
  stmt = @connection.prepare(sql)
302
- cols = stmt.columns
303
- records = stmt.to_a
304
- stmt.close
301
+ begin
302
+ cols = stmt.columns
303
+ records = stmt.to_a
304
+ ensure
305
+ stmt.close
306
+ end
305
307
  stmt = records
306
308
  else
307
309
  cache = @statements[sql] ||= {
@@ -369,7 +371,7 @@ module ActiveRecord
369
371
  sql = <<-SQL
370
372
  SELECT name
371
373
  FROM sqlite_master
372
- WHERE type = 'table' AND NOT name = 'sqlite_sequence'
374
+ WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
373
375
  SQL
374
376
  sql << " AND name = #{quote_table_name(table_name)}" if table_name
375
377
 
@@ -382,7 +384,7 @@ module ActiveRecord
382
384
  table_name && tables(nil, table_name).any?
383
385
  end
384
386
 
385
- # Returns an array of +SQLite3Column+ objects for the table specified by +table_name+.
387
+ # Returns an array of +Column+ objects for the table specified by +table_name+.
386
388
  def columns(table_name) #:nodoc:
387
389
  table_structure(table_name).map do |field|
388
390
  case field["dflt_value"]
@@ -394,7 +396,9 @@ module ActiveRecord
394
396
  field["dflt_value"] = $1.gsub('""', '"')
395
397
  end
396
398
 
397
- SQLite3Column.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
399
+ sql_type = field['type']
400
+ cast_type = lookup_cast_type(sql_type)
401
+ new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
398
402
  end
399
403
  end
400
404
 
@@ -445,12 +449,12 @@ module ActiveRecord
445
449
 
446
450
  # See: http://www.sqlite.org/lang_altertable.html
447
451
  # SQLite has an additional restriction on the ALTER TABLE statement
448
- def valid_alter_table_options( type, options)
452
+ def valid_alter_table_type?(type)
449
453
  type.to_sym != :primary_key
450
454
  end
451
455
 
452
456
  def add_column(table_name, column_name, type, options = {}) #:nodoc:
453
- if supports_add_column? && valid_alter_table_options( type, options )
457
+ if valid_alter_table_type?(type)
454
458
  super(table_name, column_name, type, options)
455
459
  else
456
460
  alter_table(table_name) do |definition|
@@ -495,16 +499,17 @@ module ActiveRecord
495
499
  end
496
500
 
497
501
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
498
- unless columns(table_name).detect{|c| c.name == column_name.to_s }
499
- raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
500
- end
501
- alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
502
- rename_column_indexes(table_name, column_name, new_column_name)
502
+ column = column_for(table_name, column_name)
503
+ alter_table(table_name, rename: {column.name => new_column_name.to_s})
504
+ rename_column_indexes(table_name, column.name, new_column_name)
503
505
  end
504
506
 
505
507
  protected
506
- def select(sql, name = nil, binds = []) #:nodoc:
507
- exec_query(sql, name, binds)
508
+
509
+ def initialize_type_map(m)
510
+ super
511
+ m.register_type(/binary/i, SQLite3Binary.new)
512
+ register_class_with_limit m, %r(char)i, SQLite3String
508
513
  end
509
514
 
510
515
  def table_structure(table_name)
@@ -1,6 +1,6 @@
1
1
  module ActiveRecord
2
2
  module ConnectionHandling
3
- RAILS_ENV = -> { Rails.env if defined?(Rails) }
3
+ RAILS_ENV = -> { (Rails.env if defined?(Rails)) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] }
4
4
  DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
5
5
 
6
6
  # Establishes the connection to the database. Accepts a hash as input where
@@ -18,14 +18,14 @@ module ActiveRecord
18
18
  # Example for SQLite database:
19
19
  #
20
20
  # ActiveRecord::Base.establish_connection(
21
- # adapter: "sqlite",
21
+ # adapter: "sqlite3",
22
22
  # database: "path/to/dbfile"
23
23
  # )
24
24
  #
25
25
  # Also accepts keys as strings (for parsing from YAML for example):
26
26
  #
27
27
  # ActiveRecord::Base.establish_connection(
28
- # "adapter" => "sqlite",
28
+ # "adapter" => "sqlite3",
29
29
  # "database" => "path/to/dbfile"
30
30
  # )
31
31
  #
@@ -1,6 +1,7 @@
1
+ require 'thread'
1
2
  require 'active_support/core_ext/hash/indifferent_access'
2
3
  require 'active_support/core_ext/object/duplicable'
3
- require 'thread'
4
+ require 'active_support/core_ext/string/filters'
4
5
 
5
6
  module ActiveRecord
6
7
  module Core
@@ -16,7 +17,6 @@ module ActiveRecord
16
17
  mattr_accessor :logger, instance_writer: false
17
18
 
18
19
  ##
19
- # :singleton-method:
20
20
  # Contains the database configuration - as is typically stored in config/database.yml -
21
21
  # as a Hash.
22
22
  #
@@ -85,15 +85,17 @@ module ActiveRecord
85
85
  mattr_accessor :dump_schema_after_migration, instance_writer: false
86
86
  self.dump_schema_after_migration = true
87
87
 
88
- # :nodoc:
89
88
  mattr_accessor :maintain_test_schema, instance_accessor: false
90
89
 
91
90
  def self.disable_implicit_join_references=(value)
92
- ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \
93
- "Make sure to remove this configuration because it does nothing.")
91
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
92
+ Implicit join references were removed with Rails 4.1.
93
+ Make sure to remove this configuration because it does nothing.
94
+ MSG
94
95
  end
95
96
 
96
97
  class_attribute :default_connection_handler, instance_writer: false
98
+ class_attribute :find_by_statement_cache
97
99
 
98
100
  def self.connection_handler
99
101
  ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
@@ -107,9 +109,93 @@ module ActiveRecord
107
109
  end
108
110
 
109
111
  module ClassMethods
110
- def initialize_generated_modules
112
+ def allocate
113
+ define_attribute_methods
114
+ super
115
+ end
116
+
117
+ def initialize_find_by_cache
118
+ self.find_by_statement_cache = {}.extend(Mutex_m)
119
+ end
120
+
121
+ def inherited(child_class)
122
+ child_class.initialize_find_by_cache
111
123
  super
124
+ end
125
+
126
+ def find(*ids)
127
+ # We don't have cache keys for this stuff yet
128
+ return super unless ids.length == 1
129
+ # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
130
+ return super if ids.first.kind_of?(Symbol)
131
+ return super if block_given? ||
132
+ primary_key.nil? ||
133
+ default_scopes.any? ||
134
+ columns_hash.include?(inheritance_column) ||
135
+ ids.first.kind_of?(Array)
136
+
137
+ id = ids.first
138
+ if ActiveRecord::Base === id
139
+ id = id.id
140
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
141
+ You are passing an instance of ActiveRecord::Base to `find`.
142
+ Please pass the id of the object by calling `.id`
143
+ MSG
144
+ end
145
+ key = primary_key
146
+
147
+ s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
148
+ find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
149
+ where(key => params.bind).limit(1)
150
+ }
151
+ }
152
+ record = s.execute([id], self, connection).first
153
+ unless record
154
+ raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
155
+ end
156
+ record
157
+ rescue RangeError
158
+ raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
159
+ end
160
+
161
+ def find_by(*args)
162
+ return super if current_scope || !(Hash === args.first) || reflect_on_all_aggregations.any?
163
+ return super if default_scopes.any?
164
+
165
+ hash = args.first
166
+
167
+ return super if hash.values.any? { |v|
168
+ v.nil? || Array === v || Hash === v
169
+ }
170
+
171
+ # We can't cache Post.find_by(author: david) ...yet
172
+ return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
173
+
174
+ key = hash.keys
175
+
176
+ klass = self
177
+ s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
178
+ find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
179
+ wheres = key.each_with_object({}) { |param,o|
180
+ o[param] = params.bind
181
+ }
182
+ klass.where(wheres).limit(1)
183
+ }
184
+ }
185
+ begin
186
+ s.execute(hash.values, self, connection).first
187
+ rescue TypeError => e
188
+ raise ActiveRecord::StatementInvalid.new(e.message, e)
189
+ rescue RangeError
190
+ nil
191
+ end
192
+ end
193
+
194
+ def find_by!(*args)
195
+ find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}")
196
+ end
112
197
 
198
+ def initialize_generated_modules
113
199
  generated_association_methods
114
200
  end
115
201
 
@@ -183,22 +269,18 @@ module ActiveRecord
183
269
  # # Instantiates a single new object
184
270
  # User.new(first_name: 'Jamie')
185
271
  def initialize(attributes = nil, options = {})
186
- defaults = self.class.column_defaults.dup
187
- defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
188
-
189
- @attributes = self.class.initialize_attributes(defaults)
190
- @column_types_override = nil
191
- @column_types = self.class.column_types
272
+ @attributes = self.class._default_attributes.dup
192
273
 
193
274
  init_internals
194
275
  initialize_internals_callback
195
276
 
277
+ self.class.define_attribute_methods
196
278
  # +options+ argument is only needed to make protected_attributes gem easier to hook.
197
279
  # Remove it when we drop support to this gem.
198
280
  init_attributes(attributes, options) if attributes
199
281
 
200
282
  yield self if block_given?
201
- run_callbacks :initialize unless _initialize_callbacks.empty?
283
+ _run_initialize_callbacks
202
284
  end
203
285
 
204
286
  # Initialize an empty model object from +coder+. +coder+ must contain
@@ -212,16 +294,16 @@ module ActiveRecord
212
294
  # post.init_with('attributes' => { 'title' => 'hello world' })
213
295
  # post.title # => 'hello world'
214
296
  def init_with(coder)
215
- @attributes = self.class.initialize_attributes(coder['attributes'])
216
- @column_types_override = coder['column_types']
217
- @column_types = self.class.column_types
297
+ @attributes = coder['attributes']
218
298
 
219
299
  init_internals
220
300
 
221
- @new_record = false
301
+ @new_record = coder['new_record']
222
302
 
223
- run_callbacks :find
224
- run_callbacks :initialize
303
+ self.class.define_attribute_methods
304
+
305
+ _run_find_callbacks
306
+ _run_initialize_callbacks
225
307
 
226
308
  self
227
309
  end
@@ -254,19 +336,16 @@ module ActiveRecord
254
336
 
255
337
  ##
256
338
  def initialize_dup(other) # :nodoc:
257
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
258
- self.class.initialize_attributes(cloned_attributes, :serialized => false)
259
-
260
- @attributes = cloned_attributes
261
- @attributes[self.class.primary_key] = nil
339
+ @attributes = @attributes.dup
340
+ @attributes.reset(self.class.primary_key)
262
341
 
263
- run_callbacks(:initialize) unless _initialize_callbacks.empty?
342
+ _run_initialize_callbacks
264
343
 
265
344
  @aggregation_cache = {}
266
345
  @association_cache = {}
267
- @attributes_cache = {}
268
346
 
269
347
  @new_record = true
348
+ @destroyed = false
270
349
 
271
350
  super
272
351
  end
@@ -284,7 +363,10 @@ module ActiveRecord
284
363
  # Post.new.encode_with(coder)
285
364
  # coder # => {"attributes" => {"id" => nil, ... }}
286
365
  def encode_with(coder)
287
- coder['attributes'] = attributes_for_coder
366
+ # FIXME: Remove this when we better serialize attributes
367
+ coder['raw_attributes'] = attributes_before_type_cast
368
+ coder['attributes'] = @attributes
369
+ coder['new_record'] = new_record?
288
370
  end
289
371
 
290
372
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -307,7 +389,11 @@ module ActiveRecord
307
389
  # Delegates to id in order to allow two records of the same type and id to work with something like:
308
390
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
309
391
  def hash
310
- id.hash
392
+ if id
393
+ id.hash
394
+ else
395
+ super
396
+ end
311
397
  end
312
398
 
313
399
  # Clone and freeze the attributes hash such that associations are still
@@ -363,6 +449,29 @@ module ActiveRecord
363
449
  "#<#{self.class} #{inspection}>"
364
450
  end
365
451
 
452
+ # Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record`
453
+ # when pp is required.
454
+ def pretty_print(pp)
455
+ pp.object_address_group(self) do
456
+ if defined?(@attributes) && @attributes
457
+ column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
458
+ pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
459
+ column_value = read_attribute(column_name)
460
+ pp.breakable ' '
461
+ pp.group(1) do
462
+ pp.text column_name
463
+ pp.text ':'
464
+ pp.breakable
465
+ pp.pp column_value
466
+ end
467
+ end
468
+ else
469
+ pp.breakable ' '
470
+ pp.text 'not initialized'
471
+ end
472
+ end
473
+ end
474
+
366
475
  # Returns a hash of the given methods with their names as keys and returned values as values.
367
476
  def slice(*methods)
368
477
  Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
@@ -426,12 +535,8 @@ module ActiveRecord
426
535
  end
427
536
 
428
537
  def init_internals
429
- pk = self.class.primary_key
430
- @attributes[pk] = nil unless @attributes.key?(pk)
431
-
432
538
  @aggregation_cache = {}
433
539
  @association_cache = {}
434
- @attributes_cache = {}
435
540
  @readonly = false
436
541
  @destroyed = false
437
542
  @marked_for_destruction = false
@@ -451,5 +556,11 @@ module ActiveRecord
451
556
  def init_attributes(attributes, options)
452
557
  assign_attributes(attributes)
453
558
  end
559
+
560
+ def thaw
561
+ if frozen?
562
+ @attributes = @attributes.dup
563
+ end
564
+ end
454
565
  end
455
566
  end