activerecord 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +35 -44
  5. data/examples/performance.rb +110 -100
  6. data/lib/active_record/aggregations.rb +59 -75
  7. data/lib/active_record/associations/alias_tracker.rb +76 -0
  8. data/lib/active_record/associations/association.rb +248 -0
  9. data/lib/active_record/associations/association_scope.rb +135 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +60 -59
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
  12. data/lib/active_record/associations/builder/association.rb +108 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  16. data/lib/active_record/associations/builder/has_many.rb +15 -0
  17. data/lib/active_record/associations/builder/has_one.rb +25 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +608 -0
  20. data/lib/active_record/associations/collection_proxy.rb +986 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
  22. data/lib/active_record/associations/has_many_association.rb +83 -76
  23. data/lib/active_record/associations/has_many_through_association.rb +147 -66
  24. data/lib/active_record/associations/has_one_association.rb +67 -108
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  27. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  28. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  29. data/lib/active_record/associations/join_dependency.rb +235 -0
  30. data/lib/active_record/associations/join_helper.rb +45 -0
  31. data/lib/active_record/associations/preloader/association.rb +121 -0
  32. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  33. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  35. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  36. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  37. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  38. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  39. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  40. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  41. data/lib/active_record/associations/preloader.rb +178 -0
  42. data/lib/active_record/associations/singular_association.rb +64 -0
  43. data/lib/active_record/associations/through_association.rb +87 -0
  44. data/lib/active_record/associations.rb +512 -1224
  45. data/lib/active_record/attribute_assignment.rb +201 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
  47. data/lib/active_record/attribute_methods/dirty.rb +51 -28
  48. data/lib/active_record/attribute_methods/primary_key.rb +94 -22
  49. data/lib/active_record/attribute_methods/query.rb +5 -4
  50. data/lib/active_record/attribute_methods/read.rb +63 -72
  51. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
  53. data/lib/active_record/attribute_methods/write.rb +39 -13
  54. data/lib/active_record/attribute_methods.rb +362 -29
  55. data/lib/active_record/autosave_association.rb +132 -75
  56. data/lib/active_record/base.rb +83 -1627
  57. data/lib/active_record/callbacks.rb +69 -47
  58. data/lib/active_record/coders/yaml_column.rb +38 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  70. data/lib/active_record/connection_adapters/column.rb +318 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
  74. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  75. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
  82. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  83. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
  84. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  85. data/lib/active_record/connection_handling.rb +98 -0
  86. data/lib/active_record/core.rb +463 -0
  87. data/lib/active_record/counter_cache.rb +108 -101
  88. data/lib/active_record/dynamic_matchers.rb +131 -0
  89. data/lib/active_record/errors.rb +54 -13
  90. data/lib/active_record/explain.rb +38 -0
  91. data/lib/active_record/explain_registry.rb +30 -0
  92. data/lib/active_record/explain_subscriber.rb +29 -0
  93. data/lib/active_record/fixture_set/file.rb +55 -0
  94. data/lib/active_record/fixtures.rb +703 -785
  95. data/lib/active_record/inheritance.rb +200 -0
  96. data/lib/active_record/integration.rb +60 -0
  97. data/lib/active_record/locale/en.yml +8 -1
  98. data/lib/active_record/locking/optimistic.rb +69 -60
  99. data/lib/active_record/locking/pessimistic.rb +34 -12
  100. data/lib/active_record/log_subscriber.rb +40 -6
  101. data/lib/active_record/migration/command_recorder.rb +164 -0
  102. data/lib/active_record/migration/join_table.rb +15 -0
  103. data/lib/active_record/migration.rb +614 -216
  104. data/lib/active_record/model_schema.rb +345 -0
  105. data/lib/active_record/nested_attributes.rb +248 -119
  106. data/lib/active_record/null_relation.rb +65 -0
  107. data/lib/active_record/persistence.rb +275 -57
  108. data/lib/active_record/query_cache.rb +29 -9
  109. data/lib/active_record/querying.rb +62 -0
  110. data/lib/active_record/railtie.rb +135 -21
  111. data/lib/active_record/railties/console_sandbox.rb +5 -0
  112. data/lib/active_record/railties/controller_runtime.rb +17 -5
  113. data/lib/active_record/railties/databases.rake +249 -359
  114. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  115. data/lib/active_record/readonly_attributes.rb +30 -0
  116. data/lib/active_record/reflection.rb +283 -103
  117. data/lib/active_record/relation/batches.rb +38 -34
  118. data/lib/active_record/relation/calculations.rb +252 -139
  119. data/lib/active_record/relation/delegation.rb +125 -0
  120. data/lib/active_record/relation/finder_methods.rb +182 -188
  121. data/lib/active_record/relation/merger.rb +161 -0
  122. data/lib/active_record/relation/predicate_builder.rb +86 -21
  123. data/lib/active_record/relation/query_methods.rb +917 -134
  124. data/lib/active_record/relation/spawn_methods.rb +53 -92
  125. data/lib/active_record/relation.rb +405 -143
  126. data/lib/active_record/result.rb +67 -0
  127. data/lib/active_record/runtime_registry.rb +17 -0
  128. data/lib/active_record/sanitization.rb +168 -0
  129. data/lib/active_record/schema.rb +20 -14
  130. data/lib/active_record/schema_dumper.rb +55 -46
  131. data/lib/active_record/schema_migration.rb +39 -0
  132. data/lib/active_record/scoping/default.rb +146 -0
  133. data/lib/active_record/scoping/named.rb +175 -0
  134. data/lib/active_record/scoping.rb +82 -0
  135. data/lib/active_record/serialization.rb +8 -46
  136. data/lib/active_record/serializers/xml_serializer.rb +21 -68
  137. data/lib/active_record/statement_cache.rb +26 -0
  138. data/lib/active_record/store.rb +156 -0
  139. data/lib/active_record/tasks/database_tasks.rb +203 -0
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  141. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  142. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  143. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  144. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  145. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  146. data/lib/active_record/test_case.rb +57 -28
  147. data/lib/active_record/timestamp.rb +49 -18
  148. data/lib/active_record/transactions.rb +106 -63
  149. data/lib/active_record/translation.rb +22 -0
  150. data/lib/active_record/validations/associated.rb +25 -24
  151. data/lib/active_record/validations/presence.rb +65 -0
  152. data/lib/active_record/validations/uniqueness.rb +123 -83
  153. data/lib/active_record/validations.rb +29 -29
  154. data/lib/active_record/version.rb +7 -5
  155. data/lib/active_record.rb +83 -34
  156. data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
  157. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  158. data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
  159. data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
  160. data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
  161. data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
  162. data/lib/rails/generators/active_record.rb +4 -8
  163. metadata +163 -121
  164. data/CHANGELOG +0 -6023
  165. data/examples/associations.png +0 -0
  166. data/lib/active_record/association_preload.rb +0 -403
  167. data/lib/active_record/associations/association_collection.rb +0 -562
  168. data/lib/active_record/associations/association_proxy.rb +0 -295
  169. data/lib/active_record/associations/through_association_scope.rb +0 -154
  170. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
  171. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
  172. data/lib/active_record/dynamic_finder_match.rb +0 -53
  173. data/lib/active_record/dynamic_scope_match.rb +0 -32
  174. data/lib/active_record/named_scope.rb +0 -138
  175. data/lib/active_record/observer.rb +0 -140
  176. data/lib/active_record/session_store.rb +0 -340
  177. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
  178. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  179. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
  180. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
  181. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -0,0 +1,67 @@
