activerecord 3.2.22.4 → 4.0.13

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/array/wrap'
2
1
  require 'active_support/core_ext/hash/conversions'
3
2
 
4
3
  module ActiveRecord #:nodoc:
@@ -19,8 +18,8 @@ module ActiveRecord #:nodoc:
19
18
  # <id type="integer">1</id>
20
19
  # <approved type="boolean">false</approved>
21
20
  # <replies-count type="integer">0</replies-count>
22
- # <bonus-time type="datetime">2000-01-01T08:28:00+12:00</bonus-time>
23
- # <written-on type="datetime">2003-07-16T09:28:00+1200</written-on>
21
+ # <bonus-time type="dateTime">2000-01-01T08:28:00+12:00</bonus-time>
22
+ # <written-on type="dateTime">2003-07-16T09:28:00+1200</written-on>
24
23
  # <content>Have a nice day</content>
25
24
  # <author-email-address>david@loudthinking.com</author-email-address>
26
25
  # <parent-id></parent-id>
@@ -37,7 +36,7 @@ module ActiveRecord #:nodoc:
37
36
  #
38
37
  # For instance:
39
38
  #
40
- # topic.to_xml(:skip_instruct => true, :except => [ :id, :bonus_time, :written_on, :replies_count ])
39
+ # topic.to_xml(skip_instruct: true, except: [ :id, :bonus_time, :written_on, :replies_count ])
41
40
  #
42
41
  # <topic>
43
42
  # <title>The First Topic</title>
@@ -51,7 +50,7 @@ module ActiveRecord #:nodoc:
51
50
  #
52
51
  # To include first level associations use <tt>:include</tt>:
53
52
  #
54
- # firm.to_xml :include => [ :account, :clients ]
53
+ # firm.to_xml include: [ :account, :clients ]
55
54
  #
56
55
  # <?xml version="1.0" encoding="UTF-8"?>
57
56
  # <firm>
@@ -82,7 +81,7 @@ module ActiveRecord #:nodoc:
82
81
  # associated with models.
83
82
  #
84
83
  # proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
85
- # firm.to_xml :procs => [ proc ]
84
+ # firm.to_xml procs: [ proc ]
86
85
  #
87
86
  # <firm>
88
87
  # # ... normal attributes as shown above ...
@@ -91,7 +90,7 @@ module ActiveRecord #:nodoc:
91
90
  #
92
91
  # To include deeper levels of associations pass a hash like this:
93
92
  #
94
- # firm.to_xml :include => {:account => {}, :clients => {:include => :address}}
93
+ # firm.to_xml include: {account: {}, clients: {include: :address}}
95
94
  # <?xml version="1.0" encoding="UTF-8"?>
96
95
  # <firm>
97
96
  # <id type="integer">1</id>
@@ -121,7 +120,7 @@ module ActiveRecord #:nodoc:
121
120
  #
122
121
  # To include any methods on the model being called use <tt>:methods</tt>:
123
122
  #
124
- # firm.to_xml :methods => [ :calculated_earnings, :real_earnings ]
123
+ # firm.to_xml methods: [ :calculated_earnings, :real_earnings ]
125
124
  #
126
125
  # <firm>
127
126
  # # ... normal attributes as shown above ...
@@ -133,7 +132,7 @@ module ActiveRecord #:nodoc:
133
132
  # modified version of the options hash that was given to +to_xml+:
134
133
  #
135
134
  # proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
136
- # firm.to_xml :procs => [ proc ]
135
+ # firm.to_xml procs: [ proc ]
137
136
  #
138
137
  # <firm>
139
138
  # # ... normal attributes as shown above ...
@@ -165,7 +164,7 @@ module ActiveRecord #:nodoc:
165
164
  # def to_xml(options = {})
166
165
  # require 'builder'
167
166
  # options[:indent] ||= 2
168
- # xml = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
167
+ # xml = options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])
169
168
  # xml.instruct! unless options[:skip_instruct]
170
169
  # xml.level_one do
