activerecord 3.2.22.4 → 4.0.13

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

Potentially problematic release.


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

Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -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
- setup :cleanup_identity_map
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
- ActiveRecord::SQLCounter.log = []
30
+ SQLCounter.clear_log
35
31
  yield
36
- ActiveRecord::SQLCounter.log
32
+ SQLCounter.log_all
37
33
  ensure
38
34
  failed_patterns = []
39
35
  patterns_to_match.each do |pattern|
40
- failed_patterns << pattern unless ActiveRecord::SQLCounter.log.any?{ |sql| pattern === sql }
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.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
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
- ActiveRecord::SQLCounter.log = []
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
- assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
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
- prev_ignored_sql = ActiveRecord::SQLCounter.ignored_sql
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
- def with_kcode(kcode)
61
- if RUBY_VERSION < '1.9'
62
- orig_kcode, $KCODE = $KCODE, kcode
63
- begin
64
- yield
65
- ensure
66
- $KCODE = orig_kcode
67
- end
68
- else
69
- yield
70
- end
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 the local timezone by default but you can use UTC by setting:
13
+ # Timestamps are in UTC by default but you can use the local timezone by setting:
15
14
  #
16
- # config.active_record.default_timezone = :utc
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 create #:nodoc:
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 update(*args) #:nodoc:
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 && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
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 #:nodoc:
89
+ def timestamp_attributes_for_update
91
90
  [:updated_at, :updated_on]
92
91
  end
93
92
 
94
- def timestamp_attributes_for_create #:nodoc:
93
+ def timestamp_attributes_for_create
95
94
  [:created_at, :created_on]
96
95
  end
97
96
 
98
- def all_timestamp_attributes #:nodoc:
97
+ def all_timestamp_attributes
99
98
  timestamp_attributes_for_create + timestamp_attributes_for_update
100
99
  end
101
100
 
102
- def current_time_from_proper_timezone #:nodoc:
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