1
+ module ActiveRecord
2
+ ###
3
+ # This class encapsulates a Result returned from calling +exec_query+ on any
4
+ # database connection adapter. For example:
5
+ #
6
+ # x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
7
+ # x # => #<ActiveRecord::Result:0xdeadbeef>
8
+ class Result
9
+ include Enumerable
10
+
11
+ attr_reader :columns, :rows, :column_types
12
+
13
+ def initialize(columns, rows, column_types = {})
14
+ @columns = columns
15
+ @rows = rows
16
+ @hash_rows = nil
17
+ @column_types = column_types
18
+ end
19
+
20
+ def each
21
+ hash_rows.each { |row| yield row }
22
+ end
23
+
24
+ def to_hash
25
+ hash_rows
26
+ end
27
+
28
+ alias :map! :map
29
+ alias :collect! :map
30
+
31
+ # Returns true if there are no records.
32
+ def empty?
33
+ rows.empty?
34
+ end
35
+
36
+ def to_ary
37
+ hash_rows
38
+ end
39
+
40
+ def [](idx)
41
+ hash_rows[idx]
42
+ end
43
+
44
+ def last
45
+ hash_rows.last
46
+ end
47
+
48
+ def initialize_copy(other)
49
+ @columns = columns.dup
50
+ @rows = rows.dup
51
+ @hash_rows = nil
52
+ end
53
+
54
+ private
55
+ def hash_rows
56
+ @hash_rows ||=
57
+ begin
58
+ # We freeze the strings to prevent them getting duped when
59
+ # used as keys in ActiveRecord::Base's @attributes hash
60
+ columns = @columns.map { |c| c.dup.freeze }
61
+ @rows.map { |row|
62
+ Hash[columns.zip(row)]
63
+ }
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,17 @@
1
+ require 'active_support/per_thread_registry'
2
+
3
+ module ActiveRecord
4
+ # This is a thread locals registry for Active Record. For example:
5
+ #
6
+ # ActiveRecord::RuntimeRegistry.connection_handler
7
+ #
8
+ # returns the connection handler local to the current thread.
9
+ #
10
+ # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
11
+ # for further details.
12
+ class RuntimeRegistry # :nodoc:
13
+ extend ActiveSupport::PerThreadRegistry
14
+
15
+ attr_accessor :connection_handler, :sql_runtime, :connection_id
16
+ end
17
+ end
@@ -0,0 +1,168 @@
1
+ module ActiveRecord
2
+ module Sanitization
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def quote_value(value, column = nil) #:nodoc:
7
+ connection.quote(value,column)
8
+ end
9
+
10
+ # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
11
+ def sanitize(object) #:nodoc:
12
+ connection.quote(object)
13
+ end
14
+
15
+ protected
16
+
17
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
18
+ # them into a valid SQL fragment for a WHERE clause.
19
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
20
+ # { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
21
+ # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
22
+ def sanitize_sql_for_conditions(condition, table_name = self.table_name)
23
+ return nil if condition.blank?
24
+
25
+ case condition
26
+ when Array; sanitize_sql_array(condition)
27
+ when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
28
+ else condition
29
+ end
30
+ end
31
+ alias_method :sanitize_sql, :sanitize_sql_for_conditions
32
+
33
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
34
+ # them into a valid SQL fragment for a SET clause.
35
+ # { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
36
+ def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
37
+ case assignments
38
+ when Array; sanitize_sql_array(assignments)
39
+ when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
40
+ else assignments
41
+ end
42
+ end
43
+
44
+ # Accepts a hash of SQL conditions and replaces those attributes
45
+ # that correspond to a +composed_of+ relationship with their expanded
46
+ # aggregate attribute values.
47
+ # Given:
48
+ # class Person < ActiveRecord::Base
49
+ # composed_of :address, class_name: "Address",
50
+ # mapping: [%w(address_street street), %w(address_city city)]
51
+ # end
52
+ # Then:
53
+ # { address: Address.new("813 abc st.", "chicago") }
54
+ # # => { address_street: "813 abc st.", address_city: "chicago" }
55
+ def expand_hash_conditions_for_aggregates(attrs)
56
+ expanded_attrs = {}
57
+ attrs.each do |attr, value|
58
+ if aggregation = reflect_on_aggregation(attr.to_sym)
59
+ mapping = aggregation.mapping
60
+ mapping.each do |field_attr, aggregate_attr|
61
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
62
+ expanded_attrs[field_attr] = value
63
+ else
64
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
65
+ end
66
+ end
67
+ else
68
+ expanded_attrs[attr] = value
69
+ end
70
+ end
71
+ expanded_attrs
72
+ end
73
+
74
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
75
+ # { name: "foo'bar", group_id: 4 }
76
+ # # => "name='foo''bar' and group_id= 4"
77
+ # { status: nil, group_id: [1,2,3] }
78
+ # # => "status IS NULL and group_id IN (1,2,3)"
79
+ # { age: 13..18 }
80
+ # # => "age BETWEEN 13 AND 18"
81
+ # { 'other_records.id' => 7 }
82
+ # # => "`other_records`.`id` = 7"
83
+ # { other_records: { id: 7 } }
84
+ # # => "`other_records`.`id` = 7"
85
+ # And for value objects on a composed_of relationship:
86
+ # { address: Address.new("123 abc st.", "chicago") }
87
+ # # => "address_street='123 abc st.' and address_city='chicago'"
88
+ def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
89
+ attrs = expand_hash_conditions_for_aggregates(attrs)
90
+
91
+ table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
92
+ PredicateBuilder.build_from_hash(self.class, attrs, table).map { |b|
93
+ connection.visitor.accept b
94
+ }.join(' AND ')
95
+ end
96
+ alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
97
+
98
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
99
+ # { status: nil, group_id: 1 }
100
+ # # => "status = NULL , group_id = 1"
101
+ def sanitize_sql_hash_for_assignment(attrs, table)
102
+ attrs.map do |attr, value|
103
+ "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}"
104
+ end.join(', ')
105
+ end
106
+
107
+ # Accepts an array of conditions. The array has each value
108
+ # sanitized and interpolated into the SQL statement.
109
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
110
+ def sanitize_sql_array(ary)
111
+ statement, *values = ary
112
+ if values.first.is_a?(Hash) && statement =~ /:\w+/
113
+ replace_named_bind_variables(statement, values.first)
114
+ elsif statement.include?('?')
115
+ replace_bind_variables(statement, values)
116
+ elsif statement.blank?
117
+ statement
118
+ else
119
+ statement % values.collect { |value| connection.quote_string(value.to_s) }
120
+ end
121
+ end
122
+
123
+ alias_method :sanitize_conditions, :sanitize_sql
124
+
125
+ def replace_bind_variables(statement, values) #:nodoc:
126
+ raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
127
+ bound = values.dup
128
+ c = connection
129
+ statement.gsub('?') { quote_bound_value(bound.shift, c) }
130
+ end
131
+
132
+ def replace_named_bind_variables(statement, bind_vars) #:nodoc:
133
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do
134
+ if $1 == ':' # skip postgresql casts
135
+ $& # return the whole match
136
+ elsif bind_vars.include?(match = $2.to_sym)
137
+ quote_bound_value(bind_vars[match])
138
+ else
139
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
140
+ end
141
+ end
142
+ end
143
+
144
+ def quote_bound_value(value, c = connection) #:nodoc:
145
+ if value.respond_to?(:map) && !value.acts_like?(:string)
146
+ if value.respond_to?(:empty?) && value.empty?
147
+ c.quote(nil)
148
+ else
149
+ value.map { |v| c.quote(v) }.join(',')
150
+ end
151
+ else
152
+ c.quote(value)
153
+ end
154
+ end
155
+
156
+ def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
157
+ unless expected == provided
158
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
159
+ end
160
+ end
161
+ end
162
+
163
+ # TODO: Deprecate this
164
+ def quoted_id
165
+ self.class.quote_value(id, column_for_attribute(self.class.primary_key))
166
+ end
167
+ end
168
+ end
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/object/blank'
2
1
 
