activerecord 3.2.22.5 → 4.0.0.beta1

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  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 +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  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 +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  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/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,158 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class DatabaseAlreadyExists < StandardError; end # :nodoc:
4
+ class DatabaseNotSupported < StandardError; end # :nodoc:
5
+
6
+ module DatabaseTasks # :nodoc:
7
+ extend self
8
+
9
+ attr_writer :current_config
10
+
11
+ LOCAL_HOSTS = ['127.0.0.1', 'localhost']
12
+
13
+ def register_task(pattern, task)
14
+ @tasks ||= {}
15
+ @tasks[pattern] = task
16
+ end
17
+
18
+ register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks)
19
+ register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
20
+ register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
21
+
22
+ def current_config(options = {})
23
+ options.reverse_merge! :env => Rails.env
24
+ if options.has_key?(:config)
25
+ @current_config = options[:config]
26
+ else
27
+ @current_config ||= if ENV['DATABASE_URL']
28
+ database_url_config
29
+ else
30
+ ActiveRecord::Base.configurations[options[:env]]
31
+ end
32
+ end
33
+ end
34
+
35
+ def create(*arguments)
36
+ configuration = arguments.first
37
+ class_for_adapter(configuration['adapter']).new(*arguments).create
38
+ rescue DatabaseAlreadyExists
39
+ $stderr.puts "#{configuration['database']} already exists"
40
+ rescue Exception => error
41
+ $stderr.puts error, *(error.backtrace)
42
+ $stderr.puts "Couldn't create database for #{configuration.inspect}"
43
+ end
44
+
45
+ def create_all
46
+ each_local_configuration { |configuration| create configuration }
47
+ end
48
+
49
+ def create_current(environment = Rails.env)
50
+ each_current_configuration(environment) { |configuration|
51
+ create configuration
52
+ }
53
+ ActiveRecord::Base.establish_connection environment
54
+ end
55
+
56
+ def create_database_url
57
+ create database_url_config
58
+ end
59
+
60
+ def drop(*arguments)
61
+ configuration = arguments.first
62
+ class_for_adapter(configuration['adapter']).new(*arguments).drop
63
+ rescue Exception => error
64
+ $stderr.puts error, *(error.backtrace)
65
+ $stderr.puts "Couldn't drop #{configuration['database']}"
66
+ end
67
+
68
+ def drop_all
69
+ each_local_configuration { |configuration| drop configuration }
70
+ end
71
+
72
+ def drop_current(environment = Rails.env)
73
+ each_current_configuration(environment) { |configuration|
74
+ drop configuration
75
+ }
76
+ end
77
+
78
+ def drop_database_url
79
+ drop database_url_config
80
+ end
81
+
82
+ def charset_current(environment = Rails.env)
83
+ charset ActiveRecord::Base.configurations[environment]
84
+ end
85
+
86
+ def charset(*arguments)
87
+ configuration = arguments.first
88
+ class_for_adapter(configuration['adapter']).new(*arguments).charset
89
+ end
90
+
91
+ def collation_current(environment = Rails.env)
92
+ collation ActiveRecord::Base.configurations[environment]
93
+ end
94
+
95
+ def collation(*arguments)
96
+ configuration = arguments.first
97
+ class_for_adapter(configuration['adapter']).new(*arguments).collation
98
+ end
99
+
100
+ def purge(configuration)
101
+ class_for_adapter(configuration['adapter']).new(configuration).purge
102
+ end
103
+
104
+ def structure_dump(*arguments)
105
+ configuration = arguments.first
106
+ filename = arguments.delete_at 1
107
+ class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
108
+ end
109
+
110
+ def structure_load(*arguments)
111
+ configuration = arguments.first
112
+ filename = arguments.delete_at 1
113
+ class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
114
+ end
115
+
116
+ private
117
+
118
+ def database_url_config
119
+ @database_url_config ||=
120
+ ConnectionAdapters::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys
121
+ end
122
+
123
+ def class_for_adapter(adapter)
124
+ key = @tasks.keys.detect { |pattern| adapter[pattern] }
125
+ unless key
126
+ raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
127
+ end
128
+ @tasks[key]
129
+ end
130
+
131
+ def each_current_configuration(environment)
132
+ environments = [environment]
133
+ environments << 'test' if environment.development?
134
+
135
+ configurations = ActiveRecord::Base.configurations.values_at(*environments)
136
+ configurations.compact.each do |configuration|
137
+ yield configuration unless configuration['database'].blank?
138
+ end
139
+ end
140
+
141
+ def each_local_configuration
142
+ ActiveRecord::Base.configurations.each_value do |configuration|
143
+ next unless configuration['database']
144
+
145
+ if local_database?(configuration)
146
+ yield configuration
147
+ else
148
+ $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
149
+ end
150
+ end
151
+ end
152
+
153
+ def local_database?(configuration)
154
+ configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,138 @@
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
+ connection.execute grant_statement.gsub(/\s+/, ' ').strip
30
+ establish_connection configuration
31
+ else
32
+ $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
33
+ $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
34
+ end
35
+ end
36
+
37
+ def drop
38
+ establish_connection configuration
39
+ connection.drop_database configuration['database']
40
+ end
41
+
42
+ def purge
43
+ establish_connection :test
44
+ connection.recreate_database configuration['database'], creation_options
45
+ end
46
+
47
+ def charset
48
+ connection.charset
49
+ end
50
+
51
+ def collation
52
+ connection.collation
53
+ end
54
+
55
+ def structure_dump(filename)
56
+ args = prepare_command_options('mysqldump')
57
+ args.concat(["--result-file", "#{filename}"])
58
+ args.concat(["--no-data"])
59
+ args.concat(["#{configuration['database']}"])
60
+ Kernel.system(*args)
61
+ end
62
+
63
+ def structure_load(filename)
64
+ args = prepare_command_options('mysql')
65
+ args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
66
+ args.concat(["--database", "#{configuration['database']}"])
67
+ Kernel.system(*args)
68
+ end
69
+
70
+ private
71
+
72
+ def configuration
73
+ @configuration
74
+ end
75
+
76
+ def configuration_without_database
77
+ configuration.merge('database' => nil)
78
+ end
79
+
80
+ def creation_options
81
+ Hash.new.tap do |options|
82
+ options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
83
+ options[:collation] = configuration['collation'] if configuration.include? 'collation'
84
+
85
+ # Set default charset only when collation isn't set.
86
+ options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
87
+
88
+ # Set default collation only when charset is also default.
89
+ options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
90
+ end
91
+ end
92
+
93
+ def error_class
94
+ if configuration['adapter'] =~ /jdbc/
95
+ require 'active_record/railties/jdbcmysql_error'
96
+ ArJdbcMySQL::Error
97
+ elsif defined?(Mysql2)
98
+ Mysql2::Error
99
+ elsif defined?(Mysql)
100
+ Mysql::Error
101
+ else
102
+ StandardError
103
+ end
104
+ end
105
+
106
+ def grant_statement
107
+ <<-SQL
108
+ GRANT ALL PRIVILEGES ON #{configuration['database']}.*
109
+ TO '#{configuration['username']}'@'localhost'
110
+ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
111
+ SQL
112
+ end
113
+
114
+ def root_configuration_without_database
115
+ configuration_without_database.merge(
116
+ 'username' => 'root',
117
+ 'password' => root_password
118
+ )
119
+ end
120
+
121
+ def root_password
122
+ $stdout.print "Please provide the root password for your mysql installation\n>"
123
+ $stdin.gets.strip
124
+ end
125
+
126
+ def prepare_command_options(command)
127
+ args = [command]
128
+ args.concat(['--user', configuration['username']]) if configuration['username']
129
+ args << "--password=#{configuration['password']}" if configuration['password']
130
+ args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
131
+ configuration.slice('host', 'port', 'socket').each do |k, v|
132
+ args.concat([ "--#{k}", v ]) if v
133
+ end
134
+ args
135
+ end
136
+ end
137
+ end
138
+ 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 -f #{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 = Rails.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
@@ -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)
@@ -31,43 +21,76 @@ module ActiveRecord
31
21
  end
