sskirby-activerecord 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +6749 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +222 -0
- data/examples/associations.png +0 -0
- data/examples/performance.rb +177 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +147 -0
- data/lib/active_record/aggregations.rb +255 -0
- data/lib/active_record/associations.rb +1604 -0
- data/lib/active_record/associations/alias_tracker.rb +79 -0
- data/lib/active_record/associations/association.rb +239 -0
- data/lib/active_record/associations/association_scope.rb +119 -0
- data/lib/active_record/associations/belongs_to_association.rb +79 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -0
- data/lib/active_record/associations/builder/association.rb +55 -0
- data/lib/active_record/associations/builder/belongs_to.rb +85 -0
- data/lib/active_record/associations/builder/collection_association.rb +75 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
- data/lib/active_record/associations/builder/has_many.rb +71 -0
- data/lib/active_record/associations/builder/has_one.rb +62 -0
- data/lib/active_record/associations/builder/singular_association.rb +32 -0
- data/lib/active_record/associations/collection_association.rb +574 -0
- data/lib/active_record/associations/collection_proxy.rb +132 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +62 -0
- data/lib/active_record/associations/has_many_association.rb +108 -0
- data/lib/active_record/associations/has_many_through_association.rb +180 -0
- data/lib/active_record/associations/has_one_association.rb +73 -0
- data/lib/active_record/associations/has_one_through_association.rb +36 -0
- data/lib/active_record/associations/join_dependency.rb +214 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +154 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
- data/lib/active_record/associations/join_helper.rb +55 -0
- data/lib/active_record/associations/preloader.rb +177 -0
- data/lib/active_record/associations/preloader/association.rb +127 -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_and_belongs_to_many.rb +60 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +15 -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 +67 -0
- data/lib/active_record/associations/singular_association.rb +64 -0
- data/lib/active_record/associations/through_association.rb +83 -0
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods.rb +272 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +101 -0
- data/lib/active_record/attribute_methods/primary_key.rb +114 -0
- data/lib/active_record/attribute_methods/query.rb +39 -0
- data/lib/active_record/attribute_methods/read.rb +135 -0
- data/lib/active_record/attribute_methods/serialization.rb +93 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -0
- data/lib/active_record/attribute_methods/write.rb +69 -0
- data/lib/active_record/autosave_association.rb +422 -0
- data/lib/active_record/base.rb +716 -0
- data/lib/active_record/callbacks.rb +275 -0
- data/lib/active_record/coders/yaml_column.rb +41 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +188 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +58 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +388 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +115 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +492 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +598 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +296 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
- data/lib/active_record/connection_adapters/column.rb +270 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +288 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +426 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1261 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +577 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/counter_cache.rb +119 -0
- data/lib/active_record/dynamic_finder_match.rb +56 -0
- data/lib/active_record/dynamic_matchers.rb +79 -0
- data/lib/active_record/dynamic_scope_match.rb +23 -0
- data/lib/active_record/errors.rb +195 -0
- data/lib/active_record/explain.rb +85 -0
- data/lib/active_record/explain_subscriber.rb +21 -0
- data/lib/active_record/fixtures.rb +906 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/identity_map.rb +156 -0
- data/lib/active_record/inheritance.rb +167 -0
- data/lib/active_record/integration.rb +49 -0
- data/lib/active_record/locale/en.yml +40 -0
- data/lib/active_record/locking/optimistic.rb +183 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/log_subscriber.rb +68 -0
- data/lib/active_record/migration.rb +765 -0
- data/lib/active_record/migration/command_recorder.rb +105 -0
- data/lib/active_record/model_schema.rb +366 -0
- data/lib/active_record/nested_attributes.rb +469 -0
- data/lib/active_record/observer.rb +121 -0
- data/lib/active_record/persistence.rb +372 -0
- data/lib/active_record/query_cache.rb +74 -0
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +119 -0
- data/lib/active_record/railties/console_sandbox.rb +6 -0
- data/lib/active_record/railties/controller_runtime.rb +49 -0
- data/lib/active_record/railties/databases.rake +620 -0
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +534 -0
- data/lib/active_record/relation.rb +534 -0
- data/lib/active_record/relation/batches.rb +90 -0
- data/lib/active_record/relation/calculations.rb +354 -0
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +398 -0
- data/lib/active_record/relation/predicate_builder.rb +58 -0
- data/lib/active_record/relation/query_methods.rb +417 -0
- data/lib/active_record/relation/spawn_methods.rb +148 -0
- data/lib/active_record/result.rb +34 -0
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema.rb +58 -0
- data/lib/active_record/schema_dumper.rb +204 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/serialization.rb +18 -0
- data/lib/active_record/serializers/xml_serializer.rb +202 -0
- data/lib/active_record/session_store.rb +358 -0
- data/lib/active_record/store.rb +50 -0
- data/lib/active_record/test_case.rb +73 -0
- data/lib/active_record/timestamp.rb +113 -0
- data/lib/active_record/transactions.rb +360 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations.rb +83 -0
- data/lib/active_record/validations/associated.rb +43 -0
- data/lib/active_record/validations/uniqueness.rb +180 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/rails/generators/active_record.rb +25 -0
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +31 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +43 -0
- data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
- data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
- metadata +242 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
# = Active Record Query Cache
|
|
5
|
+
class QueryCache
|
|
6
|
+
module ClassMethods
|
|
7
|
+
# Enable the query cache within the block if Active Record is configured.
|
|
8
|
+
def cache(&block)
|
|
9
|
+
if ActiveRecord::Base.configurations.blank?
|
|
10
|
+
yield
|
|
11
|
+
else
|
|
12
|
+
connection.cache(&block)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Disable the query cache within the block if Active Record is configured.
|
|
17
|
+
def uncached(&block)
|
|
18
|
+
if ActiveRecord::Base.configurations.blank?
|
|
19
|
+
yield
|
|
20
|
+
else
|
|
21
|
+
connection.uncached(&block)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize(app)
|
|
27
|
+
@app = app
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class BodyProxy # :nodoc:
|
|
31
|
+
def initialize(original_cache_value, target, connection_id)
|
|
32
|
+
@original_cache_value = original_cache_value
|
|
33
|
+
@target = target
|
|
34
|
+
@connection_id = connection_id
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def method_missing(method_sym, *arguments, &block)
|
|
38
|
+
@target.send(method_sym, *arguments, &block)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def respond_to?(method_sym, include_private = false)
|
|
42
|
+
super || @target.respond_to?(method_sym)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def each(&block)
|
|
46
|
+
@target.each(&block)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def close
|
|
50
|
+
@target.close if @target.respond_to?(:close)
|
|
51
|
+
ensure
|
|
52
|
+
ActiveRecord::Base.connection_id = @connection_id
|
|
53
|
+
ActiveRecord::Base.connection.clear_query_cache
|
|
54
|
+
unless @original_cache_value
|
|
55
|
+
ActiveRecord::Base.connection.disable_query_cache!
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def call(env)
|
|
61
|
+
old = ActiveRecord::Base.connection.query_cache_enabled
|
|
62
|
+
ActiveRecord::Base.connection.enable_query_cache!
|
|
63
|
+
|
|
64
|
+
status, headers, body = @app.call(env)
|
|
65
|
+
[status, headers, BodyProxy.new(old, body, ActiveRecord::Base.connection_id)]
|
|
66
|
+
rescue Exception => e
|
|
67
|
+
ActiveRecord::Base.connection.clear_query_cache
|
|
68
|
+
unless old
|
|
69
|
+
ActiveRecord::Base.connection.disable_query_cache!
|
|
70
|
+
end
|
|
71
|
+
raise e
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Querying
|
|
5
|
+
delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
|
|
6
|
+
delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :scoped
|
|
7
|
+
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
|
|
8
|
+
delegate :find_each, :find_in_batches, :to => :scoped
|
|
9
|
+
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
|
|
10
|
+
:where, :preload, :eager_load, :includes, :from, :lock, :readonly,
|
|
11
|
+
:having, :create_with, :uniq, :to => :scoped
|
|
12
|
+
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :to => :scoped
|
|
13
|
+
|
|
14
|
+
# Executes a custom SQL query against your database and returns all the results. The results will
|
|
15
|
+
# be returned as an array with columns requested encapsulated as attributes of the model you call
|
|
16
|
+
# this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
|
|
17
|
+
# a Product object with the attributes you specified in the SQL query.
|
|
18
|
+
#
|
|
19
|
+
# If you call a complicated SQL query which spans multiple tables the columns specified by the
|
|
20
|
+
# SELECT will be attributes of the model, whether or not they are columns of the corresponding
|
|
21
|
+
# table.
|
|
22
|
+
#
|
|
23
|
+
# The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
|
|
24
|
+
# no database agnostic conversions performed. This should be a last resort because using, for example,
|
|
25
|
+
# MySQL specific terms will lock you to using that particular database engine or require you to
|
|
26
|
+
# change your call if you switch engines.
|
|
27
|
+
#
|
|
28
|
+
# ==== Examples
|
|
29
|
+
# # A simple SQL query spanning multiple tables
|
|
30
|
+
# Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
|
|
31
|
+
# > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
|
|
32
|
+
#
|
|
33
|
+
# # You can use the same string replacement techniques as you can with ActiveRecord#find
|
|
34
|
+
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
|
|
35
|
+
# > [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
|
|
36
|
+
def find_by_sql(sql, binds = [])
|
|
37
|
+
logging_query_plan do
|
|
38
|
+
connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
|
|
43
|
+
# The use of this method should be restricted to complicated SQL queries that can't be executed
|
|
44
|
+
# using the ActiveRecord::Calculations class methods. Look into those before using this.
|
|
45
|
+
#
|
|
46
|
+
# ==== Parameters
|
|
47
|
+
#
|
|
48
|
+
# * +sql+ - An SQL statement which should return a count query from the database, see the example below.
|
|
49
|
+
#
|
|
50
|
+
# ==== Examples
|
|
51
|
+
#
|
|
52
|
+
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
|
|
53
|
+
def count_by_sql(sql)
|
|
54
|
+
sql = sanitize_conditions(sql)
|
|
55
|
+
connection.select_value(sql, "#{name} Count").to_i
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require "active_record"
|
|
2
|
+
require "rails"
|
|
3
|
+
require "active_model/railtie"
|
|
4
|
+
|
|
5
|
+
# For now, action_controller must always be present with
|
|
6
|
+
# rails, so let's make sure that it gets required before
|
|
7
|
+
# here. This is needed for correctly setting up the middleware.
|
|
8
|
+
# In the future, this might become an optional require.
|
|
9
|
+
require "action_controller/railtie"
|
|
10
|
+
|
|
11
|
+
module ActiveRecord
|
|
12
|
+
# = Active Record Railtie
|
|
13
|
+
class Railtie < Rails::Railtie
|
|
14
|
+
config.active_record = ActiveSupport::OrderedOptions.new
|
|
15
|
+
|
|
16
|
+
config.app_generators.orm :active_record, :migration => true,
|
|
17
|
+
:timestamps => true
|
|
18
|
+
|
|
19
|
+
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
|
|
20
|
+
"ActiveRecord::QueryCache"
|
|
21
|
+
|
|
22
|
+
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
|
|
23
|
+
"ActiveRecord::ConnectionAdapters::ConnectionManagement"
|
|
24
|
+
|
|
25
|
+
config.action_dispatch.rescue_responses.merge!(
|
|
26
|
+
'ActiveRecord::RecordNotFound' => :not_found,
|
|
27
|
+
'ActiveRecord::StaleObjectError' => :conflict,
|
|
28
|
+
'ActiveRecord::RecordInvalid' => :unprocessable_entity,
|
|
29
|
+
'ActiveRecord::RecordNotSaved' => :unprocessable_entity
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
rake_tasks do
|
|
33
|
+
load "active_record/railties/databases.rake"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# When loading console, force ActiveRecord::Base to be loaded
|
|
37
|
+
# to avoid cross references when loading a constant for the
|
|
38
|
+
# first time. Also, make it output to STDERR.
|
|
39
|
+
console do |app|
|
|
40
|
+
require "active_record/railties/console_sandbox" if app.sandbox?
|
|
41
|
+
ActiveRecord::Base.logger = Logger.new(STDERR)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
initializer "active_record.initialize_timezone" do
|
|
45
|
+
ActiveSupport.on_load(:active_record) do
|
|
46
|
+
self.time_zone_aware_attributes = true
|
|
47
|
+
self.default_timezone = :utc
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
initializer "active_record.logger" do
|
|
52
|
+
ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
initializer "active_record.identity_map" do |app|
|
|
56
|
+
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
|
|
57
|
+
"ActiveRecord::IdentityMap::Middleware" if config.active_record.delete(:identity_map)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
initializer "active_record.set_configs" do |app|
|
|
61
|
+
ActiveSupport.on_load(:active_record) do
|
|
62
|
+
if app.config.active_record.delete(:whitelist_attributes)
|
|
63
|
+
attr_accessible(nil)
|
|
64
|
+
end
|
|
65
|
+
app.config.active_record.each do |k,v|
|
|
66
|
+
send "#{k}=", v
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# This sets the database configuration from Configuration#database_configuration
|
|
72
|
+
# and then establishes the connection.
|
|
73
|
+
initializer "active_record.initialize_database" do |app|
|
|
74
|
+
ActiveSupport.on_load(:active_record) do
|
|
75
|
+
self.configurations = app.config.database_configuration
|
|
76
|
+
establish_connection
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Expose database runtime to controller for logging.
|
|
81
|
+
initializer "active_record.log_runtime" do |app|
|
|
82
|
+
require "active_record/railties/controller_runtime"
|
|
83
|
+
ActiveSupport.on_load(:action_controller) do
|
|
84
|
+
include ActiveRecord::Railties::ControllerRuntime
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
initializer "active_record.set_reloader_hooks" do |app|
|
|
89
|
+
hook = lambda do
|
|
90
|
+
ActiveRecord::Base.clear_reloadable_connections!
|
|
91
|
+
ActiveRecord::Base.clear_cache!
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if app.config.reload_classes_only_on_change
|
|
95
|
+
ActiveSupport.on_load(:active_record) do
|
|
96
|
+
ActionDispatch::Reloader.to_prepare(&hook)
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
ActiveSupport.on_load(:active_record) do
|
|
100
|
+
ActionDispatch::Reloader.to_cleanup(&hook)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
initializer "active_record.add_watchable_files" do |app|
|
|
106
|
+
config.watchable_files.concat ["#{app.root}/db/schema.rb", "#{app.root}/db/structure.sql"]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
config.after_initialize do
|
|
110
|
+
ActiveSupport.on_load(:active_record) do
|
|
111
|
+
instantiate_observers
|
|
112
|
+
|
|
113
|
+
ActionDispatch::Reloader.to_prepare do
|
|
114
|
+
ActiveRecord::Base.instantiate_observers
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'active_support/core_ext/module/attr_internal'
|
|
2
|
+
require 'active_record/log_subscriber'
|
|
3
|
+
|
|
4
|
+
module ActiveRecord
|
|
5
|
+
module Railties
|
|
6
|
+
module ControllerRuntime #:nodoc:
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
protected
|
|
10
|
+
|
|
11
|
+
attr_internal :db_runtime
|
|
12
|
+
|
|
13
|
+
def process_action(action, *args)
|
|
14
|
+
# We also need to reset the runtime before each action
|
|
15
|
+
# because of queries in middleware or in cases we are streaming
|
|
16
|
+
# and it won't be cleaned up by the method below.
|
|
17
|
+
ActiveRecord::LogSubscriber.reset_runtime
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def cleanup_view_runtime
|
|
22
|
+
if ActiveRecord::Base.connected?
|
|
23
|
+
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
|
|
24
|
+
runtime = super
|
|
25
|
+
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
|
|
26
|
+
self.db_runtime = db_rt_before_render + db_rt_after_render
|
|
27
|
+
runtime - db_rt_after_render
|
|
28
|
+
else
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def append_info_to_payload(payload)
|
|
34
|
+
super
|
|
35
|
+
if ActiveRecord::Base.connected?
|
|
36
|
+
payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module ClassMethods
|
|
41
|
+
def log_process_action(payload)
|
|
42
|
+
messages, db_runtime = super, payload[:db_runtime]
|
|
43
|
+
messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime
|
|
44
|
+
messages
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
require 'active_support/core_ext/object/inclusion'
|
|
2
|
+
require 'active_record'
|
|
3
|
+
|
|
4
|
+
db_namespace = namespace :db do
|
|
5
|
+
task :load_config => :rails_env do
|
|
6
|
+
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
|
|
7
|
+
ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
|
|
8
|
+
|
|
9
|
+
if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
|
|
10
|
+
if engine.paths['db/migrate'].existent
|
|
11
|
+
ActiveRecord::Migrator.migrations_paths += engine.paths['db/migrate'].to_a
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
namespace :create do
|
|
17
|
+
# desc 'Create all the local databases defined in config/database.yml'
|
|
18
|
+
task :all => :load_config do
|
|
19
|
+
ActiveRecord::Base.configurations.each_value do |config|
|
|
20
|
+
# Skip entries that don't have a database key, such as the first entry here:
|
|
21
|
+
#
|
|
22
|
+
# defaults: &defaults
|
|
23
|
+
# adapter: mysql
|
|
24
|
+
# username: root
|
|
25
|
+
# password:
|
|
26
|
+
# host: localhost
|
|
27
|
+
#
|
|
28
|
+
# development:
|
|
29
|
+
# database: blog_development
|
|
30
|
+
# *defaults
|
|
31
|
+
next unless config['database']
|
|
32
|
+
# Only connect to local databases
|
|
33
|
+
local_database?(config) { create_database(config) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)'
|
|
39
|
+
task :create => :load_config do
|
|
40
|
+
configs_for_environment.each { |config| create_database(config) }
|
|
41
|
+
ActiveRecord::Base.establish_connection(configs_for_environment.first)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def mysql_creation_options(config)
|
|
45
|
+
@charset = ENV['CHARSET'] || 'utf8'
|
|
46
|
+
@collation = ENV['COLLATION'] || 'utf8_unicode_ci'
|
|
47
|
+
{:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def create_database(config)
|
|
51
|
+
begin
|
|
52
|
+
if config['adapter'] =~ /sqlite/
|
|
53
|
+
if File.exist?(config['database'])
|
|
54
|
+
$stderr.puts "#{config['database']} already exists"
|
|
55
|
+
else
|
|
56
|
+
begin
|
|
57
|
+
# Create the SQLite database
|
|
58
|
+
ActiveRecord::Base.establish_connection(config)
|
|
59
|
+
ActiveRecord::Base.connection
|
|
60
|
+
rescue Exception => e
|
|
61
|
+
$stderr.puts e, *(e.backtrace)
|
|
62
|
+
$stderr.puts "Couldn't create database for #{config.inspect}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
return # Skip the else clause of begin/rescue
|
|
66
|
+
else
|
|
67
|
+
ActiveRecord::Base.establish_connection(config)
|
|
68
|
+
ActiveRecord::Base.connection
|
|
69
|
+
end
|
|
70
|
+
rescue
|
|
71
|
+
case config['adapter']
|
|
72
|
+
when /mysql/
|
|
73
|
+
if config['adapter'] =~ /jdbc/
|
|
74
|
+
#FIXME After Jdbcmysql gives this class
|
|
75
|
+
require 'active_record/railties/jdbcmysql_error'
|
|
76
|
+
error_class = ArJdbcMySQL::Error
|
|
77
|
+
else
|
|
78
|
+
error_class = config['adapter'] =~ /mysql2/ ? Mysql2::Error : Mysql::Error
|
|
79
|
+
end
|
|
80
|
+
access_denied_error = 1045
|
|
81
|
+
begin
|
|
82
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => nil))
|
|
83
|
+
ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
|
|
84
|
+
ActiveRecord::Base.establish_connection(config)
|
|
85
|
+
rescue error_class => sqlerr
|
|
86
|
+
if sqlerr.errno == access_denied_error
|
|
87
|
+
print "#{sqlerr.error}. \nPlease provide the root password for your mysql installation\n>"
|
|
88
|
+
root_password = $stdin.gets.strip
|
|
89
|
+
grant_statement = "GRANT ALL PRIVILEGES ON #{config['database']}.* " \
|
|
90
|
+
"TO '#{config['username']}'@'localhost' " \
|
|
91
|
+
"IDENTIFIED BY '#{config['password']}' WITH GRANT OPTION;"
|
|
92
|
+
ActiveRecord::Base.establish_connection(config.merge(
|
|
93
|
+
'database' => nil, 'username' => 'root', 'password' => root_password))
|
|
94
|
+
ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
|
|
95
|
+
ActiveRecord::Base.connection.execute grant_statement
|
|
96
|
+
ActiveRecord::Base.establish_connection(config)
|
|
97
|
+
else
|
|
98
|
+
$stderr.puts sqlerr.error
|
|
99
|
+
$stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation}"
|
|
100
|
+
$stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['charset']
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
when /postgresql/
|
|
104
|
+
@encoding = config['encoding'] || ENV['CHARSET'] || 'utf8'
|
|
105
|
+
begin
|
|
106
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
|
|
107
|
+
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
|
|
108
|
+
ActiveRecord::Base.establish_connection(config)
|
|
109
|
+
rescue Exception => e
|
|
110
|
+
$stderr.puts e, *(e.backtrace)
|
|
111
|
+
$stderr.puts "Couldn't create database for #{config.inspect}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
else
|
|
115
|
+
# Bug with 1.9.2 Calling return within begin still executes else
|
|
116
|
+
$stderr.puts "#{config['database']} already exists" unless config['adapter'] =~ /sqlite/
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
namespace :drop do
|
|
121
|
+
# desc 'Drops all the local databases defined in config/database.yml'
|
|
122
|
+
task :all => :load_config do
|
|
123
|
+
ActiveRecord::Base.configurations.each_value do |config|
|
|
124
|
+
# Skip entries that don't have a database key
|
|
125
|
+
next unless config['database']
|
|
126
|
+
begin
|
|
127
|
+
# Only connect to local databases
|
|
128
|
+
local_database?(config) { drop_database(config) }
|
|
129
|
+
rescue Exception => e
|
|
130
|
+
$stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)'
|
|
137
|
+
task :drop => :load_config do
|
|
138
|
+
configs_for_environment.each { |config| drop_database_and_rescue(config) }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def local_database?(config, &block)
|
|
142
|
+
if config['host'].in?(['127.0.0.1', 'localhost']) || config['host'].blank?
|
|
143
|
+
yield
|
|
144
|
+
else
|
|
145
|
+
$stderr.puts "This task only modifies local databases. #{config['database']} is on a remote host."
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
|
|
151
|
+
task :migrate => [:environment, :load_config] do
|
|
152
|
+
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
|
153
|
+
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
|
|
154
|
+
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
|
|
155
|
+
end
|
|
156
|
+
db_namespace['_dump'].invoke
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
task :_dump do
|
|
160
|
+
case ActiveRecord::Base.schema_format
|
|
161
|
+
when :ruby then db_namespace["schema:dump"].invoke
|
|
162
|
+
when :sql then db_namespace["structure:dump"].invoke
|
|
163
|
+
else
|
|
164
|
+
raise "unknown schema format #{ActiveRecord::Base.schema_format}"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
namespace :migrate do
|
|
169
|
+
# desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
|
|
170
|
+
task :redo => [:environment, :load_config] do
|
|
171
|
+
if ENV['VERSION']
|
|
172
|
+
db_namespace['migrate:down'].invoke
|
|
173
|
+
db_namespace['migrate:up'].invoke
|
|
174
|
+
else
|
|
175
|
+
db_namespace['rollback'].invoke
|
|
176
|
+
db_namespace['migrate'].invoke
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# desc 'Resets your database using your migrations for the current environment'
|
|
181
|
+
task :reset => ['db:drop', 'db:create', 'db:migrate']
|
|
182
|
+
|
|
183
|
+
# desc 'Runs the "up" for a given migration VERSION.'
|
|
184
|
+
task :up => [:environment, :load_config] do
|
|
185
|
+
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
|
186
|
+
raise 'VERSION is required' unless version
|
|
187
|
+
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
|
|
188
|
+
db_namespace['_dump'].invoke
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# desc 'Runs the "down" for a given migration VERSION.'
|
|
192
|
+
task :down => [:environment, :load_config] do
|
|
193
|
+
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
|
194
|
+
raise 'VERSION is required' unless version
|
|
195
|
+
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
|
|
196
|
+
db_namespace['_dump'].invoke
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
desc 'Display status of migrations'
|
|
200
|
+
task :status => [:environment, :load_config] do
|
|
201
|
+
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
|
202
|
+
ActiveRecord::Base.establish_connection(config)
|
|
203
|
+
unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
|
|
204
|
+
puts 'Schema migrations table does not exist yet.'
|
|
205
|
+
next # means "return" for rake task
|
|
206
|
+
end
|
|
207
|
+
db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
|
|
208
|
+
file_list = []
|
|
209
|
+
ActiveRecord::Migrator.migrations_paths.each do |path|
|
|
210
|
+
Dir.foreach(path) do |file|
|
|
211
|
+
# only files matching "20091231235959_some_name.rb" pattern
|
|
212
|
+
if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
|
|
213
|
+
status = db_list.delete(match_data[1]) ? 'up' : 'down'
|
|
214
|
+
file_list << [status, match_data[1], match_data[2].humanize]
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
db_list.map! do |version|
|
|
219
|
+
['up', version, '********** NO FILE **********']
|
|
220
|
+
end
|
|
221
|
+
# output
|
|
222
|
+
puts "\ndatabase: #{config['database']}\n\n"
|
|
223
|
+
puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
|
|
224
|
+
puts "-" * 50
|
|
225
|
+
(db_list + file_list).sort_by {|migration| migration[1]}.each do |migration|
|
|
226
|
+
puts "#{migration[0].center(8)} #{migration[1].ljust(14)} #{migration[2]}"
|
|
227
|
+
end
|
|
228
|
+
puts
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
|
233
|
+
task :rollback => [:environment, :load_config] do
|
|
234
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
|
235
|
+
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
|
|
236
|
+
db_namespace['_dump'].invoke
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
|
|
240
|
+
task :forward => [:environment, :load_config] do
|
|
241
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
|
242
|
+
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
|
|
243
|
+
db_namespace['_dump'].invoke
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
|
|
247
|
+
task :reset => :environment do
|
|
248
|
+
db_namespace["drop"].invoke
|
|
249
|
+
db_namespace["setup"].invoke
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# desc "Retrieves the charset for the current environment's database"
|
|
253
|
+
task :charset => :environment do
|
|
254
|
+
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
|
255
|
+
case config['adapter']
|
|
256
|
+
when /mysql/
|
|
257
|
+
ActiveRecord::Base.establish_connection(config)
|
|
258
|
+
puts ActiveRecord::Base.connection.charset
|
|
259
|
+
when /postgresql/
|
|
260
|
+
ActiveRecord::Base.establish_connection(config)
|
|
261
|
+
puts ActiveRecord::Base.connection.encoding
|
|
262
|
+
when /sqlite/
|
|
263
|
+
ActiveRecord::Base.establish_connection(config)
|
|
264
|
+
puts ActiveRecord::Base.connection.encoding
|
|
265
|
+
else
|
|
266
|
+
$stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# desc "Retrieves the collation for the current environment's database"
|
|
271
|
+
task :collation => :environment do
|
|
272
|
+
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
|
273
|
+
case config['adapter']
|
|
274
|
+
when /mysql/
|
|
275
|
+
ActiveRecord::Base.establish_connection(config)
|
|
276
|
+
puts ActiveRecord::Base.connection.collation
|
|
277
|
+
else
|
|
278
|
+
$stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
desc 'Retrieves the current schema version number'
|
|
283
|
+
task :version => :environment do
|
|
284
|
+
puts "Current version: #{ActiveRecord::Migrator.current_version}"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# desc "Raises an error if there are pending migrations"
|
|
288
|
+
task :abort_if_pending_migrations => :environment do
|
|
289
|
+
pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
|
|
290
|
+
|
|
291
|
+
if pending_migrations.any?
|
|
292
|
+
puts "You have #{pending_migrations.size} pending migrations:"
|
|
293
|
+
pending_migrations.each do |pending_migration|
|
|
294
|
+
puts ' %4d %s' % [pending_migration.version, pending_migration.name]
|
|
295
|
+
end
|
|
296
|
+
abort %{Run `rake db:migrate` to update your database then try again.}
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)'
|
|
301
|
+
task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed]
|
|
302
|
+
|
|
303
|
+
desc 'Load the seed data from db/seeds.rb'
|
|
304
|
+
task :seed do
|
|
305
|
+
db_namespace['abort_if_pending_migrations'].invoke
|
|
306
|
+
Rails.application.load_seed
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
namespace :fixtures do
|
|
310
|
+
desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
|
|
311
|
+
task :load => :environment do
|
|
312
|
+
require 'active_record/fixtures'
|
|
313
|
+
|
|
314
|
+
ActiveRecord::Base.establish_connection(Rails.env)
|
|
315
|
+
base_dir = File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten
|
|
316
|
+
fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact
|
|
317
|
+
|
|
318
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.{yml,csv}"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file|
|
|
319
|
+
ActiveRecord::Fixtures.create_fixtures(fixtures_dir, fixture_file)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
|
|
324
|
+
task :identify => :environment do
|
|
325
|
+
require 'active_record/fixtures'
|
|
326
|
+
|
|
327
|
+
label, id = ENV['LABEL'], ENV['ID']
|
|
328
|
+
raise 'LABEL or ID required' if label.blank? && id.blank?
|
|
329
|
+
|
|
330
|
+
puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::Fixtures.identify(label)}.) if label
|
|
331
|
+
|
|
332
|
+
base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures')
|
|
333
|
+
Dir["#{base_dir}/**/*.yml"].each do |file|
|
|
334
|
+
if data = YAML::load(ERB.new(IO.read(file)).result)
|
|
335
|
+
data.keys.each do |key|
|
|
336
|
+
key_id = ActiveRecord::Fixtures.identify(key)
|
|
337
|
+
|
|
338
|
+
if key == label || key_id == id.to_i
|
|
339
|
+
puts "#{file}: #{key} (#{key_id})"
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
namespace :schema do
|
|
348
|
+
desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR'
|
|
349
|
+
task :dump => [:environment, :load_config] do
|
|
350
|
+
require 'active_record/schema_dumper'
|
|
351
|
+
filename = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb"
|
|
352
|
+
File.open(filename, "w:utf-8") do |file|
|
|
353
|
+
ActiveRecord::Base.establish_connection(Rails.env)
|
|
354
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
|
355
|
+
end
|
|
356
|
+
db_namespace['schema:dump'].reenable
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
desc 'Load a schema.rb file into the database'
|
|
360
|
+
task :load => :environment do
|
|
361
|
+
file = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb"
|
|
362
|
+
if File.exists?(file)
|
|
363
|
+
load(file)
|
|
364
|
+
else
|
|
365
|
+
abort %{#{file} doesn't exist yet. Run `rake db:migrate` to create it then try again. 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}
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
task :load_if_ruby => 'db:create' do
|
|
370
|
+
db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
namespace :structure do
|
|
375
|
+
desc 'Dump the database structure to db/structure.sql. Specify another file with DB_STRUCTURE=db/my_structure.sql'
|
|
376
|
+
task :dump => :environment do
|
|
377
|
+
abcs = ActiveRecord::Base.configurations
|
|
378
|
+
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
|
|
379
|
+
case abcs[Rails.env]['adapter']
|
|
380
|
+
when /mysql/, 'oci', 'oracle'
|
|
381
|
+
ActiveRecord::Base.establish_connection(abcs[Rails.env])
|
|
382
|
+
File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
|
383
|
+
when /postgresql/
|
|
384
|
+
set_psql_env(abcs[Rails.env])
|
|
385
|
+
search_path = abcs[Rails.env]['schema_search_path']
|
|
386
|
+
unless search_path.blank?
|
|
387
|
+
search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ")
|
|
388
|
+
end
|
|
389
|
+
`pg_dump -i -s -x -O -f #{filename} #{search_path} #{abcs[Rails.env]['database']}`
|
|
390
|
+
raise 'Error dumping database' if $?.exitstatus == 1
|
|
391
|
+
when /sqlite/
|
|
392
|
+
dbfile = abcs[Rails.env]['database']
|
|
393
|
+
`sqlite3 #{dbfile} .schema > #{filename}`
|
|
394
|
+
when 'sqlserver'
|
|
395
|
+
`smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f #{filename} -A -U`
|
|
396
|
+
when "firebird"
|
|
397
|
+
set_firebird_env(abcs[Rails.env])
|
|
398
|
+
db_string = firebird_db_string(abcs[Rails.env])
|
|
399
|
+
sh "isql -a #{db_string} > #{filename}"
|
|
400
|
+
else
|
|
401
|
+
raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
if ActiveRecord::Base.connection.supports_migrations?
|
|
405
|
+
File.open(filename, "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# desc "Recreate the databases from the structure.sql file"
|
|
410
|
+
task :load => [:environment, :load_config] do
|
|
411
|
+
env = ENV['RAILS_ENV'] || 'test'
|
|
412
|
+
|
|
413
|
+
abcs = ActiveRecord::Base.configurations
|
|
414
|
+
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
|
|
415
|
+
case abcs[env]['adapter']
|
|
416
|
+
when /mysql/
|
|
417
|
+
ActiveRecord::Base.establish_connection(abcs[env])
|
|
418
|
+
ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
|
|
419
|
+
IO.read(filename).split("\n\n").each do |table|
|
|
420
|
+
ActiveRecord::Base.connection.execute(table)
|
|
421
|
+
end
|
|
422
|
+
when /postgresql/
|
|
423
|
+
set_psql_env(abcs[env])
|
|
424
|
+
`psql -f "#{filename}" #{abcs[env]['database']}`
|
|
425
|
+
when /sqlite/
|
|
426
|
+
dbfile = abcs[env]['database']
|
|
427
|
+
`sqlite3 #{dbfile} < "#{filename}"`
|
|
428
|
+
when 'sqlserver'
|
|
429
|
+
`sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i #{filename}`
|
|
430
|
+
when 'oci', 'oracle'
|
|
431
|
+
ActiveRecord::Base.establish_connection(abcs[env])
|
|
432
|
+
IO.read(filename).split(";\n\n").each do |ddl|
|
|
433
|
+
ActiveRecord::Base.connection.execute(ddl)
|
|
434
|
+
end
|
|
435
|
+
when 'firebird'
|
|
436
|
+
set_firebird_env(abcs[env])
|
|
437
|
+
db_string = firebird_db_string(abcs[env])
|
|
438
|
+
sh "isql -i #{filename} #{db_string}"
|
|
439
|
+
else
|
|
440
|
+
raise "Task not supported by '#{abcs[env]['adapter']}'"
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
task :load_if_sql => 'db:create' do
|
|
445
|
+
db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
namespace :test do
|
|
450
|
+
|
|
451
|
+
# desc "Recreate the test database from the current schema"
|
|
452
|
+
task :load => 'db:test:purge' do
|
|
453
|
+
case ActiveRecord::Base.schema_format
|
|
454
|
+
when :ruby
|
|
455
|
+
db_namespace["test:load_schema"].invoke
|
|
456
|
+
when :sql
|
|
457
|
+
db_namespace["test:load_structure"].invoke
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
# desc "Recreate the test database from an existent structure.sql file"
|
|
462
|
+
task :load_structure => 'db:test:purge' do
|
|
463
|
+
begin
|
|
464
|
+
old_env, ENV['RAILS_ENV'] = ENV['RAILS_ENV'], 'test'
|
|
465
|
+
db_namespace["structure:load"].invoke
|
|
466
|
+
ensure
|
|
467
|
+
ENV['RAILS_ENV'] = old_env
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# desc "Recreate the test database from an existent schema.rb file"
|
|
472
|
+
task :load_schema => 'db:test:purge' do
|
|
473
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
|
474
|
+
ActiveRecord::Schema.verbose = false
|
|
475
|
+
db_namespace["schema:load"].invoke
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# desc "Recreate the test database from a fresh schema.rb file"
|
|
479
|
+
task :clone => %w(db:schema:dump db:test:load_schema)
|
|
480
|
+
|
|
481
|
+
# desc "Recreate the test database from a fresh structure.sql file"
|
|
482
|
+
task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ]
|
|
483
|
+
|
|
484
|
+
# desc "Empty the test database"
|
|
485
|
+
task :purge => :environment do
|
|
486
|
+
abcs = ActiveRecord::Base.configurations
|
|
487
|
+
case abcs['test']['adapter']
|
|
488
|
+
when /mysql/
|
|
489
|
+
ActiveRecord::Base.establish_connection(:test)
|
|
490
|
+
ActiveRecord::Base.connection.recreate_database(abcs['test']['database'], mysql_creation_options(abcs['test']))
|
|
491
|
+
when /postgresql/
|
|
492
|
+
ActiveRecord::Base.clear_active_connections!
|
|
493
|
+
drop_database(abcs['test'])
|
|
494
|
+
create_database(abcs['test'])
|
|
495
|
+
when /sqlite/
|
|
496
|
+
dbfile = abcs['test']['database']
|
|
497
|
+
File.delete(dbfile) if File.exist?(dbfile)
|
|
498
|
+
when 'sqlserver'
|
|
499
|
+
test = abcs.deep_dup['test']
|
|
500
|
+
test_database = test['database']
|
|
501
|
+
test['database'] = 'master'
|
|
502
|
+
ActiveRecord::Base.establish_connection(test)
|
|
503
|
+
ActiveRecord::Base.connection.recreate_database!(test_database)
|
|
504
|
+
when "oci", "oracle"
|
|
505
|
+
ActiveRecord::Base.establish_connection(:test)
|
|
506
|
+
ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
|
|
507
|
+
ActiveRecord::Base.connection.execute(ddl)
|
|
508
|
+
end
|
|
509
|
+
when 'firebird'
|
|
510
|
+
ActiveRecord::Base.establish_connection(:test)
|
|
511
|
+
ActiveRecord::Base.connection.recreate_database!
|
|
512
|
+
else
|
|
513
|
+
raise "Task not supported by '#{abcs['test']['adapter']}'"
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# desc 'Check for pending migrations and load the test schema'
|
|
518
|
+
task :prepare => 'db:abort_if_pending_migrations' do
|
|
519
|
+
unless ActiveRecord::Base.configurations.blank?
|
|
520
|
+
db_namespace[{ :sql => 'test:clone_structure', :ruby => 'test:load' }[ActiveRecord::Base.schema_format]].invoke
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
namespace :sessions do
|
|
526
|
+
# desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
|
|
527
|
+
task :create => :environment do
|
|
528
|
+
raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
|
|
529
|
+
Rails.application.load_generators
|
|
530
|
+
require 'rails/generators/rails/session_migration/session_migration_generator'
|
|
531
|
+
Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
# desc "Clear the sessions table"
|
|
535
|
+
task :clear => :environment do
|
|
536
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}"
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
namespace :railties do
|
|
542
|
+
namespace :install do
|
|
543
|
+
# desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
|
|
544
|
+
task :migrations => :'db:load_config' do
|
|
545
|
+
to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip }
|
|
546
|
+
railties = ActiveSupport::OrderedHash.new
|
|
547
|
+
Rails.application.railties.all do |railtie|
|
|
548
|
+
next unless to_load == :all || to_load.include?(railtie.railtie_name)
|
|
549
|
+
|
|
550
|
+
if railtie.respond_to?(:paths) && (path = railtie.paths['db/migrate'].first)
|
|
551
|
+
railties[railtie.railtie_name] = path
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
on_skip = Proc.new do |name, migration|
|
|
556
|
+
puts "NOTE: Migration #{migration.basename} from #{name} has been skipped. Migration with the same name already exists."
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
on_copy = Proc.new do |name, migration, old_path|
|
|
560
|
+
puts "Copied migration #{migration.basename} from #{name}"
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_paths.first, railties,
|
|
564
|
+
:on_skip => on_skip, :on_copy => on_copy)
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
task 'test:prepare' => 'db:test:prepare'
|
|
570
|
+
|
|
571
|
+
def drop_database(config)
|
|
572
|
+
case config['adapter']
|
|
573
|
+
when /mysql/
|
|
574
|
+
ActiveRecord::Base.establish_connection(config)
|
|
575
|
+
ActiveRecord::Base.connection.drop_database config['database']
|
|
576
|
+
when /sqlite/
|
|
577
|
+
require 'pathname'
|
|
578
|
+
path = Pathname.new(config['database'])
|
|
579
|
+
file = path.absolute? ? path.to_s : File.join(Rails.root, path)
|
|
580
|
+
|
|
581
|
+
FileUtils.rm(file)
|
|
582
|
+
when /postgresql/
|
|
583
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
|
|
584
|
+
ActiveRecord::Base.connection.drop_database config['database']
|
|
585
|
+
end
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def drop_database_and_rescue(config)
|
|
589
|
+
begin
|
|
590
|
+
drop_database(config)
|
|
591
|
+
rescue Exception => e
|
|
592
|
+
$stderr.puts "Couldn't drop #{config['database']} : #{e.inspect}"
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def configs_for_environment
|
|
597
|
+
environments = [Rails.env]
|
|
598
|
+
environments << 'test' if Rails.env.development?
|
|
599
|
+
ActiveRecord::Base.configurations.values_at(*environments).compact.reject { |config| config['database'].blank? }
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
def session_table_name
|
|
603
|
+
ActiveRecord::SessionStore::Session.table_name
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def set_firebird_env(config)
|
|
607
|
+
ENV['ISC_USER'] = config['username'].to_s if config['username']
|
|
608
|
+
ENV['ISC_PASSWORD'] = config['password'].to_s if config['password']
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def firebird_db_string(config)
|
|
612
|
+
FireRuby::Database.db_string_for(config.symbolize_keys)
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def set_psql_env(config)
|
|
616
|
+
ENV['PGHOST'] = config['host'] if config['host']
|
|
617
|
+
ENV['PGPORT'] = config['port'].to_s if config['port']
|
|
618
|
+
ENV['PGPASSWORD'] = config['password'].to_s if config['password']
|
|
619
|
+
ENV['PGUSER'] = config['username'].to_s if config['username']
|
|
620
|
+
end
|