3
2
  module ActiveRecord
4
3
  # = Active Record Schema
@@ -12,16 +11,16 @@ module ActiveRecord
12
11
  #
13
12
  # ActiveRecord::Schema.define do
14
13
  # create_table :authors do |t|
15
- # t.string :name, :null => false
14
+ # t.string :name, null: false
16
15
  # end
17
16
  #
18
17
  # add_index :authors, :name, :unique
19
18
  #
20
19
  # create_table :posts do |t|
21
- # t.integer :author_id, :null => false
20
+ # t.integer :author_id, null: false
22
21
  # t.string :subject
23
22
  # t.text :body
24
- # t.boolean :private, :default => false
23
+ # t.boolean :private, default: false
25
24
  # end
26
25
  #
27
26
  # add_index :posts, :author_id
@@ -30,10 +29,22 @@ module ActiveRecord
30
29
  # ActiveRecord::Schema is only supported by database adapters that also
31
30
  # support migrations, the two features being very similar.
32
31
  class Schema < Migration
33
- private_class_method :new
34
32
 
35
- def self.migrations_path
36
- ActiveRecord::Migrator.migrations_path
33
+ # Returns the migrations paths.
34
+ #
35
+ # ActiveRecord::Schema.new.migrations_paths
36
+ # # => ["db/migrate"] # Rails migration path by default.
37
+ def migrations_paths
38
+ ActiveRecord::Migrator.migrations_paths
39
+ end
40
+
41
+ def define(info, &block) # :nodoc:
42
+ instance_eval(&block)
43
+
44
+ unless info[:version].blank?
45
+ initialize_schema_migrations_table
46
+ connection.assume_migrated_upto_version(info[:version], migrations_paths)
47
+ end
37
48
  end