171
170
  # xml.tag!(:second_level, 'content')
@@ -178,11 +177,6 @@ module ActiveRecord #:nodoc:
178
177
  end
179
178
 
180
179
  class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
181
- def initialize(*args)
182
- super
183
- options[:except] = Array.wrap(options[:except]) | Array.wrap(@serializable.class.inheritance_column)
184
- end
185
-
186
180
  class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
187
181
  def compute_type
188
182
  klass = @serializable.class
@@ -0,0 +1,26 @@
1
+ module ActiveRecord
2
+
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 initialization block:
5
+ #
6
+ # cache = ActiveRecord::StatementCache.new do
7
+ # Book.where(name: "my book").limit(100)
8
+ # end
9
+ #
10
+ # The cached statement is executed by using the +execute+ method:
11
+ #
12
+ # cache.execute
13
+ #
14
+ # The relation returned by the block is cached, and for each +execute+ call the cached relation gets duped.
15
+ # Database is queried when +to_a+ is called on the relation.
16
+ class StatementCache
17
+ def initialize
18
+ @relation = yield
19
+ raise ArgumentError.new("Statement cannot be nil") if @relation.nil?
20
+ end
21
+
22
+ def execute
23
+ @relation.dup.to_a
24
+ end
25
+ end
26
+ end
@@ -1,6 +1,8 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+
1
3
  module ActiveRecord
2
4
  # Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
3
- # It's like a simple key/value store backed into your record when you don't care about being able to
5
+ # It's like a simple key/value store baked into your record when you don't care about being able to
4
6
  # query that store outside the context of a single record.
5
7
  #
6
8
  # You can then declare accessors to this store that are then accessible just like any other attribute
@@ -10,43 +12,157 @@ module ActiveRecord
10
12
  # Make sure that you declare the database column used for the serialized store as a text, so there's
11
13
  # plenty of room.
12
14
  #
15
+ # You can set custom coder to encode/decode your serialized attributes to/from different formats.
16
+ # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
17
+ #
13
18
  # Examples:
14
19
  #
15
20
  # class User < ActiveRecord::Base
16
- # store :settings, accessors: [ :color, :homepage ]
21
+ # store :settings, accessors: [ :color, :homepage ], coder: JSON
17
22
  # end
18
- #
23
+ #
19
24
  # u = User.new(color: 'black', homepage: '37signals.com')
20
25
  # u.color # Accessor stored attribute
21
26
  # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
22
27
  #
28
+ # # There is no difference between strings and symbols for accessing custom attributes
29
+ # u.settings[:country] # => 'Denmark'
30
+ # u.settings['country'] # => 'Denmark'
31
+ #
23
32
  # # Add additional accessors to an existing store through store_accessor
24
33
  # class SuperUser < User
25
34
  # store_accessor :settings, :privileges, :servants
26
35
  # end
36
+ #
37
+ # The stored attribute names can be retrieved using +stored_attributes+.
38
+ #
39
+ # User.stored_attributes[:settings] # [:color, :homepage]
40
+ #
41
+ # == Overwriting default accessors
42
+ #
43
+ # All stored values are automatically available through accessors on the Active Record
44
+ # object, but sometimes you want to specialize this behavior. This can be done by overwriting
45
+ # the default accessors (using the same name as the attribute) and calling <tt>super</tt>
46
+ # to actually change things.
47
+ #
48
+ # class Song < ActiveRecord::Base
49
+ # # Uses a stored integer to hold the volume adjustment of the song
50
+ # store :settings, accessors: [:volume_adjustment]
51
+ #
52
+ # def volume_adjustment=(decibels)
53
+ # super(decibels.to_i)
54
+ # end
55
+ #
56
+ # def volume_adjustment
57
+ # super.to_i
58
+ # end
59
+ # end
27
60
  module Store
28
61
  extend ActiveSupport::Concern
29
-
62
+
63
+ included do
64
+ class << self
65
+ attr_accessor :local_stored_attributes
66
+ end
67
+ end
68
+
30
69
  module ClassMethods
