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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,144 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Tasks # :nodoc:
|
3
|
+
class MySQLDatabaseTasks # :nodoc:
|
4
|
+
DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8'
|
5
|
+
DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci'
|
6
|
+
ACCESS_DENIED_ERROR = 1045
|
7
|
+
|
8
|
+
delegate :connection, :establish_connection, to: ActiveRecord::Base
|
9
|
+
|
10
|
+
def initialize(configuration)
|
11
|
+
@configuration = configuration
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
establish_connection configuration_without_database
|
16
|
+
connection.create_database configuration['database'], creation_options
|
17
|
+
establish_connection configuration
|
18
|
+
rescue ActiveRecord::StatementInvalid => error
|
19
|
+
if /database exists/ === error.message
|
20
|
+
raise DatabaseAlreadyExists
|
21
|
+
else
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
rescue error_class => error
|
25
|
+
if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
|
26
|
+
$stdout.print error.error
|
27
|
+
establish_connection root_configuration_without_database
|
28
|
+
connection.create_database configuration['database'], creation_options
|
29
|
+
if configuration['username'] != 'root'
|
30
|
+
connection.execute grant_statement.gsub(/\s+/, ' ').strip
|
31
|
+
end
|
32
|
+
establish_connection configuration
|
33
|
+
else
|
34
|
+
$stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
|
35
|
+
$stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def drop
|
40
|
+
establish_connection configuration
|
41
|
+
connection.drop_database configuration['database']
|
42
|
+
end
|
43
|
+
|
44
|
+
def purge
|
45
|
+
establish_connection :test
|
46
|
+
connection.recreate_database configuration['database'], creation_options
|
47
|
+
end
|
48
|
+
|
49
|
+
def charset
|
50
|
+
connection.charset
|
51
|
+
end
|
52
|
+
|
53
|
+
def collation
|
54
|
+
connection.collation
|
55
|
+
end
|
56
|
+
|
57
|
+
def structure_dump(filename)
|
58
|
+
args = prepare_command_options('mysqldump')
|
59
|
+
args.concat(["--result-file", "#{filename}"])
|
60
|
+
args.concat(["--no-data"])
|
61
|
+
args.concat(["#{configuration['database']}"])
|
62
|
+
unless Kernel.system(*args)
|
63
|
+
$stderr.puts "Could not dump the database structure. "\
|
64
|
+
"Make sure `mysqldump` is in your PATH and check the command output for warnings."
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def structure_load(filename)
|
69
|
+
args = prepare_command_options('mysql')
|
70
|
+
args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
|
71
|
+
args.concat(["--database", "#{configuration['database']}"])
|
72
|
+
Kernel.system(*args)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def configuration
|
78
|
+
@configuration
|
79
|
+
end
|
80
|
+
|
81
|
+
def configuration_without_database
|
82
|
+
configuration.merge('database' => nil)
|
83
|
+
end
|
84
|
+
|
85
|
+
def creation_options
|
86
|
+
Hash.new.tap do |options|
|
87
|
+
options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
|
88
|
+
options[:collation] = configuration['collation'] if configuration.include? 'collation'
|
89
|
+
|
90
|
+
# Set default charset only when collation isn't set.
|
91
|
+
options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
|
92
|
+
|
93
|
+
# Set default collation only when charset is also default.
|
94
|
+
options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def error_class
|
99
|
+
if configuration['adapter'] =~ /jdbc/
|
100
|
+
require 'active_record/railties/jdbcmysql_error'
|
101
|
+
ArJdbcMySQL::Error
|
102
|
+
elsif defined?(Mysql2)
|
103
|
+
Mysql2::Error
|
104
|
+
elsif defined?(Mysql)
|
105
|
+
Mysql::Error
|
106
|
+
else
|
107
|
+
StandardError
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def grant_statement
|
112
|
+
<<-SQL
|
113
|
+
GRANT ALL PRIVILEGES ON #{configuration['database']}.*
|
114
|
+
TO '#{configuration['username']}'@'localhost'
|
115
|
+
IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
|
116
|
+
SQL
|
117
|
+
end
|
118
|
+
|
119
|
+
def root_configuration_without_database
|
120
|
+
configuration_without_database.merge(
|
121
|
+
'username' => 'root',
|
122
|
+
'password' => root_password
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def root_password
|
127
|
+
$stdout.print "Please provide the root password for your mysql installation\n>"
|
128
|
+
$stdin.gets.strip
|
129
|
+
end
|
130
|
+
|
131
|
+
def prepare_command_options(command)
|
132
|
+
args = [command]
|
133
|
+
args.concat(['--user', configuration['username']]) if configuration['username']
|
134
|
+
args << "--password=#{configuration['password']}" if configuration['password']
|
135
|
+
args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
|
136
|
+
configuration.slice('host', 'port', 'socket').each do |k, v|
|
137
|
+
args.concat([ "--#{k}", v.to_s ]) if v
|
138
|
+
end
|
139
|
+
|
140
|
+
args
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Tasks # :nodoc:
|
3
|
+
class OracleDatabaseTasks # :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.structure_drop.split(";\n\n").each { |ddl| connection.execute(ddl) }
|
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
|
+
establish_connection(configuration)
|
30
|
+
File.open(filename, "w:utf-8") { |f| f << connection.structure_dump }
|
31
|
+
end
|
32
|
+
|
33
|
+
def structure_load(filename)
|
34
|
+
establish_connection(configuration)
|
35
|
+
IO.read(filename).split(";\n\n").each { |ddl| connection.execute(ddl) }
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def configuration
|
41
|
+
@configuration
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Tasks # :nodoc:
|
5
|
+
class PostgreSQLDatabaseTasks # :nodoc:
|
6
|
+
DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8'
|
7
|
+
|
8
|
+
delegate :connection, :establish_connection, :clear_active_connections!,
|
9
|
+
to: ActiveRecord::Base
|
10
|
+
|
11
|
+
def initialize(configuration)
|
12
|
+
@configuration = configuration
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(master_established = false)
|
16
|
+
establish_master_connection unless master_established
|
17
|
+
connection.create_database configuration['database'],
|
18
|
+
configuration.merge('encoding' => encoding)
|
19
|
+
establish_connection configuration
|
20
|
+
rescue ActiveRecord::StatementInvalid => error
|
21
|
+
if /database .* already exists/ === error.message
|
22
|
+
raise DatabaseAlreadyExists
|
23
|
+
else
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def drop
|
29
|
+
establish_master_connection
|
30
|
+
connection.drop_database configuration['database']
|
31
|
+
end
|
32
|
+
|
33
|
+
def charset
|
34
|
+
connection.encoding
|
35
|
+
end
|
36
|
+
|
37
|
+
def collation
|
38
|
+
connection.collation
|
39
|
+
end
|
40
|
+
|
41
|
+
def purge
|
42
|
+
clear_active_connections!
|
43
|
+
drop
|
44
|
+
create true
|
45
|
+
end
|
46
|
+
|
47
|
+
def structure_dump(filename)
|
48
|
+
set_psql_env
|
49
|
+
search_path = configuration['schema_search_path']
|
50
|
+
unless search_path.blank?
|
51
|
+
search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
|
52
|
+
end
|
53
|
+
|
54
|
+
command = "pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(configuration['database'])}"
|
55
|
+
raise 'Error dumping database' unless Kernel.system(command)
|
56
|
+
|
57
|
+
File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
|
58
|
+
end
|
59
|
+
|
60
|
+
def structure_load(filename)
|
61
|
+
set_psql_env
|
62
|
+
Kernel.system("psql -q -f #{Shellwords.escape(filename)} #{configuration['database']}")
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def configuration
|
68
|
+
@configuration
|
69
|
+
end
|
70
|
+
|
71
|
+
def encoding
|
72
|
+
configuration['encoding'] || DEFAULT_ENCODING
|
73
|
+
end
|
74
|
+
|
75
|
+
def establish_master_connection
|
76
|
+
establish_connection configuration.merge(
|
77
|
+
'database' => 'postgres',
|
78
|
+
'schema_search_path' => 'public'
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_psql_env
|
83
|
+
ENV['PGHOST'] = configuration['host'] if configuration['host']
|
84
|
+
ENV['PGPORT'] = configuration['port'].to_s if configuration['port']
|
85
|
+
ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password']
|
86
|
+
ENV['PGUSER'] = configuration['username'].to_s if configuration['username']
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Tasks # :nodoc:
|
3
|
+
class SQLiteDatabaseTasks # :nodoc:
|
4
|
+
delegate :connection, :establish_connection, to: ActiveRecord::Base
|
5
|
+
|
6
|
+
def initialize(configuration, root = ActiveRecord::Tasks::DatabaseTasks.root)
|
7
|
+
@configuration, @root = configuration, root
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
raise DatabaseAlreadyExists if File.exist?(configuration['database'])
|
12
|
+
|
13
|
+
establish_connection configuration
|
14
|
+
connection
|
15
|
+
end
|
16
|
+
|
17
|
+
def drop
|
18
|
+
require 'pathname'
|
19
|
+
path = Pathname.new configuration['database']
|
20
|
+
file = path.absolute? ? path.to_s : File.join(root, path)
|
21
|
+
|
22
|
+
FileUtils.rm(file) if File.exist?(file)
|
23
|
+
end
|
24
|
+
alias :purge :drop
|
25
|
+
|
26
|
+
def charset
|
27
|
+
connection.encoding
|
28
|
+
end
|
29
|
+
|
30
|
+
def structure_dump(filename)
|
31
|
+
dbfile = configuration['database']
|
32
|
+
`sqlite3 #{dbfile} .schema > #{filename}`
|
33
|
+
end
|
34
|
+
|
35
|
+
def structure_load(filename)
|
36
|
+
dbfile = configuration['database']
|
37
|
+
`sqlite3 #{dbfile} < "#{filename}"`
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def configuration
|
43
|
+
@configuration
|
44
|
+
end
|
45
|
+
|
46
|
+
def root
|
47
|
+
@root
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Tasks # :nodoc:
|
5
|
+
class SqlserverDatabaseTasks # :nodoc:
|
6
|
+
delegate :connection, :establish_connection, to: ActiveRecord::Base
|
7
|
+
|
8
|
+
def initialize(configuration)
|
9
|
+
ActiveSupport::Deprecation.warn "This database tasks were deprecated, because this tasks should be served by the 3rd party adapter."
|
10
|
+
@configuration = configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def create
|
14
|
+
$stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
15
|
+
end
|
16
|
+
|
17
|
+
def drop
|
18
|
+
$stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
19
|
+
end
|
20
|
+
|
21
|
+
def purge
|
22
|
+
test = configuration.deep_dup
|
23
|
+
test_database = test['database']
|
24
|
+
test['database'] = 'master'
|
25
|
+
establish_connection(test)
|
26
|
+
connection.recreate_database!(test_database)
|
27
|
+
end
|
28
|
+
|
29
|
+
def charset
|
30
|
+
$stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
|
31
|
+
end
|
32
|
+
|
33
|
+
def structure_dump(filename)
|
34
|
+
Kernel.system("smoscript -s #{configuration['host']} -d #{configuration['database']} -u #{configuration['username']} -p #{configuration['password']} -f #{filename} -A -U")
|
35
|
+
end
|
36
|
+
|
37
|
+
def structure_load(filename)
|
38
|
+
Kernel.system("sqlcmd -S #{configuration['host']} -d #{configuration['database']} -U #{configuration['username']} -P #{configuration['password']} -i #{filename}")
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def configuration
|
44
|
+
@configuration
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,23 +1,13 @@
|
|
1
|
+
require 'active_support/test_case'
|
2
|
+
|
3
|
+
ActiveSupport::Deprecation.warn('ActiveRecord::TestCase is deprecated, please use ActiveSupport::TestCase')
|
1
4
|
module ActiveRecord
|
2
5
|
# = Active Record Test Case
|
3
6
|
#
|
4
7
|
# Defines some test assertions to test against SQL queries.
|
5
8
|
class TestCase < ActiveSupport::TestCase #:nodoc:
|
6
|
-
|
7
|
-
|
8
|
-
def setup
|
9
|
-
cleanup_identity_map
|
10
|
-
end
|
11
|
-
|
12
|
-
def cleanup_identity_map
|
13
|
-
ActiveRecord::IdentityMap.clear
|
14
|
-
end
|
15
|
-
|
16
|
-
# Backport skip to Ruby 1.8. test/unit doesn't support it, so just
|
17
|
-
# make it a noop.
|
18
|
-
unless instance_methods.map(&:to_s).include?("skip")
|
19
|
-
def skip(message)
|
20
|
-
end
|
9
|
+
def teardown
|
10
|
+
SQLCounter.clear_log
|
21
11
|
end
|
22
12
|
|
23
13
|
def assert_date_from_db(expected, actual, message = nil)
|
@@ -30,44 +20,83 @@ module ActiveRecord
|
|
30
20
|
end
|
31
21
|
end
|
32
22
|
|
23
|
+
def capture_sql
|
24
|
+
SQLCounter.clear_log
|
25
|
+
yield
|
26
|
+
SQLCounter.log_all.dup
|
27
|
+
end
|
28
|
+
|
33
29
|
def assert_sql(*patterns_to_match)
|
34
|
-
|
30
|
+
SQLCounter.clear_log
|
35
31
|
yield
|
36
|
-
|
32
|
+
SQLCounter.log_all
|
37
33
|
ensure
|
38
34
|
failed_patterns = []
|
39
35
|
patterns_to_match.each do |pattern|
|
40
|
-
failed_patterns << pattern unless
|
36
|
+
failed_patterns << pattern unless SQLCounter.log_all.any?{ |sql| pattern === sql }
|
41
37
|
end
|
42
|
-
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{
|
38
|
+
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
|
43
39
|
end
|
44
40
|
|
45
|
-
def assert_queries(num = 1)
|
46
|
-
|
41
|
+
def assert_queries(num = 1, options = {})
|
42
|
+
ignore_none = options.fetch(:ignore_none) { num == :any }
|
43
|
+
SQLCounter.clear_log
|
47
44
|
yield
|
48
45
|
ensure
|
49
|
-
|
46
|
+
the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
|
47
|
+
if num == :any
|
48
|
+
assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed."
|
49
|
+
else
|
50
|
+
mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}"
|
51
|
+
assert_equal num, the_log.size, mesg
|
52
|
+
end
|
50
53
|
end
|
51
54
|
|
52
55
|
def assert_no_queries(&block)
|
53
|
-
|
54
|
-
ActiveRecord::SQLCounter.ignored_sql = []
|
55
|
-
assert_queries(0, &block)
|
56
|
-
ensure
|
57
|
-
ActiveRecord::SQLCounter.ignored_sql = prev_ignored_sql
|
56
|
+
assert_queries(0, :ignore_none => true, &block)
|
58
57
|
end
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
59
|
+
end
|
60
|
+
|
61
|
+
class SQLCounter
|
62
|
+
class << self
|
63
|
+
attr_accessor :ignored_sql, :log, :log_all
|
64
|
+
def clear_log; self.log = []; self.log_all = []; end
|
65
|
+
end
|
66
|
+
|
67
|
+
self.clear_log
|
68
|
+
|
69
|
+
self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
|
70
|
+
|
71
|
+
# FIXME: this needs to be refactored so specific database can add their own
|
72
|
+
# ignored SQL, or better yet, use a different notification for the queries
|
73
|
+
# instead examining the SQL content.
|
74
|
+
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
|
75
|
+
mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/]
|
76
|
+
postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
|
77
|
+
sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im]
|
78
|
+
|
79
|
+
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
|
80
|
+
ignored_sql.concat db_ignored_sql
|
81
|
+
end
|
82
|
+
|
83
|
+
attr_reader :ignore
|
84
|
+
|
85
|
+
def initialize(ignore = Regexp.union(self.class.ignored_sql))
|
86
|
+
@ignore = ignore
|
87
|
+
end
|
88
|
+
|
89
|
+
def call(name, start, finish, message_id, values)
|
90
|
+
sql = values[:sql]
|
91
|
+
|
92
|
+
# FIXME: this seems bad. we should probably have a better way to indicate
|
93
|
+
# the query was cached
|
94
|
+
return if 'CACHE' == values[:name]
|
95
|
+
|
96
|
+
self.class.log_all << sql
|
97
|
+
self.class.log << sql unless ignore =~ sql
|
71
98
|
end
|
72
99
|
end
|
100
|
+
|
101
|
+
ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
|
73
102
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/class/attribute'
|
2
1
|
|
3
2
|
module ActiveRecord
|
4
3
|
# = Active Record Timestamp
|
@@ -11,9 +10,9 @@ module ActiveRecord
|
|
11
10
|
#
|
12
11
|
# config.active_record.record_timestamps = false
|
13
12
|
#
|
14
|
-
# Timestamps are in
|
13
|
+
# Timestamps are in UTC by default but you can use the local timezone by setting:
|
15
14
|
#
|
16
|
-
# config.active_record.default_timezone = :
|
15
|
+
# config.active_record.default_timezone = :local
|
17
16
|
#
|
18
17
|
# == Time Zone aware attributes
|
19
18
|
#
|
@@ -37,14 +36,14 @@ module ActiveRecord
|
|
37
36
|
self.record_timestamps = true
|
38
37
|
end
|
39
38
|
|
40
|
-
def initialize_dup(other)
|
39
|
+
def initialize_dup(other) # :nodoc:
|
41
40
|
clear_timestamp_attributes
|
42
41
|
super
|
43
42
|
end
|
44
43
|
|
45
44
|
private
|
46
45
|
|
47
|
-
def
|
46
|
+
def _create_record
|
48
47
|
if self.record_timestamps
|
49
48
|
current_time = current_time_from_proper_timezone
|
50
49
|
|
@@ -58,7 +57,7 @@ module ActiveRecord
|
|
58
57
|
super
|
59
58
|
end
|
60
59
|
|
61
|
-
def
|
60
|
+
def _update_record(*args)
|
62
61
|
if should_record_timestamps?
|
63
62
|
current_time = current_time_from_proper_timezone
|
64
63
|
|
@@ -72,7 +71,7 @@ module ActiveRecord
|
|
72
71
|
end
|
73
72
|
|
74
73
|
def should_record_timestamps?
|
75
|
-
self.record_timestamps && (!
|
74
|
+
self.record_timestamps && (!partial_writes? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
|
76
75
|
end
|
77
76
|
|
78
77
|
def timestamp_attributes_for_create_in_model
|
@@ -87,19 +86,25 @@ module ActiveRecord
|
|
87
86
|
timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
|
88
87
|
end
|
89
88
|
|
90
|
-
def timestamp_attributes_for_update
|
89
|
+
def timestamp_attributes_for_update
|
91
90
|
[:updated_at, :updated_on]
|
92
91
|
end
|
93
92
|
|
94
|
-
def timestamp_attributes_for_create
|
93
|
+
def timestamp_attributes_for_create
|
95
94
|
[:created_at, :created_on]
|
96
95
|
end
|
97
96
|
|
98
|
-
def all_timestamp_attributes
|
97
|
+
def all_timestamp_attributes
|
99
98
|
timestamp_attributes_for_create + timestamp_attributes_for_update
|
100
99
|
end
|
101
100
|
|
102
|
-
def
|
101
|
+
def max_updated_column_timestamp
|
102
|
+
if (timestamps = timestamp_attributes_for_update.map { |attr| self[attr] }.compact).present?
|
103
|
+
timestamps.map { |ts| ts.to_time }.max
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def current_time_from_proper_timezone
|
103
108
|
self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
104
109
|
end
|
105
110
|
|