activerecord 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1372 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +218 -0
- data/examples/performance.rb +184 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +173 -0
- data/lib/active_record/aggregations.rb +266 -0
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations.rb +1724 -0
- data/lib/active_record/associations/alias_tracker.rb +87 -0
- data/lib/active_record/associations/association.rb +253 -0
- data/lib/active_record/associations/association_scope.rb +194 -0
- data/lib/active_record/associations/belongs_to_association.rb +111 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
- data/lib/active_record/associations/builder/association.rb +149 -0
- data/lib/active_record/associations/builder/belongs_to.rb +116 -0
- data/lib/active_record/associations/builder/collection_association.rb +91 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +23 -0
- data/lib/active_record/associations/builder/singular_association.rb +38 -0
- data/lib/active_record/associations/collection_association.rb +634 -0
- data/lib/active_record/associations/collection_proxy.rb +1027 -0
- data/lib/active_record/associations/has_many_association.rb +184 -0
- data/lib/active_record/associations/has_many_through_association.rb +238 -0
- data/lib/active_record/associations/has_one_association.rb +105 -0
- data/lib/active_record/associations/has_one_through_association.rb +36 -0
- data/lib/active_record/associations/join_dependency.rb +282 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/preloader.rb +203 -0
- data/lib/active_record/associations/preloader/association.rb +162 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +96 -0
- data/lib/active_record/associations/singular_association.rb +86 -0
- data/lib/active_record/associations/through_association.rb +96 -0
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +439 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
- data/lib/active_record/attribute_methods/dirty.rb +181 -0
- data/lib/active_record/attribute_methods/primary_key.rb +128 -0
- data/lib/active_record/attribute_methods/query.rb +40 -0
- data/lib/active_record/attribute_methods/read.rb +103 -0
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
- data/lib/active_record/attribute_methods/write.rb +83 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +439 -0
- data/lib/active_record/base.rb +317 -0
- data/lib/active_record/callbacks.rb +313 -0
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
- data/lib/active_record/connection_adapters/column.rb +82 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +566 -0
- data/lib/active_record/counter_cache.rb +175 -0
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +198 -0
- data/lib/active_record/errors.rb +252 -0
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +1007 -0
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- data/lib/active_record/locale/en.yml +47 -0
- data/lib/active_record/locking/optimistic.rb +204 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/log_subscriber.rb +75 -0
- data/lib/active_record/migration.rb +1051 -0
- data/lib/active_record/migration/command_recorder.rb +197 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +340 -0
- data/lib/active_record/nested_attributes.rb +548 -0
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +532 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +162 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +50 -0
- data/lib/active_record/railties/databases.rake +391 -0
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +881 -0
- data/lib/active_record/relation.rb +681 -0
- data/lib/active_record/relation/batches.rb +138 -0
- data/lib/active_record/relation/calculations.rb +403 -0
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +528 -0
- data/lib/active_record/relation/merger.rb +170 -0
- data/lib/active_record/relation/predicate_builder.rb +126 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +1176 -0
- data/lib/active_record/relation/spawn_methods.rb +75 -0
- data/lib/active_record/result.rb +131 -0
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +64 -0
- data/lib/active_record/schema_dumper.rb +251 -0
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/scoping/default.rb +134 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/serialization.rb +22 -0
- data/lib/active_record/serializers/xml_serializer.rb +193 -0
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- data/lib/active_record/tasks/database_tasks.rb +296 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +121 -0
- data/lib/active_record/transactions.rb +417 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +90 -0
- data/lib/active_record/validations/associated.rb +51 -0
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +229 -0
- data/lib/active_record/version.rb +8 -0
- data/lib/rails/generators/active_record.rb +17 -0
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
- metadata +309 -0
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
# Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
|
5
|
+
# It's like a simple key/value store baked into your record when you don't care about being able to
|
6
|
+
# query that store outside the context of a single record.
|
7
|
+
#
|
8
|
+
# You can then declare accessors to this store that are then accessible just like any other attribute
|
9
|
+
# of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
|
10
|
+
# already built around just accessing attributes on the model.
|
11
|
+
#
|
12
|
+
# Make sure that you declare the database column used for the serialized store as a text, so there's
|
13
|
+
# plenty of room.
|
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
|
+
#
|
18
|
+
# NOTE - If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
|
19
|
+
# the serialization provided by +store+. Simply use +store_accessor+ instead to generate
|
20
|
+
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
|
21
|
+
# using a symbol.
|
22
|
+
#
|
23
|
+
# Examples:
|
24
|
+
#
|
25
|
+
# class User < ActiveRecord::Base
|
26
|
+
# store :settings, accessors: [ :color, :homepage ], coder: JSON
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# u = User.new(color: 'black', homepage: '37signals.com')
|
30
|
+
# u.color # Accessor stored attribute
|
31
|
+
# u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
|
32
|
+
#
|
33
|
+
# # There is no difference between strings and symbols for accessing custom attributes
|
34
|
+
# u.settings[:country] # => 'Denmark'
|
35
|
+
# u.settings['country'] # => 'Denmark'
|
36
|
+
#
|
37
|
+
# # Add additional accessors to an existing store through store_accessor
|
38
|
+
# class SuperUser < User
|
39
|
+
# store_accessor :settings, :privileges, :servants
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# The stored attribute names can be retrieved using +stored_attributes+.
|
43
|
+
#
|
44
|
+
# User.stored_attributes[:settings] # [:color, :homepage]
|
45
|
+
#
|
46
|
+
# == Overwriting default accessors
|
47
|
+
#
|
48
|
+
# All stored values are automatically available through accessors on the Active Record
|
49
|
+
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
|
50
|
+
# the default accessors (using the same name as the attribute) and calling <tt>super</tt>
|
51
|
+
# to actually change things.
|
52
|
+
#
|
53
|
+
# class Song < ActiveRecord::Base
|
54
|
+
# # Uses a stored integer to hold the volume adjustment of the song
|
55
|
+
# store :settings, accessors: [:volume_adjustment]
|
56
|
+
#
|
57
|
+
# def volume_adjustment=(decibels)
|
58
|
+
# super(decibels.to_i)
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# def volume_adjustment
|
62
|
+
# super.to_i
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
module Store
|
66
|
+
extend ActiveSupport::Concern
|
67
|
+
|
68
|
+
included do
|
69
|
+
class << self
|
70
|
+
attr_accessor :local_stored_attributes
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module ClassMethods
|
75
|
+
def store(store_attribute, options = {})
|
76
|
+
serialize store_attribute, IndifferentCoder.new(options[:coder])
|
77
|
+
store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
|
78
|
+
end
|
79
|
+
|
80
|
+
def store_accessor(store_attribute, *keys)
|
81
|
+
keys = keys.flatten
|
82
|
+
|
83
|
+
_store_accessors_module.module_eval do
|
84
|
+
keys.each do |key|
|
85
|
+
define_method("#{key}=") do |value|
|
86
|
+
write_store_attribute(store_attribute, key, value)
|
87
|
+
end
|
88
|
+
|
89
|
+
define_method(key) do
|
90
|
+
read_store_attribute(store_attribute, key)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# assign new store attribute and create new hash to ensure that each class in the hierarchy
|
96
|
+
# has its own hash of stored attributes.
|
97
|
+
self.local_stored_attributes ||= {}
|
98
|
+
self.local_stored_attributes[store_attribute] ||= []
|
99
|
+
self.local_stored_attributes[store_attribute] |= keys
|
100
|
+
end
|
101
|
+
|
102
|
+
def _store_accessors_module # :nodoc:
|
103
|
+
@_store_accessors_module ||= begin
|
104
|
+
mod = Module.new
|
105
|
+
include mod
|
106
|
+
mod
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def stored_attributes
|
111
|
+
parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
|
112
|
+
if self.local_stored_attributes
|
113
|
+
parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
|
114
|
+
end
|
115
|
+
parent
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
protected
|
120
|
+
def read_store_attribute(store_attribute, key)
|
121
|
+
accessor = store_accessor_for(store_attribute)
|
122
|
+
accessor.read(self, store_attribute, key)
|
123
|
+
end
|
124
|
+
|
125
|
+
def write_store_attribute(store_attribute, key, value)
|
126
|
+
accessor = store_accessor_for(store_attribute)
|
127
|
+
accessor.write(self, store_attribute, key, value)
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def store_accessor_for(store_attribute)
|
132
|
+
type_for_attribute(store_attribute.to_s).accessor
|
133
|
+
end
|
134
|
+
|
135
|
+
class HashAccessor # :nodoc:
|
136
|
+
def self.read(object, attribute, key)
|
137
|
+
prepare(object, attribute)
|
138
|
+
object.public_send(attribute)[key]
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.write(object, attribute, key, value)
|
142
|
+
prepare(object, attribute)
|
143
|
+
if value != read(object, attribute, key)
|
144
|
+
object.public_send :"#{attribute}_will_change!"
|
145
|
+
object.public_send(attribute)[key] = value
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.prepare(object, attribute)
|
150
|
+
object.public_send :"#{attribute}=", {} unless object.send(attribute)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class StringKeyedHashAccessor < HashAccessor # :nodoc:
|
155
|
+
def self.read(object, attribute, key)
|
156
|
+
super object, attribute, key.to_s
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.write(object, attribute, key, value)
|
160
|
+
super object, attribute, key.to_s, value
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
|
165
|
+
def self.prepare(object, store_attribute)
|
166
|
+
attribute = object.send(store_attribute)
|
167
|
+
unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
|
168
|
+
attribute = IndifferentCoder.as_indifferent_hash(attribute)
|
169
|
+
object.send :"#{store_attribute}=", attribute
|
170
|
+
end
|
171
|
+
attribute
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class IndifferentCoder # :nodoc:
|
176
|
+
def initialize(coder_or_class_name)
|
177
|
+
@coder =
|
178
|
+
if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
|
179
|
+
coder_or_class_name
|
180
|
+
else
|
181
|
+
ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def dump(obj)
|
186
|
+
@coder.dump self.class.as_indifferent_hash(obj)
|
187
|
+
end
|
188
|
+
|
189
|
+
def load(yaml)
|
190
|
+
self.class.as_indifferent_hash(@coder.load(yaml || ''))
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.as_indifferent_hash(obj)
|
194
|
+
case obj
|
195
|
+
when ActiveSupport::HashWithIndifferentAccess
|
196
|
+
obj
|
197
|
+
when Hash
|
198
|
+
obj.with_indifferent_access
|
199
|
+
else
|
200
|
+
ActiveSupport::HashWithIndifferentAccess.new
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
require 'active_support/core_ext/string/filters'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Tasks # :nodoc:
|
5
|
+
class DatabaseAlreadyExists < StandardError; end # :nodoc:
|
6
|
+
class DatabaseNotSupported < StandardError; end # :nodoc:
|
7
|
+
|
8
|
+
# <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
|
9
|
+
# logic behind common tasks used to manage database and migrations.
|
10
|
+
#
|
11
|
+
# The tasks defined here are used with Rake tasks provided by Active Record.
|
12
|
+
#
|
13
|
+
# In order to use DatabaseTasks, a few config values need to be set. All the needed
|
14
|
+
# config values are set by Rails already, so it's necessary to do it only if you
|
15
|
+
# want to change the defaults or when you want to use Active Record outside of Rails
|
16
|
+
# (in such case after configuring the database tasks, you can also use the rake tasks
|
17
|
+
# defined in Active Record).
|
18
|
+
#
|
19
|
+
# The possible config values are:
|
20
|
+
#
|
21
|
+
# * +env+: current environment (like Rails.env).
|
22
|
+
# * +database_configuration+: configuration of your databases (as in +config/database.yml+).
|
23
|
+
# * +db_dir+: your +db+ directory.
|
24
|
+
# * +fixtures_path+: a path to fixtures directory.
|
25
|
+
# * +migrations_paths+: a list of paths to directories with migrations.
|
26
|
+
# * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
|
27
|
+
# * +root+: a path to the root of the application.
|
28
|
+
#
|
29
|
+
# Example usage of +DatabaseTasks+ outside Rails could look as such:
|
30
|
+
#
|
31
|
+
# include ActiveRecord::Tasks
|
32
|
+
# DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
|
33
|
+
# DatabaseTasks.db_dir = 'db'
|
34
|
+
# # other settings...
|
35
|
+
#
|
36
|
+
# DatabaseTasks.create_current('production')
|
37
|
+
module DatabaseTasks
|
38
|
+
extend self
|
39
|
+
|
40
|
+
attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
|
41
|
+
attr_accessor :database_configuration
|
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
|
+
def db_dir
|
55
|
+
@db_dir ||= Rails.application.config.paths["db"].first
|
56
|
+
end
|
57
|
+
|
58
|
+
def migrations_paths
|
59
|
+
@migrations_paths ||= Rails.application.paths['db/migrate'].to_a
|
60
|
+
end
|
61
|
+
|
62
|
+
def fixtures_path
|
63
|
+
@fixtures_path ||= if ENV['FIXTURES_PATH']
|
64
|
+
File.join(root, ENV['FIXTURES_PATH'])
|
65
|
+
else
|
66
|
+
File.join(root, 'test', 'fixtures')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def root
|
71
|
+
@root ||= Rails.root
|
72
|
+
end
|
73
|
+
|
74
|
+
def env
|
75
|
+
@env ||= Rails.env
|
76
|
+
end
|
77
|
+
|
78
|
+
def seed_loader
|
79
|
+
@seed_loader ||= Rails.application
|
80
|
+
end
|
81
|
+
|
82
|
+
def current_config(options = {})
|
83
|
+
options.reverse_merge! :env => env
|
84
|
+
if options.has_key?(:config)
|
85
|
+
@current_config = options[:config]
|
86
|
+
else
|
87
|
+
@current_config ||= ActiveRecord::Base.configurations[options[:env]]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def create(*arguments)
|
92
|
+
configuration = arguments.first
|
93
|
+
class_for_adapter(configuration['adapter']).new(*arguments).create
|
94
|
+
rescue DatabaseAlreadyExists
|
95
|
+
$stderr.puts "#{configuration['database']} already exists"
|
96
|
+
rescue Exception => error
|
97
|
+
$stderr.puts error, *(error.backtrace)
|
98
|
+
$stderr.puts "Couldn't create database for #{configuration.inspect}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_all
|
102
|
+
each_local_configuration { |configuration| create configuration }
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_current(environment = env)
|
106
|
+
each_current_configuration(environment) { |configuration|
|
107
|
+
create configuration
|
108
|
+
}
|
109
|
+
ActiveRecord::Base.establish_connection(environment.to_sym)
|
110
|
+
end
|
111
|
+
|
112
|
+
def drop(*arguments)
|
113
|
+
configuration = arguments.first
|
114
|
+
class_for_adapter(configuration['adapter']).new(*arguments).drop
|
115
|
+
rescue ActiveRecord::NoDatabaseError
|
116
|
+
$stderr.puts "Database '#{configuration['database']}' does not exist"
|
117
|
+
rescue Exception => error
|
118
|
+
$stderr.puts error, *(error.backtrace)
|
119
|
+
$stderr.puts "Couldn't drop #{configuration['database']}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def drop_all
|
123
|
+
each_local_configuration { |configuration| drop configuration }
|
124
|
+
end
|
125
|
+
|
126
|
+
def drop_current(environment = env)
|
127
|
+
each_current_configuration(environment) { |configuration|
|
128
|
+
drop configuration
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def migrate
|
133
|
+
verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
134
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
135
|
+
scope = ENV['SCOPE']
|
136
|
+
verbose_was, Migration.verbose = Migration.verbose, verbose
|
137
|
+
Migrator.migrate(Migrator.migrations_paths, version) do |migration|
|
138
|
+
scope.blank? || scope == migration.scope
|
139
|
+
end
|
140
|
+
ensure
|
141
|
+
Migration.verbose = verbose_was
|
142
|
+
end
|
143
|
+
|
144
|
+
def charset_current(environment = env)
|
145
|
+
charset ActiveRecord::Base.configurations[environment]
|
146
|
+
end
|
147
|
+
|
148
|
+
def charset(*arguments)
|
149
|
+
configuration = arguments.first
|
150
|
+
class_for_adapter(configuration['adapter']).new(*arguments).charset
|
151
|
+
end
|
152
|
+
|
153
|
+
def collation_current(environment = env)
|
154
|
+
collation ActiveRecord::Base.configurations[environment]
|
155
|
+
end
|
156
|
+
|
157
|
+
def collation(*arguments)
|
158
|
+
configuration = arguments.first
|
159
|
+
class_for_adapter(configuration['adapter']).new(*arguments).collation
|
160
|
+
end
|
161
|
+
|
162
|
+
def purge(configuration)
|
163
|
+
class_for_adapter(configuration['adapter']).new(configuration).purge
|
164
|
+
end
|
165
|
+
|
166
|
+
def purge_all
|
167
|
+
each_local_configuration { |configuration|
|
168
|
+
purge configuration
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
def purge_current(environment = env)
|
173
|
+
each_current_configuration(environment) { |configuration|
|
174
|
+
purge configuration
|
175
|
+
}
|
176
|
+
ActiveRecord::Base.establish_connection(environment.to_sym)
|
177
|
+
end
|
178
|
+
|
179
|
+
def structure_dump(*arguments)
|
180
|
+
configuration = arguments.first
|
181
|
+
filename = arguments.delete_at 1
|
182
|
+
class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
|
183
|
+
end
|
184
|
+
|
185
|
+
def structure_load(*arguments)
|
186
|
+
configuration = arguments.first
|
187
|
+
filename = arguments.delete_at 1
|
188
|
+
class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
|
189
|
+
end
|
190
|
+
|
191
|
+
def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
|
192
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
193
|
+
This method will act on a specific connection in the future.
|
194
|
+
To act on the current connection, use `load_schema_current` instead.
|
195
|
+
MSG
|
196
|
+
|
197
|
+
load_schema_current(format, file)
|
198
|
+
end
|
199
|
+
|
200
|
+
def schema_file(format = ActiveSupport::Base.schema_format)
|
201
|
+
case format
|
202
|
+
when :ruby
|
203
|
+
File.join(db_dir, "schema.rb")
|
204
|
+
when :sql
|
205
|
+
File.join(db_dir, "structure.sql")
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# This method is the successor of +load_schema+. We should rename it
|
210
|
+
# after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
|
211
|
+
def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
|
212
|
+
file ||= schema_file(format)
|
213
|
+
|
214
|
+
case format
|
215
|
+
when :ruby
|
216
|
+
check_schema_file(file)
|
217
|
+
ActiveRecord::Base.establish_connection(configuration)
|
218
|
+
load(file)
|
219
|
+
when :sql
|
220
|
+
check_schema_file(file)
|
221
|
+
structure_load(configuration, file)
|
222
|
+
else
|
223
|
+
raise ArgumentError, "unknown format #{format.inspect}"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
228
|
+
if File.exist?(file || schema_file(format))
|
229
|
+
load_schema_current(format, file, environment)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
234
|
+
each_current_configuration(environment) { |configuration|
|
235
|
+
load_schema_for configuration, format, file
|
236
|
+
}
|
237
|
+
ActiveRecord::Base.establish_connection(environment.to_sym)
|
238
|
+
end
|
239
|
+
|
240
|
+
def check_schema_file(filename)
|
241
|
+
unless File.exist?(filename)
|
242
|
+
message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
|
243
|
+
message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails)
|
244
|
+
Kernel.abort message
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def load_seed
|
249
|
+
if seed_loader
|
250
|
+
seed_loader.load_seed
|
251
|
+
else
|
252
|
+
raise "You tried to load seed data, but no seed loader is specified. Please specify seed " +
|
253
|
+
"loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" +
|
254
|
+
"Seed loader should respond to load_seed method"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
def class_for_adapter(adapter)
|
261
|
+
key = @tasks.keys.detect { |pattern| adapter[pattern] }
|
262
|
+
unless key
|
263
|
+
raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
|
264
|
+
end
|
265
|
+
@tasks[key]
|
266
|
+
end
|
267
|
+
|
268
|
+
def each_current_configuration(environment)
|
269
|
+
environments = [environment]
|
270
|
+
# add test environment only if no RAILS_ENV was specified.
|
271
|
+
environments << 'test' if environment == 'development' && ENV['RAILS_ENV'].nil?
|
272
|
+
|
273
|
+
configurations = ActiveRecord::Base.configurations.values_at(*environments)
|
274
|
+
configurations.compact.each do |configuration|
|
275
|
+
yield configuration unless configuration['database'].blank?
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def each_local_configuration
|
280
|
+
ActiveRecord::Base.configurations.each_value do |configuration|
|
281
|
+
next unless configuration['database']
|
282
|
+
|
283
|
+
if local_database?(configuration)
|
284
|
+
yield configuration
|
285
|
+
else
|
286
|
+
$stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def local_database?(configuration)
|
292
|
+
configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|