31
70
  def store(store_attribute, options = {})
32
- serialize store_attribute, Hash
71
+ serialize store_attribute, IndifferentCoder.new(options[:coder])
33
72
  store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
34
73
  end
35
74
 
36
75
  def store_accessor(store_attribute, *keys)
37
- Array(keys).flatten.each do |key|
38
- define_method("#{key}=") do |value|
39
- send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
40
- send("#{store_attribute}_will_change!")
41
- send(store_attribute)[key] = value
76
+ keys = keys.flatten
77
+
78
+ _store_accessors_module.module_eval do
79
+ keys.each do |key|
80
+ define_method("#{key}=") do |value|
81
+ write_store_attribute(store_attribute, key, value)
82
+ end
83
+
84
+ define_method(key) do
85
+ read_store_attribute(store_attribute, key)
86
+ end
42
87
  end
43
-
44
- define_method(key) do
45
- send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
46
- send(store_attribute)[key]
88
+ end
89
+
90
+ # assign new store attribute and create new hash to ensure that each class in the hierarchy
91
+ # has its own hash of stored attributes.
92
+ self.local_stored_attributes ||= {}
93
+ self.local_stored_attributes[store_attribute] ||= []
94
+ self.local_stored_attributes[store_attribute] |= keys
95
+ end
96
+
97
+ def _store_accessors_module
98
+ @_store_accessors_module ||= begin
99
+ mod = Module.new
100
+ include mod
101
+ mod
102
+ end
103
+ end
104
+
105
+ def stored_attributes
106
+ parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
107
+ if self.local_stored_attributes
108
+ parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
109
+ end
110
+ parent
111
+ end
112
+ end
113
+
114
+ protected
115
+ def read_store_attribute(store_attribute, key)
116
+ attribute = initialize_store_attribute(store_attribute)
117
+ attribute[key]
118
+ end
119
+
120
+ def write_store_attribute(store_attribute, key, value)
121
+ attribute = initialize_store_attribute(store_attribute)
122
+ if value != attribute[key]
123
+ send :"#{store_attribute}_will_change!"
124
+ attribute[key] = value
125
+ end
126
+ end
127
+
128
+ private
129
+ def initialize_store_attribute(store_attribute)
130
+ attribute = send(store_attribute)
131
+ unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
132
+ attribute = IndifferentCoder.as_indifferent_hash(attribute)
133
+ send :"#{store_attribute}=", attribute
134
+ end
135
+ attribute
136
+ end
137
+
138
+ class IndifferentCoder # :nodoc:
139
+ def initialize(coder_or_class_name)
140
+ @coder =
141
+ if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
142
+ coder_or_class_name
143
+ else
144
+ ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
47
145
  end
146
+ end
147
+
148
+ def dump(obj)
149
+ @coder.dump self.class.as_indifferent_hash(obj)
150
+ end
151
+
152
+ def load(yaml)
153
+ self.class.as_indifferent_hash @coder.load(yaml || '')
154
+ end
155
+
156
+ def self.as_indifferent_hash(obj)
157
+ case obj
158
+ when ActiveSupport::HashWithIndifferentAccess
159
+ obj
160
+ when Hash
161
+ obj.with_indifferent_access
162
+ else
163
+ ActiveSupport::HashWithIndifferentAccess.new
48
164
  end
49
165
  end
50
166
  end
51
167
  end