32
22
 
33
23
  def assert_sql(*patterns_to_match)
34
- ActiveRecord::SQLCounter.log = []
24
+ SQLCounter.clear_log
35
25
  yield
36
- ActiveRecord::SQLCounter.log
26
+ SQLCounter.log_all
37
27
  ensure
38
28
  failed_patterns = []
39
29
  patterns_to_match.each do |pattern|
40
- failed_patterns << pattern unless ActiveRecord::SQLCounter.log.any?{ |sql| pattern === sql }
30
+ failed_patterns << pattern unless SQLCounter.log_all.any?{ |sql| pattern === sql }
41
31
  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")}"}"
32
+ 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
33
  end
44
34
 
45
- def assert_queries(num = 1)
46
- ActiveRecord::SQLCounter.log = []
35
+ def assert_queries(num = 1, options = {})
36
+ ignore_none = options.fetch(:ignore_none) { num == :any }
37
+ SQLCounter.clear_log
47
38
  yield
48
39
  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")}"}"
40
+ the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
41
+ if num == :any
42
+ assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed."
43
+ else
44
+ mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}"
45
+ assert_equal num, the_log.size, mesg
46
+ end
50
47
  end
51
48
 
52
49
  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
50
+ assert_queries(0, :ignore_none => true, &block)
58
51
  end
59
52
 
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
53
+ end
54
+
55
+ class SQLCounter
56
+ class << self
57
+ attr_accessor :ignored_sql, :log, :log_all
58
+ def clear_log; self.log = []; self.log_all = []; end
59
+ end
60
+
61
+ self.clear_log
62
+
63
+ self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
64
+
65
+ # FIXME: this needs to be refactored so specific database can add their own
66
+ # ignored SQL, or better yet, use a different notification for the queries
67
+ # instead examining the SQL content.
68
+ oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
69
+ mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/]
70
+ 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]
71
+ sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im]
72
+
73
+ [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
74
+ ignored_sql.concat db_ignored_sql
75
+ end
76
+
77
+ attr_reader :ignore
78
+
79
+ def initialize(ignore = Regexp.union(self.class.ignored_sql))
80
+ @ignore = ignore
81
+ end
82
+
83
+ def call(name, start, finish, message_id, values)
84
+ sql = values[:sql]
85
+
86
+ # FIXME: this seems bad. we should probably have a better way to indicate
87
+ # the query was cached
88
+ return if 'CACHE' == values[:name]
89
+
90
+ self.class.log_all << sql
91
+ self.class.log << sql unless ignore =~ sql
71
92
  end
72
93
  end
94
+
95
+ ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
73
96
  end