38
49
 
39
50
  # Eval the given block. All methods available to the current connection
@@ -44,16 +55,11 @@ module ActiveRecord
44
55
  # The +info+ hash is optional, and if given is used to define metadata
45
56
  # about the current schema (currently, only the schema's version):
46
57
  #
47
- # ActiveRecord::Schema.define(:version => 20380119000001) do
58
+ # ActiveRecord::Schema.define(version: 20380119000001) do
48
59
  # ...
49
60
  # end
50
61
  def self.define(info={}, &block)
51
- instance_eval(&block)
52
-
53
- unless info[:version].blank?
54
- initialize_schema_migrations_table
55
- assume_migrated_upto_version(info[:version], migrations_path)
56
- end
62
+ new.define(info, &block)
57
63
  end
58
64
  end
59
65
  end
@@ -24,6 +24,7 @@ module ActiveRecord
24
24
 
25
25
  def dump(stream)
26
26
  header(stream)
27
+ extensions(stream)
27
28
  tables(stream)
28
29
  trailer(stream)
29
30
  stream
@@ -38,7 +39,11 @@ module ActiveRecord
38
39
  end
39
40
 
40
41
  def header(stream)
41
- define_params = @version ? ":version => #{@version}" : ""
42
+ define_params = @version ? "version: #{@version}" : ""
43
+
44
+ if stream.respond_to?(:external_encoding) && stream.external_encoding
45
+ stream.puts "# encoding: #{stream.external_encoding.name}"
46
+ end
42
47
 