52
- end
168
+ end
@@ -0,0 +1,204 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class DatabaseAlreadyExists < StandardError; end # :nodoc:
4
+ class DatabaseNotSupported < StandardError; end # :nodoc:
5
+
6
+ # <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
7
+ # logic behind common tasks used to manage database and migrations.
8
+ #
9
+ # The tasks defined here are used in rake tasks provided by Active Record.
10
+ #
11
+ # In order to use DatabaseTasks, a few config values need to be set. All the needed
12
+ # config values are set by Rails already, so it's necessary to do it only if you
13
+ # want to change the defaults or when you want to use Active Record outside of Rails
14
+ # (in such case after configuring the database tasks, you can also use the rake tasks
15
+ # defined in Active Record).
16
+ #
17
+ #
18
+ # The possible config values are:
19
+ #
20
+ # * +env+: current environment (like Rails.env).
21
+ # * +database_configuration+: configuration of your databases (as in +config/database.yml+).
22
+ # * +db_dir+: your +db+ directory.
23
+ # * +fixtures_path+: a path to fixtures directory.
24
+ # * +migrations_paths+: a list of paths to directories with migrations.
25
+ # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
26
+ # * +root+: a path to the root of the application.
27
+ #
28
+ # Example usage of +DatabaseTasks+ outside Rails could look as such:
29
+ #
30
+ # include ActiveRecord::Tasks
31
+ # DatabaseTasks.database_configuration = YAML.load(File.read('my_database_config.yml'))
32
+ # DatabaseTasks.db_dir = 'db'
33
+ # # other settings...
34
+ #
35
+ # DatabaseTasks.create_current('production')
36
+ module DatabaseTasks
37
+ extend self
38
+
39
+ attr_writer :current_config
40
+ attr_accessor :database_configuration, :migrations_paths, :seed_loader, :db_dir,
41
+ :fixtures_path, :env, :root
42
+
43
+ LOCAL_HOSTS = ['127.0.0.1', 'localhost']
44
+
45
+ def register_task(pattern, task)
46
+ @tasks ||= {}
47
+ @tasks[pattern] = task
48
+ end
49
+
50
+ register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks)
51
+ register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
52
+ register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
53
+
54
+ register_task(/firebird/, ActiveRecord::Tasks::FirebirdDatabaseTasks)
55
+ register_task(/sqlserver/, ActiveRecord::Tasks::SqlserverDatabaseTasks)
56
+ register_task(/(oci|oracle)/, ActiveRecord::Tasks::OracleDatabaseTasks)
57
+
58
+ def current_config(options = {})
59
+ options.reverse_merge! :env => env
60
+ if options.has_key?(:config)
61
+ @current_config = options[:config]
62
+ else
63
+ @current_config ||= if ENV['DATABASE_URL']
64
+ database_url_config
65
+ else
66
+ ActiveRecord::Base.configurations[options[:env]]
67
+ end
68
+ end
69
+ end
70
+
71
+ def create(*arguments)
72
+ configuration = arguments.first
73
+ class_for_adapter(configuration['adapter']).new(*arguments).create
74
+ rescue DatabaseAlreadyExists
75
+ $stderr.puts "#{configuration['database']} already exists"
76
+ rescue Exception => error
77
+ $stderr.puts error, *(error.backtrace)
78
+ $stderr.puts "Couldn't create database for #{configuration.inspect}"
79
+ end
80
+
81
+ def create_all
82
+ each_local_configuration { |configuration| create configuration }
83
+ end
84
+
85
+ def create_current(environment = env)
86
+ each_current_configuration(environment) { |configuration|
87
+ create configuration
88
+ }
89
+ ActiveRecord::Base.establish_connection environment
90
+ end
91
+
92
+ def create_database_url
93
+ create database_url_config
94
+ end
95
+
96
+ def drop(*arguments)
97
+ configuration = arguments.first
98
+ class_for_adapter(configuration['adapter']).new(*arguments).drop
99
+ rescue Exception => error
100
+ $stderr.puts error, *(error.backtrace)
101
+ $stderr.puts "Couldn't drop #{configuration['database']}"
102
+ end
103
+
104
+ def drop_all
105
+ each_local_configuration { |configuration| drop configuration }
106
+ end
107
+
108
+ def drop_current(environment = env)
109
+ each_current_configuration(environment) { |configuration|
110
+ drop configuration
111
+ }
112
+ end
113
+
114
+ def drop_database_url
115
+ drop database_url_config
116
+ end
117
+
118
+ def charset_current(environment = env)
119
+ charset ActiveRecord::Base.configurations[environment]
120
+ end
121
+
122
+ def charset(*arguments)
123
+ configuration = arguments.first
124
+ class_for_adapter(configuration['adapter']).new(*arguments).charset
125
+ end
126
+
127
+ def collation_current(environment = env)
128
+ collation ActiveRecord::Base.configurations[environment]
129
+ end
130
+
131
+ def collation(*arguments)
132
+ configuration = arguments.first
133
+ class_for_adapter(configuration['adapter']).new(*arguments).collation
134
+ end
135
+
136
+ def purge(configuration)
137
+ class_for_adapter(configuration['adapter']).new(configuration).purge
138
+ end
139
+
140
+ def structure_dump(*arguments)
141
+ configuration = arguments.first
142
+ filename = arguments.delete_at 1
143
+ class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
144
+ end
145
+
146
+ def structure_load(*arguments)
147
+ configuration = arguments.first
148
+ filename = arguments.delete_at 1
149
+ class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
150
+ end
151
+
152
+ def load_seed
153
+ if seed_loader
154
+ seed_loader.load_seed
155
+ else
156
+ raise "You tried to load seed data, but no seed loader is specified. Please specify seed " +
157
+ "loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" +
158
+ "Seed loader should respond to load_seed method"
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ def database_url_config
165
+ @database_url_config ||=
166
+ ConnectionAdapters::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys
167
+ end
168
+
169
+ def class_for_adapter(adapter)
170
+ key = @tasks.keys.detect { |pattern| adapter[pattern] }
171
+ unless key
172
+ raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
173
+ end
174
+ @tasks[key]
175
+ end
176
+
177
+ def each_current_configuration(environment)
178
+ environments = [environment]
179
+ environments << 'test' if environment == 'development'
180
+
181
+ configurations = ActiveRecord::Base.configurations.values_at(*environments)
182
+ configurations.compact.each do |configuration|
183
+ yield configuration unless configuration['database'].blank?
184
+ end
185
+ end
186
+
187
+ def each_local_configuration
188
+ ActiveRecord::Base.configurations.each_value do |configuration|
189
+ next unless configuration['database']
190
+
191
+ if local_database?(configuration)
192
+ yield configuration
193
+ else
194
+ $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
195
+ end
196
+ end
197
+ end
198
+
199
+ def local_database?(configuration)
200
+ configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,56 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class FirebirdDatabaseTasks # :nodoc:
4
+ delegate :connection, :establish_connection, to: ActiveRecord::Base
5
+
6
+ def initialize(configuration)
7
+ ActiveSupport::Deprecation.warn "This database tasks were deprecated, because this tasks should be served by the 3rd party adapter."
8
+ @configuration = configuration
9
+ end
10
+
11
+ def create
12
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
13
+ end
14
+
15
+ def drop
16
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
17
+ end
18
+
19
+ def purge
20
+ establish_connection(:test)
21
+ connection.recreate_database!
22
+ end
23
+
24
+ def charset
25
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
26
+ end
27
+
28
+ def structure_dump(filename)
29
+ set_firebird_env(configuration)
30
+ db_string = firebird_db_string(configuration)
31
+ Kernel.system "isql -a #{db_string} > #{filename}"
32
+ end
33
+
34
+ def structure_load(filename)
35
+ set_firebird_env(configuration)
36
+ db_string = firebird_db_string(configuration)
37
+ Kernel.system "isql -i #{filename} #{db_string}"
38
+ end
39
+
40
+ private
41
+
42
+ def set_firebird_env(config)
43
+ ENV['ISC_USER'] = config['username'].to_s if config['username']
44
+ ENV['ISC_PASSWORD'] = config['password'].to_s if config['password']
45
+ end
46
+
47
+ def firebird_db_string(config)
48
+ FireRuby::Database.db_string_for(config.symbolize_keys)
49
+ end
50
+
51
+ def configuration
52
+ @configuration
53
+ end
54
+ end
55
+ end
56
+ end