43
48
  stream.puts <<HEADER
44
49
  # This file is auto-generated from the current state of the database. Instead
@@ -51,7 +56,7 @@ module ActiveRecord
51
56
  # from scratch. The latter is a flawed and unsustainable approach (the more migrations
52
57
  # you'll amass, the slower it'll run and the greater likelihood for issues).
53
58
  #
54
- # It's strongly recommended to check this file into your version control system.
59
+ # It's strongly recommended that you check this file into your version control system.
55
60
 
56
61
  ActiveRecord::Schema.define(#{define_params}) do
57
62
 
@@ -62,12 +67,24 @@ HEADER
62
67
  stream.puts "end"
63
68
  end
64
69
 
70
+ def extensions(stream)
71
+ return unless @connection.supports_extensions?
72
+ extensions = @connection.extensions
73
+ if extensions.any?
74
+ stream.puts " # These are extensions that must be enabled in order to support this database"
75
+ extensions.each do |extension|
76
+ stream.puts " enable_extension #{extension.inspect}"
77
+ end
78
+ stream.puts
79
+ end
80
+ end
81
+
65
82
  def tables(stream)
66
83
  @connection.tables.sort.each do |tbl|
67
84
  next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
68
85
  case ignored
69
- when String; tbl == ignored
70
- when Regexp; tbl =~ ignored
86
+ when String; remove_prefix_and_suffix(tbl) == ignored
87
+ when Regexp; remove_prefix_and_suffix(tbl) =~ ignored
71
88
  else
72
89
  raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
73
90
  end
@@ -83,50 +100,38 @@ HEADER
83
100
 
84
101
  # first dump primary key column
85
102
  if @connection.respond_to?(:pk_and_sequence_for)
86
- pk, pk_seq = @connection.pk_and_sequence_for(table)
103
+ pk, _ = @connection.pk_and_sequence_for(table)
87
104
  elsif @connection.respond_to?(:primary_key)
88
105
  pk = @connection.primary_key(table)
89
106
  end
90
107
 
91
- tbl.print " create_table #{table.inspect}"
108
+ tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
92
109
  if columns.detect { |c| c.name == pk }
93
110
  if pk != 'id'
94
- tbl.print %Q(, :primary_key => "#{pk}")
111
+ tbl.print %Q(, primary_key: "#{pk}")
95
112
  end
96
113
  else
97
- tbl.print ", :id => false"
114
+ tbl.print ", id: false"
98
115
  end
99
- tbl.print ", :force => true"
116
+ tbl.print ", force: true"
100
117
  tbl.puts " do |t|"
101
118
 
102
119
  # then dump all non-primary key columns
103
120
  column_specs = columns.map do |column|
104
- raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
121
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
105
122
  next if column.name == pk
106
- spec = {}
107
- spec[:name] = column.name.inspect
108
-
109
- # AR has an optimisation which handles zero-scale decimals as integers. This
110
- # code ensures that the dumper still dumps the column as a decimal.
111
- spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
112
- 'decimal'
113
- else
114
- column.type.to_s
115
- end
116
- spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
117
- spec[:precision] = column.precision.inspect if !column.precision.nil?
118
- spec[:scale] = column.scale.inspect if !column.scale.nil?
119
- spec[:null] = 'false' if !column.null
120
- spec[:default] = default_string(column.default) if column.has_default?
121
- (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
122
- spec
123
+ @connection.column_spec(column, @types)
123
124
  end.compact
124
125
 
125
126
  # find all migration keys used in this table
126
- keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map{ |k| k.keys }.flatten
127
+ keys = @connection.migration_keys
127
128
 
128
129
  # figure out the lengths for each column based on above keys
129
- lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
130
+ lengths = keys.map { |key|
131
+ column_specs.map { |spec|
132
+ spec[key] ? spec[key].length + 2 : 0
133
+ }.max
134
+ }
130
135
 
131
136
  # the string we're going to sprintf our values against, with standardized column widths
132
137
  format_string = lengths.map{ |len| "%-#{len}s" }
@@ -162,27 +167,27 @@ HEADER
162
167
  stream
163
168
  end
164
169
 
165
- def default_string(value)
166
- case value
167
- when BigDecimal
168
- value.to_s
169
- when Date, DateTime, Time
170
- "'" + value.to_s(:db) + "'"
171
- else
172
- value.inspect
173
- end
174
- end
175
-
176
170
  def indexes(table, stream)
177
171
  if (indexes = @connection.indexes(table)).any?
178
172
  add_index_statements = indexes.map do |index|
179
- statement_parts = [ ('add_index ' + index.table.inspect) ]
180
- statement_parts << index.columns.inspect
181
- statement_parts << (':name => ' + index.name.inspect)
182
- statement_parts << ':unique => true' if index.unique
173
+ statement_parts = [
174
+ ('add_index ' + remove_prefix_and_suffix(index.table).inspect),
175
+ index.columns.inspect,
176
+ ('name: ' + index.name.inspect),
177
+ ]
178
+ statement_parts << 'unique: true' if index.unique
179
+
180
+ index_lengths = (index.lengths || []).compact
181
+ statement_parts << ('length: ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
182
+
183
+ index_orders = (index.orders || {})
184
+ statement_parts << ('order: ' + index.orders.inspect) unless index_orders.empty?
183
185
 
184
- index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
185
- statement_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present?
186
+ statement_parts << ('where: ' + index.where.inspect) if index.where
187
+
188
+ statement_parts << ('using: ' + index.using.inspect) if index.using
189
+
190
+ statement_parts << ('type: ' + index.type.inspect) if index.type
186
191
 
187
192
  ' ' + statement_parts.join(', ')
188
193
  end
@@ -191,5 +196,9 @@ HEADER
191
196
  stream.puts
192
197
  end
193
198
  end
199
+
200
+ def remove_prefix_and_suffix(table)
201
+ table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
202
+ end
194
203
  end
195
204
  end
@@ -0,0 +1,39 @@
1
+ require 'active_record/scoping/default'
2
+ require 'active_record/scoping/named'
3
+ require 'active_record/base'
4
+
5
+ module ActiveRecord
6
+ class SchemaMigration < ActiveRecord::Base
7
+
8
+ def self.table_name
9
+ "#{Base.table_name_prefix}schema_migrations#{Base.table_name_suffix}"
10
+ end
11
+
12
+ def self.index_name
13
+ "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
14
+ end
15
+
16
+ def self.create_table(limit=nil)
17
+ unless connection.table_exists?(table_name)
18
+ version_options = {null: false}
19
+ version_options[:limit] = limit if limit
20
+
21
+ connection.create_table(table_name, id: false) do |t|
22
+ t.column :version, :string, version_options
23
+ end
24
+ connection.add_index table_name, :version, unique: true, name: index_name
25
+ end
26
+ end
27
+
28
+ def self.drop_table
29
+ if connection.table_exists?(table_name)
30
+ connection.remove_index table_name, name: index_name
31
+ connection.drop_table(table_name)
32
+ end
33
+ end
34
+
35
+ def version
36
+ super.to_i
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,146 @@
1
+ module ActiveRecord
2
+ module Scoping
3
+ module Default
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # Stores the default scope for the class.
8
+ class_attribute :default_scopes, instance_writer: false, instance_predicate: false
9
+
10
+ self.default_scopes = []
11
+
12
+ def self.default_scopes?
13
+ ActiveSupport::Deprecation.warn(
14
+ "#default_scopes? is deprecated. Do something like #default_scopes.empty? instead."
15
+ )
16
+
17
+ !!self.default_scopes
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ # Returns a scope for the model without the +default_scope+.
23
+ #
24
+ # class Post < ActiveRecord::Base
25
+ # def self.default_scope
26
+ # where published: true
27
+ # end
28
+ # end
29
+ #
30
+ # Post.all # Fires "SELECT * FROM posts WHERE published = true"
31
+ # Post.unscoped.all # Fires "SELECT * FROM posts"
32
+ #
33
+ # This method also accepts a block. All queries inside the block will
34
+ # not use the +default_scope+:
35
+ #
36
+ # Post.unscoped {
37
+ # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
38
+ # }
39
+ def unscoped
40
+ block_given? ? relation.scoping { yield } : relation
41
+ end
42
+
43
+ def before_remove_const #:nodoc:
44
+ self.current_scope = nil
45
+ end
46
+
47
+ protected
48
+
49
+ # Use this macro in your model to set a default scope for all operations on
50
+ # the model.
51
+ #
52
+ # class Article < ActiveRecord::Base
53
+ # default_scope { where(published: true) }
54
+ # end
55
+ #
56
+ # Article.all # => SELECT * FROM articles WHERE published = true
57
+ #
58
+ # The +default_scope+ is also applied while creating/building a record.
59
+ # It is not applied while updating a record.
60
+ #
61
+ # Article.new.published # => true
62
+ # Article.create.published # => true
63
+ #
64
+ # (You can also pass any object which responds to +call+ to the
65
+ # +default_scope+ macro, and it will be called when building the
66
+ # default scope.)
67
+ #
68
+ # If you use multiple +default_scope+ declarations in your model then
69
+ # they will be merged together:
70
+ #
71
+ # class Article < ActiveRecord::Base
72
+ # default_scope { where(published: true) }
73
+ # default_scope { where(rating: 'G') }
74
+ # end
75
+ #
76
+ # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
77
+ #
78
+ # This is also the case with inheritance and module includes where the
79
+ # parent or module defines a +default_scope+ and the child or including
80
+ # class defines a second one.
81
+ #
82
+ # If you need to do more complex things with a default scope, you can
83
+ # alternatively define it as a class method:
84
+ #
85
+ # class Article < ActiveRecord::Base
86
+ # def self.default_scope
87
+ # # Should return a scope, you can call 'super' here etc.
88
+ # end
89
+ # end
90
+ def default_scope(scope = nil)
91
+ scope = Proc.new if block_given?
92
+
93
+ if scope.is_a?(Relation) || !scope.respond_to?(:call)
94
+ ActiveSupport::Deprecation.warn(
95
+ "Calling #default_scope without a block is deprecated. For example instead " \
96
+ "of `default_scope where(color: 'red')`, please use " \
97
+ "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
98
+ "self.default_scope.)"
99
+ )
100
+ end
101
+
102
+ self.default_scopes += [scope]
103
+ end
104
+
105
+ def build_default_scope # :nodoc:
106
+ if !Base.is_a?(method(:default_scope).owner)
107
+ # The user has defined their own default scope method, so call that
108
+ evaluate_default_scope { default_scope }
109
+ elsif default_scopes.any?
110
+ evaluate_default_scope do
111
+ default_scopes.inject(relation) do |default_scope, scope|
112
+ if !scope.is_a?(Relation) && scope.respond_to?(:call)
113
+ default_scope.merge(unscoped { scope.call })
114
+ else
115
+ default_scope.merge(scope)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ def ignore_default_scope? # :nodoc:
123
+ ScopeRegistry.value_for(:ignore_default_scope, self)
124
+ end
125
+
126
+ def ignore_default_scope=(ignore) # :nodoc:
127
+ ScopeRegistry.set_value_for(:ignore_default_scope, self, ignore)
128
+ end
129
+
130
+ # The ignore_default_scope flag is used to prevent an infinite recursion
131
+ # situation where a default scope references a scope which has a default
132
+ # scope which references a scope...
133
+ def evaluate_default_scope # :nodoc:
134
+ return if ignore_default_scope?
135
+
136
+ begin
137
+ self.ignore_default_scope = true
138
+ yield
139
+ ensure
140
+ self.ignore_default_scope = false
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end