diff options
51 files changed, 503 insertions, 469 deletions
diff --git a/.travis.yml b/.travis.yml index 5352873d8f..6c4d540a8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,8 +23,7 @@ rvm: - jruby matrix: allow_failures: - - rvm: ruby-head - env: "GEM=ar:mysql" + - env: "GEM=ar:mysql" - rvm: rbx-2 - rvm: jruby - env: "GEM=aj" diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb index c1562fcc0d..9082aac271 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -1,6 +1,10 @@ require 'action_dispatch/http/request' require 'action_dispatch/middleware/exception_wrapper' require 'action_dispatch/routing/inspector' +require 'action_view' +require 'action_view/base' + +require 'pp' module ActionDispatch # This middleware is responsible for logging exceptions and @@ -8,6 +12,32 @@ module ActionDispatch class DebugExceptions RESCUES_TEMPLATE_PATH = File.expand_path('../templates', __FILE__) + class DebugView < ActionView::Base + def debug_params(params) + clean_params = params.clone + clean_params.delete("action") + clean_params.delete("controller") + + if clean_params.empty? + 'None' + else + PP.pp(clean_params, "", 200) + end + end + + def debug_headers(headers) + if headers.present? + headers.inspect.gsub(',', ",\n") + else + 'None' + end + end + + def debug_hash(object) + object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n") + end + end + def initialize(app, routes_app = nil) @app = app @routes_app = routes_app @@ -46,7 +76,7 @@ module ActionDispatch source_to_show_id = source_to_show[:id] end - template = ActionView::Base.new([RESCUES_TEMPLATE_PATH], + template = DebugView.new([RESCUES_TEMPLATE_PATH], request: request, exception: wrapper.exception, traces: traces, diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index 8edeeaf2af..a4862e33aa 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -1,6 +1,5 @@ require 'action_controller/metal/exceptions' require 'active_support/core_ext/module/attribute_accessors' -require 'pp' module ActionDispatch class ExceptionWrapper diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb index 761cd0ffcc..49b1e83551 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb @@ -5,27 +5,8 @@ <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%= @exception.describe_blame %></code></pre> <% end %> -<% - clean_params = @request.filtered_parameters.clone - clean_params.delete("action") - clean_params.delete("controller") - - request_dump = if clean_params.empty? - 'None' - else - pretty_params = "" - PP.pp(clean_params, pretty_params, 200) - - pretty_params - end - - def debug_hash(object) - object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n") - end unless self.class.method_defined?(:debug_hash) -%> - <h2 style="margin-top: 30px">Request</h2> -<p><b>Parameters</b>:</p> <pre><%= request_dump %></pre> +<p><b>Parameters</b>:</p> <pre><%= debug_params(@request.filtered_parameters) %></pre> <div class="details"> <div class="summary"><a href="#" onclick="return toggleSessionDump()">Toggle session dump</a></div> @@ -38,4 +19,4 @@ </div> <h2 style="margin-top: 30px">Response</h2> -<p><b>Headers</b>:</p> <pre><%= defined?(@response) ? @response.headers.inspect.gsub(',', ",\n") : 'None' %></pre> +<p><b>Headers</b>:</p> <pre><%= debug_headers(defined?(@response) ? @response.headers : {}) %></pre> diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index c2cacbd288..f3144dc2d3 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -522,6 +522,8 @@ module ActionDispatch define_method(:_generate_paths_by_default) do supports_path end + + private :_generate_paths_by_default end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 57c076eac6..2d69ceed3a 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -21,6 +21,7 @@ PROCESS_COUNT = (ENV['N'] || 4).to_i require 'active_support/testing/autorun' require 'abstract_controller' +require 'abstract_controller/railties/routes_helpers' require 'action_controller' require 'action_view' require 'action_view/testing/resolvers' @@ -260,7 +261,7 @@ end module ActionController class Base # This stub emulates the Railtie including the URL helpers from a Rails application - include SharedTestRoutes.url_helpers + extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes) include SharedTestRoutes.mounted_helpers self.view_paths = FIXTURE_LOAD_PATH diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 7dc6c37522..5be75a5de6 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -494,7 +494,7 @@ class CookiesTest < ActionController::TestCase assert_nil @response.cookies["user_id"] end - def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature + def test_accessing_nonexistent_signed_cookie_should_not_raise_an_invalid_signature get :set_signed_cookie assert_nil @controller.send(:cookies).signed[:non_existant_attribute] end @@ -612,7 +612,7 @@ class CookiesTest < ActionController::TestCase assert_nil @response.cookies["foo"] end - def test_accessing_nonexistant_encrypted_cookie_should_not_raise_invalid_message + def test_accessing_nonexistent_encrypted_cookie_should_not_raise_invalid_message get :set_encrypted_cookie assert_nil @controller.send(:cookies).encrypted[:non_existant_attribute] end diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 5e87744f6b..1e5ed60b09 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -241,6 +241,29 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_match(/RuntimeError\n\s+in FeaturedTileController/, body) end + test "show formatted params" do + @app = DevelopmentApp + + params = { + 'id' => 'unknown', + 'someparam' => { + 'foo' => 'bar', + 'abc' => 'goo' + } + } + + get("/runtime_error", {}, { + 'action_dispatch.show_exceptions' => true, + 'action_dispatch.request.parameters' => { + 'action' => 'show', + 'controller' => 'featured_tile' + }.merge(params) + }) + assert_response 500 + + assert_includes(body, CGI.escapeHTML(PP.pp(params, "", 200))) + end + test "sets the HTTP charset parameter" do @app = DevelopmentApp diff --git a/actionview/test/fixtures/happy_path/render_action/hello_world.erb b/actionview/test/fixtures/happy_path/render_action/hello_world.erb deleted file mode 100644 index 6769dd60bd..0000000000 --- a/actionview/test/fixtures/happy_path/render_action/hello_world.erb +++ /dev/null @@ -1 +0,0 @@ -Hello world!
\ No newline at end of file diff --git a/actionview/test/fixtures/scope/test/modgreet.erb b/actionview/test/fixtures/scope/test/modgreet.erb deleted file mode 100644 index 8947726e89..0000000000 --- a/actionview/test/fixtures/scope/test/modgreet.erb +++ /dev/null @@ -1 +0,0 @@ -<p>Beautiful modules!</p>
\ No newline at end of file diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 38c5583708..e43fa2b4cc 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1 +1,8 @@ +* `ActiveRecord::ConnectionAdapters::ColumnDumper#column_spec` and + `ActiveRecord::ConnectionAdapters::ColumnDumper#prepare_column_options` no + longer have a `types` argument. They should access + `connection#native_database_types` directly. + + *Yves Senn* + Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activerecord/CHANGELOG.md) for previous changes. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb index 6eab11b88b..0834105079 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb @@ -6,8 +6,8 @@ module ActiveRecord # We can then redefine how certain data types may be handled in the schema dumper on the # Adapter level by over-writing this code inside the database specific adapters module ColumnDumper - def column_spec(column, types) - spec = prepare_column_options(column, types) + def column_spec(column) + spec = prepare_column_options(column) (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k}: ")} spec end @@ -15,13 +15,13 @@ module ActiveRecord # This can be overridden on a Adapter level basis to support other # extended datatypes (Example: Adding an array option in the # PostgreSQLAdapter) - def prepare_column_options(column, types) + def prepare_column_options(column) spec = {} spec[:name] = column.name.inspect spec[:type] = column.type.to_s spec[:null] = 'false' unless column.null - limit = column.limit || types[column.type][:limit] + limit = column.limit || native_database_types[column.type][:limit] spec[:limit] = limit.inspect if limit spec[:precision] = column.precision.inspect if column.precision spec[:scale] = column.scale.inspect if column.scale diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index fd3232e902..3a60de1f28 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -127,7 +127,7 @@ module ActiveRecord # Adds +:array+ option to the default set provided by the # AbstractAdapter - def prepare_column_options(column, types) # :nodoc: + def prepare_column_options(column) # :nodoc: spec = super spec[:array] = 'true' if column.respond_to?(:array) && column.array spec[:default] = "\"#{column.default_function}\"" if column.default_function diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7df50f7c97..cfbd3076cb 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -677,5 +677,9 @@ module ActiveRecord # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_'] end + + def predicate_builder + @predicate_builder ||= PredicateBuilder.new(klass, table) + end end end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 938bf55c6d..eb21d01465 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -5,7 +5,12 @@ module ActiveRecord autoload :RelationHandler, 'active_record/relation/predicate_builder/relation_handler' autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler' - def self.resolve_column_aliases(klass, hash) + def initialize(klass, table) + @klass = klass + @table = table + end + + def resolve_column_aliases(hash) hash = hash.dup hash.keys.grep(Symbol) do |key| if klass.attribute_alias? key @@ -15,39 +20,12 @@ module ActiveRecord hash end - def self.build_from_hash(klass, attributes, default_table) - queries = [] - - attributes.each do |column, value| - table = default_table - - if value.is_a?(Hash) - if value.empty? - queries << '1=0' - else - table = Arel::Table.new(column) - association = klass._reflect_on_association(column) - - value.each do |k, v| - queries.concat expand(association && association.klass, table, k, v) - end - end - else - column = column.to_s - - if column.include?('.') - table_name, column = column.split('.', 2) - table = Arel::Table.new(table_name) - end - - queries.concat expand(klass, table, column, value) - end - end - - queries + def build_from_hash(attributes) + attributes = convert_dot_notation_to_hash(attributes.stringify_keys) + expand_from_hash(attributes) end - def self.expand(klass, table, column, value) + def expand(column, value) queries = [] # Find the foreign key when using queries such as: @@ -57,17 +35,17 @@ module ActiveRecord # PriceEstimate.where(estimate_of: treasure) if klass && reflection = klass._reflect_on_association(column) if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value) - queries << build(table[reflection.foreign_type], base_class) + queries << self.class.build(table[reflection.foreign_type], base_class) end column = reflection.foreign_key end - queries << build(table[column], value) + queries << self.class.build(table[column], value) queries end - def self.polymorphic_base_class_from_value(value) + def polymorphic_base_class_from_value(value) case value when Relation value.klass.base_class @@ -116,11 +94,46 @@ module ActiveRecord def self.build(attribute, value) handler_for(value).call(attribute, value) end - private_class_method :build def self.handler_for(object) @handlers.detect { |klass, _| klass === object }.last end private_class_method :handler_for + + protected + + attr_reader :klass, :table + + def expand_from_hash(attributes) + return ["1=0"] if attributes.empty? + + attributes.flat_map do |key, value| + if value.is_a?(Hash) + arel_table = Arel::Table.new(key) + association = klass._reflect_on_association(key) + builder = self.class.new(association && association.klass, arel_table) + + builder.expand_from_hash(value) + else + expand(key, value) + end + end + end + + private + + def convert_dot_notation_to_hash(attributes) + dot_notation = attributes.keys.select { |s| s.include?(".") } + + dot_notation.each do |key| + table_name, column_name = key.split(".") + value = attributes.delete(key) + attributes[table_name] ||= {} + + attributes[table_name] = attributes[table_name].merge(column_name => value) + end + + attributes + end end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index d58af01339..ef380abfe8 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -909,7 +909,7 @@ module ActiveRecord case rel when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right) - subrelation.name == target_value + subrelation.name.to_s == target_value end end @@ -947,7 +947,7 @@ module ActiveRecord when String, Array [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))] when Hash - opts = PredicateBuilder.resolve_column_aliases(klass, opts) + opts = predicate_builder.resolve_column_aliases(opts) tmp_opts, bind_values = create_binds(opts) self.bind_values += bind_values @@ -955,7 +955,7 @@ module ActiveRecord attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts) add_relations_to_bind_values(attributes) - PredicateBuilder.build_from_hash(klass, attributes, table) + predicate_builder.build_from_hash(attributes) else [opts] end diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 88dfddebcc..6c103e331f 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -87,14 +87,15 @@ module ActiveRecord # { address: Address.new("123 abc st.", "chicago") } # # => "address_street='123 abc st.' and address_city='chicago'" def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name) + table = Arel::Table.new(table_name).alias(default_table_name) + predicate_builder = PredicateBuilder.new(self, table) ActiveSupport::Deprecation.warn(<<-EOWARN) sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0 EOWARN - attrs = PredicateBuilder.resolve_column_aliases self, attrs + attrs = predicate_builder.resolve_column_aliases(attrs) attrs = expand_hash_conditions_for_aggregates(attrs) - table = Arel::Table.new(table_name).alias(default_table_name) - PredicateBuilder.build_from_hash(self, attrs, table).map { |b| + predicate_builder.build_from_hash(attrs).map { |b| connection.visitor.compile b }.join(' AND ') end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 1dd2cadc01..77aa2efc47 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -44,7 +44,6 @@ module ActiveRecord def initialize(connection, options = {}) @connection = connection - @types = @connection.native_database_types @version = Migrator::current_version rescue nil @options = options end @@ -134,7 +133,7 @@ HEADER column_specs = columns.map do |column| raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type) next if column.name == pk - @connection.column_spec(column, @types) + @connection.column_spec(column) end.compact # find all migration keys used in this table diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index fa7bebf08b..db302b6294 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -1,7 +1,9 @@ # encoding: utf-8 require "cases/helper" +require 'support/schema_dumping_helper' class PostgresqlArrayTest < ActiveRecord::TestCase + include SchemaDumpingHelper include InTimeZone OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID @@ -108,6 +110,12 @@ class PostgresqlArrayTest < ActiveRecord::TestCase assert_equal([1, 2], x.ratings) end + def test_schema_dump_with_shorthand + output = dump_table_schema "pg_arrays" + assert_match %r[t.string\s+"tags",\s+array: true], output + assert_match %r[t.integer\s+"ratings",\s+array: true], output + end + def test_select_with_strings @connection.execute "insert into pg_arrays (tags) VALUES ('{1,2,3}')" x = PgArray.first @@ -263,6 +271,21 @@ class PostgresqlArrayTest < ActiveRecord::TestCase assert_instance_of PG::InvalidTextRepresentation, e.original_exception end + def test_uniqueness_validation + klass = Class.new(PgArray) do + validates_uniqueness_of :tags + + def self.model_name; ActiveModel::Name.new(PgArray) end + end + e1 = klass.create("tags" => ["black", "blue"]) + assert e1.persisted?, "Saving e1" + + e2 = klass.create("tags" => ["black", "blue"]) + assert !e2.persisted?, "e2 shouldn't be valid" + assert e2.errors[:tags].any?, "Should have errors for tags" + assert_equal ["has already been taken"], e2.errors[:tags], "Should have uniqueness message for tags" + end + private def assert_cycle field, array # test creation diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb index cb024463c9..85bff979c9 100644 --- a/activerecord/test/cases/adapters/postgresql/citext_test.rb +++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb @@ -1,8 +1,10 @@ # encoding: utf-8 require 'cases/helper' +require 'support/schema_dumping_helper' if ActiveRecord::Base.connection.supports_extensions? class PostgresqlCitextTest < ActiveRecord::TestCase + include SchemaDumpingHelper class Citext < ActiveRecord::Base self.table_name = 'citexts' end @@ -67,5 +69,10 @@ if ActiveRecord::Base.connection.supports_extensions? x = Citext.where(cival: 'cased text').first assert_equal 'Cased Text', x.cival end + + def test_schema_dump_with_shorthand + output = dump_table_schema("citexts") + assert_match %r[t.citext "cival"], output + end end end diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index 2c4839338c..4f48a7bce3 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -2,9 +2,6 @@ require "cases/helper" require 'support/ddl_helper' -class PostgresqlNumber < ActiveRecord::Base -end - class PostgresqlTime < ActiveRecord::Base end @@ -20,13 +17,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection - @connection.execute("INSERT INTO postgresql_numbers (id, single, double) VALUES (1, 123.456, 123456.789)") - @connection.execute("INSERT INTO postgresql_numbers (id, single, double) VALUES (2, '-Infinity', 'Infinity')") - @connection.execute("INSERT INTO postgresql_numbers (id, single, double) VALUES (3, 123.456, 'NaN')") - @first_number = PostgresqlNumber.find(1) - @second_number = PostgresqlNumber.find(2) - @third_number = PostgresqlNumber.find(3) - @connection.execute("INSERT INTO postgresql_times (id, time_interval, scaled_time_interval) VALUES (1, '1 year 2 days ago', '3 weeks ago')") @first_time = PostgresqlTime.find(1) @@ -35,12 +25,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase end teardown do - [PostgresqlNumber, PostgresqlTime, PostgresqlOid].each(&:delete_all) - end - - def test_data_type_of_number_types - assert_equal :float, @first_number.column_for_attribute(:single).type - assert_equal :float, @first_number.column_for_attribute(:double).type + [PostgresqlTime, PostgresqlOid].each(&:delete_all) end def test_data_type_of_time_types @@ -52,14 +37,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type end - def test_number_values - assert_equal 123.456, @first_number.single - assert_equal 123456.789, @first_number.double - assert_equal(-::Float::INFINITY, @second_number.single) - assert_equal ::Float::INFINITY, @second_number.double - assert_same ::Float::NAN, @third_number.double - end - def test_time_values assert_equal '-1 years -2 days', @first_time.time_interval assert_equal '-21 days', @first_time.scaled_time_interval @@ -69,17 +46,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase assert_equal 1234, @first_oid.obj_id end - def test_update_number - new_single = 789.012 - new_double = 789012.345 - @first_number.single = new_single - @first_number.double = new_double - assert @first_number.save - assert @first_number.reload - assert_equal new_single, @first_number.single - assert_equal new_double, @first_number.double - end - def test_update_time @first_time.time_interval = '2 years 3 minutes' assert @first_time.save diff --git a/activerecord/test/cases/adapters/postgresql/full_text_test.rb b/activerecord/test/cases/adapters/postgresql/full_text_test.rb index 9dadb177ca..dca35422b9 100644 --- a/activerecord/test/cases/adapters/postgresql/full_text_test.rb +++ b/activerecord/test/cases/adapters/postgresql/full_text_test.rb @@ -1,11 +1,24 @@ # encoding: utf-8 require "cases/helper" +require 'support/schema_dumping_helper' class PostgresqlFullTextTest < ActiveRecord::TestCase - class PostgresqlTsvector < ActiveRecord::Base; end + include SchemaDumpingHelper + class Tsvector < ActiveRecord::Base; end + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table('tsvectors') do |t| + t.tsvector 'text_vector' + end + end + + teardown do + @connection.execute 'DROP TABLE IF EXISTS tsvectors;' + end def test_tsvector_column - column = PostgresqlTsvector.columns_hash["text_vector"] + column = Tsvector.columns_hash["text_vector"] assert_equal :tsvector, column.type assert_equal "tsvector", column.sql_type assert_not column.number? @@ -14,8 +27,8 @@ class PostgresqlFullTextTest < ActiveRecord::TestCase end def test_update_tsvector - PostgresqlTsvector.create text_vector: "'text' 'vector'" - tsvector = PostgresqlTsvector.first + Tsvector.create text_vector: "'text' 'vector'" + tsvector = Tsvector.first assert_equal "'text' 'vector'", tsvector.text_vector tsvector.text_vector = "'new' 'text' 'vector'" @@ -23,4 +36,9 @@ class PostgresqlFullTextTest < ActiveRecord::TestCase assert tsvector.reload assert_equal "'new' 'text' 'vector'", tsvector.text_vector end + + def test_schema_dump_with_shorthand + output = dump_table_schema("tsvectors") + assert_match %r{t.tsvector "text_vector"}, output + end end diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb index 6c0adbbeaa..228221e034 100644 --- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb +++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb @@ -11,12 +11,10 @@ class PostgresqlPointTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection - @connection.transaction do - @connection.create_table('postgresql_points') do |t| - t.point :x - t.point :y, default: [12.2, 13.3] - t.point :z, default: "(14.4,15.5)" - end + @connection.create_table('postgresql_points') do |t| + t.point :x + t.point :y, default: [12.2, 13.3] + t.point :z, default: "(14.4,15.5)" end end @@ -70,3 +68,72 @@ class PostgresqlPointTest < ActiveRecord::TestCase assert_not p.changed? end end + +class PostgresqlGeometricTest < ActiveRecord::TestCase + class PostgresqlGeometric < ActiveRecord::Base; end + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table("postgresql_geometrics") do |t| + t.column :a_line_segment, :lseg + t.column :a_box, :box + t.column :a_path, :path + t.column :a_polygon, :polygon + t.column :a_circle, :circle + end + end + + teardown do + @connection.execute 'DROP TABLE IF EXISTS postgresql_geometrics' + end + + def test_geometric_types + g = PostgresqlGeometric.new( + :a_line_segment => '(2.0, 3), (5.5, 7.0)', + :a_box => '2.0, 3, 5.5, 7.0', + :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', + :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', + :a_circle => '<(5.3, 10.4), 2>' + ) + + g.save! + + h = PostgresqlGeometric.find(g.id) + + assert_equal '[(2,3),(5.5,7)]', h.a_line_segment + assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner + assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path + assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon + assert_equal '<(5.3,10.4),2>', h.a_circle + end + + def test_alternative_format + g = PostgresqlGeometric.new( + :a_line_segment => '((2.0, 3), (5.5, 7.0))', + :a_box => '(2.0, 3), (5.5, 7.0)', + :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', + :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0', + :a_circle => '((5.3, 10.4), 2)' + ) + + g.save! + + h = PostgresqlGeometric.find(g.id) + assert_equal '[(2,3),(5.5,7)]', h.a_line_segment + assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner + assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path + assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon + assert_equal '<(5.3,10.4),2>', h.a_circle + end + + def test_geometric_function + PostgresqlGeometric.create! a_path: '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]' # [ ] is an open path + PostgresqlGeometric.create! a_path: '((2.0, 3), (5.5, 7.0), (8.5, 11.0))' # ( ) is a closed path + + objs = PostgresqlGeometric.find_by_sql "SELECT isopen(a_path) FROM postgresql_geometrics ORDER BY id ASC" + assert_equal [true, false], objs.map(&:isopen) + + objs = PostgresqlGeometric.find_by_sql "SELECT isclosed(a_path) FROM postgresql_geometrics ORDER BY id ASC" + assert_equal [false, true], objs.map(&:isclosed) + end +end diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 6a9c6483fe..00ff456e16 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -1,8 +1,10 @@ # encoding: utf-8 require "cases/helper" +require 'support/schema_dumping_helper' if ActiveRecord::Base.connection.supports_extensions? class PostgresqlHstoreTest < ActiveRecord::TestCase + include SchemaDumpingHelper class Hstore < ActiveRecord::Base self.table_name = 'hstores' @@ -313,6 +315,11 @@ if ActiveRecord::Base.connection.supports_extensions? assert_equal({"one" => "two"}, dupe.tags.to_hash) end + def test_schema_dump_with_shorthand + output = dump_table_schema("hstores") + assert_match %r[t.hstore "tags",\s+default: {}], output + end + private def assert_array_cycle(array) # test creation diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb index 2968109346..5a0f505072 100644 --- a/activerecord/test/cases/adapters/postgresql/ltree_test.rb +++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb @@ -1,7 +1,9 @@ # encoding: utf-8 require "cases/helper" +require 'support/schema_dumping_helper' class PostgresqlLtreeTest < ActiveRecord::TestCase + include SchemaDumpingHelper class Ltree < ActiveRecord::Base self.table_name = 'ltrees' end @@ -43,4 +45,9 @@ class PostgresqlLtreeTest < ActiveRecord::TestCase ltree = Ltree.first assert_equal '1.2.3', ltree.path end + + def test_schema_dump_with_shorthand + output = dump_table_schema("ltrees") + assert_match %r[t.ltree "path"], output + end end diff --git a/activerecord/test/cases/adapters/postgresql/network_test.rb b/activerecord/test/cases/adapters/postgresql/network_test.rb index 4f4c1103fa..73e0fb5acd 100644 --- a/activerecord/test/cases/adapters/postgresql/network_test.rb +++ b/activerecord/test/cases/adapters/postgresql/network_test.rb @@ -1,8 +1,22 @@ # encoding: utf-8 require "cases/helper" +require 'support/schema_dumping_helper' class PostgresqlNetworkTest < ActiveRecord::TestCase - class PostgresqlNetworkAddress < ActiveRecord::Base + include SchemaDumpingHelper + class PostgresqlNetworkAddress < ActiveRecord::Base; end + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table('postgresql_network_addresses') do |t| + t.inet 'inet_address', default: "192.168.1.1" + t.cidr 'cidr_address', default: "192.168.1.0/24" + t.macaddr 'mac_address', default: "ff:ff:ff:ff:ff:ff" + end + end + + teardown do + @connection.execute 'DROP TABLE IF EXISTS postgresql_network_addresses' end def test_cidr_column @@ -68,4 +82,11 @@ class PostgresqlNetworkTest < ActiveRecord::TestCase assert_nil invalid_address.cidr_address_before_type_cast assert_nil invalid_address.inet_address_before_type_cast end + + def test_schema_dump_with_shorthand + output = dump_table_schema("postgresql_network_addresses") + assert_match %r{t.inet\s+"inet_address",\s+default: "192.168.1.1"}, output + assert_match %r{t.cidr\s+"cidr_address",\s+default: "192.168.1.0/24"}, output + assert_match %r{t.macaddr\s+"mac_address",\s+default: "ff:ff:ff:ff:ff:ff"}, output + end end diff --git a/activerecord/test/cases/adapters/postgresql/numbers_test.rb b/activerecord/test/cases/adapters/postgresql/numbers_test.rb new file mode 100644 index 0000000000..d90e9ccc66 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/numbers_test.rb @@ -0,0 +1,49 @@ +require "cases/helper" + +class PostgresqlNumberTest < ActiveRecord::TestCase + class PostgresqlNumber < ActiveRecord::Base; end + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table('postgresql_numbers') do |t| + t.column 'single', 'REAL' + t.column 'double', 'DOUBLE PRECISION' + end + end + + teardown do + @connection.execute 'DROP TABLE IF EXISTS postgresql_numbers' + end + + def test_data_type + assert_equal :float, PostgresqlNumber.columns_hash["single"].type + assert_equal :float, PostgresqlNumber.columns_hash["double"].type + end + + def test_values + @connection.execute("INSERT INTO postgresql_numbers (id, single, double) VALUES (1, 123.456, 123456.789)") + @connection.execute("INSERT INTO postgresql_numbers (id, single, double) VALUES (2, '-Infinity', 'Infinity')") + @connection.execute("INSERT INTO postgresql_numbers (id, single, double) VALUES (3, 123.456, 'NaN')") + + first, second, third = PostgresqlNumber.find(1, 2, 3) + + assert_equal 123.456, first.single + assert_equal 123456.789, first.double + assert_equal(-::Float::INFINITY, second.single) + assert_equal ::Float::INFINITY, second.double + assert_same ::Float::NAN, third.double + end + + def test_update + record = PostgresqlNumber.create! single: "123.456", double: "123456.789" + new_single = 789.012 + new_double = 789012.345 + record.single = new_single + record.double = new_double + record.save! + + record.reload + assert_equal new_single, record.single + assert_equal new_double, record.double + end +end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 92c9a13ee3..e99f1e2867 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -1,4 +1,5 @@ require "cases/helper" +require 'models/default' require 'support/schema_dumping_helper' class SchemaTest < ActiveRecord::TestCase @@ -88,7 +89,7 @@ class SchemaTest < ActiveRecord::TestCase end def test_schema_names - assert_equal ["public", "schema_1", "test_schema", "test_schema2"], @connection.schema_names + assert_equal ["public", "test_schema", "test_schema2"], @connection.schema_names end def test_create_schema @@ -460,3 +461,52 @@ class SchemaForeignKeyTest < ActiveRecord::TestCase @connection.execute "DROP SCHEMA IF EXISTS my_schema" end end + +class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase + setup do + @connection = ActiveRecord::Base.connection + @connection.execute "DROP SCHEMA IF EXISTS schema_1 CASCADE" + @connection.execute "CREATE SCHEMA schema_1" + @connection.execute "CREATE DOMAIN schema_1.text AS text" + @connection.execute "CREATE DOMAIN schema_1.varchar AS varchar" + @connection.execute "CREATE DOMAIN schema_1.bpchar AS bpchar" + + @old_search_path = @connection.schema_search_path + @connection.schema_search_path = "schema_1, pg_catalog" + @connection.create_table "defaults" do |t| + t.text "text_col", default: "some value" + t.string "string_col", default: "some value" + end + Default.reset_column_information + end + + teardown do + @connection.schema_search_path = @old_search_path + @connection.execute "DROP SCHEMA IF EXISTS schema_1 CASCADE" + Default.reset_column_information + end + + def test_text_defaults_in_new_schema_when_overriding_domain + assert_equal "some value", Default.new.text_col, "Default of text column was not correctly parsed" + end + + def test_string_defaults_in_new_schema_when_overriding_domain + assert_equal "some value", Default.new.string_col, "Default of string column was not correctly parsed" + end + + def test_bpchar_defaults_in_new_schema_when_overriding_domain + @connection.execute "ALTER TABLE defaults ADD bpchar_col bpchar DEFAULT 'some value'" + Default.reset_column_information + assert_equal "some value", Default.new.bpchar_col, "Default of bpchar column was not correctly parsed" + end + + def test_text_defaults_after_updating_column_default + @connection.execute "ALTER TABLE defaults ALTER COLUMN text_col SET DEFAULT 'some text'::schema_1.text" + assert_equal "some text", Default.new.text_col, "Default of text column was not correctly parsed after updating default using '::text' since postgreSQL will add parens to the default in db" + end + + def test_default_containing_quote_and_colons + @connection.execute "ALTER TABLE defaults ALTER COLUMN string_col SET DEFAULT 'foo''::bar'" + assert_equal "foo'::bar", Default.new.string_col + end +end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 5753da1173..fac21996ed 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -14,6 +14,7 @@ end class PostgresqlUUIDTest < ActiveRecord::TestCase include PostgresqlUUIDHelper + include SchemaDumpingHelper class UUIDType < ActiveRecord::Base self.table_name = "uuid_data_type" @@ -106,6 +107,11 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase assert_equal "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", uuid.guid end end + + def test_schema_dump_with_shorthand + output = dump_table_schema "uuid_data_type" + assert_match %r{t.uuid "guid"}, output + end end class PostgresqlLargeKeysTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/adapters/postgresql/xml_test.rb b/activerecord/test/cases/adapters/postgresql/xml_test.rb index 4165dd5ac9..5aba118518 100644 --- a/activerecord/test/cases/adapters/postgresql/xml_test.rb +++ b/activerecord/test/cases/adapters/postgresql/xml_test.rb @@ -1,7 +1,9 @@ # encoding: utf-8 require 'cases/helper' +require 'support/schema_dumping_helper' class PostgresqlXMLTest < ActiveRecord::TestCase + include SchemaDumpingHelper class XmlDataType < ActiveRecord::Base self.table_name = 'xml_data_type' end @@ -45,4 +47,9 @@ class PostgresqlXMLTest < ActiveRecord::TestCase XmlDataType.update_all(payload: "<bar>baz</bar>") assert_equal "<bar>baz</bar>", data.reload.payload end + + def test_schema_dump_with_shorthand + output = dump_table_schema("xml_data_type") + assert_match %r{t.xml "payload"}, output + end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 4321a198ca..db8fd92c1f 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -503,8 +503,8 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both - author = Author.all.merge!(:includes => :special_nonexistant_post_comments, :order => 'authors.id').first - assert_equal [], author.special_nonexistant_post_comments + author = Author.all.merge!(:includes => :special_nonexistent_post_comments, :order => 'authors.id').first + assert_equal [], author.special_nonexistent_post_comments end def test_eager_with_has_many_through_join_model_with_conditions diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 19d1aa87a8..f8772547a2 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -15,6 +15,7 @@ require 'models/essay' require 'models/owner' require 'models/post' require 'models/comment' +require 'models/categorization' class HasOneThroughAssociationsTest < ActiveRecord::TestCase fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans, diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index 9938302885..b3fe759ad9 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -102,7 +102,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase def test_find_with_conditions_on_reflection assert !posts(:welcome).comments.empty? - assert Post.joins(:nonexistant_comments).where(:id => posts(:welcome).id).empty? # [sic!] + assert Post.joins(:nonexistent_comments).where(:id => posts(:welcome).id).empty? # [sic!] end def test_find_with_conditions_on_through_reflection diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 140ccdac72..9918601623 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -444,7 +444,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_has_many_through_uses_conditions_specified_on_the_has_many_association author = Author.first assert author.comments.present? - assert author.nonexistant_comments.blank? + assert author.nonexistent_comments.blank? end def test_has_many_through_uses_correct_attributes diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 098d5b8451..67bb405629 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -898,93 +898,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 'a text field', default.char3 end end - - class Geometric < ActiveRecord::Base; end - def test_geometric_content - - # accepted format notes: - # ()'s aren't required - # values can be a mix of float or integer - - g = Geometric.new( - :a_point => '(5.0, 6.1)', - #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql - :a_line_segment => '(2.0, 3), (5.5, 7.0)', - :a_box => '2.0, 3, 5.5, 7.0', - :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path - :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', - :a_circle => '<(5.3, 10.4), 2>' - ) - - assert g.save - - # Reload and check that we have all the geometric attributes. - h = Geometric.find(g.id) - - assert_equal [5.0, 6.1], h.a_point - assert_equal '[(2,3),(5.5,7)]', h.a_line_segment - assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner - assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path - assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon - assert_equal '<(5.3,10.4),2>', h.a_circle - - # use a geometric function to test for an open path - objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id] - - assert_equal true, objs[0].isopen - - # test alternate formats when defining the geometric types - - g = Geometric.new( - :a_point => '5.0, 6.1', - #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql - :a_line_segment => '((2.0, 3), (5.5, 7.0))', - :a_box => '(2.0, 3), (5.5, 7.0)', - :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path - :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0', - :a_circle => '((5.3, 10.4), 2)' - ) - - assert g.save - - # Reload and check that we have all the geometric attributes. - h = Geometric.find(g.id) - - assert_equal [5.0, 6.1], h.a_point - assert_equal '[(2,3),(5.5,7)]', h.a_line_segment - assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner - assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path - assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon - assert_equal '<(5.3,10.4),2>', h.a_circle - - # use a geometric function to test for an closed path - objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id] - - assert_equal true, objs[0].isclosed - - # test native ruby formats when defining the geometric types - g = Geometric.new( - :a_point => [5.0, 6.1], - #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql - :a_line_segment => '((2.0, 3), (5.5, 7.0))', - :a_box => '(2.0, 3), (5.5, 7.0)', - :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path - :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0', - :a_circle => '((5.3, 10.4), 2)' - ) - - assert g.save - - # Reload and check that we have all the geometric attributes. - h = Geometric.find(g.id) - - assert_equal [5.0, 6.1], h.a_point - assert_equal '[(2,3),(5.5,7)]', h.a_line_segment - assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner - assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path - assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon - assert_equal '<(5.3,10.4),2>', h.a_circle - end end class NumericData < ActiveRecord::Base diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb index 1892958270..e9bc583bf4 100644 --- a/activerecord/test/cases/defaults_test.rb +++ b/activerecord/test/cases/defaults_test.rb @@ -198,48 +198,3 @@ if current_adapter?(:MysqlAdapter, :Mysql2Adapter) end end end - -if current_adapter?(:PostgreSQLAdapter) - class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase - def setup - @connection = ActiveRecord::Base.connection - - @old_search_path = @connection.schema_search_path - @connection.schema_search_path = "schema_1, pg_catalog" - @connection.create_table "defaults" do |t| - t.text "text_col", :default => "some value" - t.string "string_col", :default => "some value" - end - Default.reset_column_information - end - - def test_text_defaults_in_new_schema_when_overriding_domain - assert_equal "some value", Default.new.text_col, "Default of text column was not correctly parse" - end - - def test_string_defaults_in_new_schema_when_overriding_domain - assert_equal "some value", Default.new.string_col, "Default of string column was not correctly parse" - end - - def test_bpchar_defaults_in_new_schema_when_overriding_domain - @connection.execute "ALTER TABLE defaults ADD bpchar_col bpchar DEFAULT 'some value'" - Default.reset_column_information - assert_equal "some value", Default.new.bpchar_col, "Default of bpchar column was not correctly parse" - end - - def test_text_defaults_after_updating_column_default - @connection.execute "ALTER TABLE defaults ALTER COLUMN text_col SET DEFAULT 'some text'::schema_1.text" - assert_equal "some text", Default.new.text_col, "Default of text column was not correctly parse after updating default using '::text' since postgreSQL will add parens to the default in db" - end - - def test_default_containing_quote_and_colons - @connection.execute "ALTER TABLE defaults ALTER COLUMN string_col SET DEFAULT 'foo''::bar'" - assert_equal "foo'::bar", Default.new.string_col - end - - teardown do - @connection.schema_search_path = @old_search_path - Default.reset_column_information - end - end -end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index a607c1319f..f24b30c685 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -489,6 +489,12 @@ class FinderTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordNotFound) { Topic.where(topics: { approved: true }).find(1) } end + def test_find_on_combined_explicit_and_hashed_table_names + assert Topic.where('topics.approved' => false, topics: { author_name: "David" }).find(1) + assert_raise(ActiveRecord::RecordNotFound) { Topic.where('topics.approved' => true, topics: { author_name: "David" }).find(1) } + assert_raise(ActiveRecord::RecordNotFound) { Topic.where('topics.approved' => false, topics: { author_name: "Melanie" }).find(1) } + end + def test_find_with_hash_conditions_on_joined_table firms = Firm.joins(:account).where(:accounts => { :credit_limit => 50 }) assert_equal 1, firms.size diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 8731531f54..01c686f934 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -253,77 +253,6 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_no_match %r{enable_extension}, output end end - - def test_schema_dump_includes_xml_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_xml_data_type"} =~ output - assert_match %r{t.xml "data"}, output - end - end - - def test_schema_dump_includes_inet_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_network_addresses"} =~ output - assert_match %r{t.inet\s+"inet_address",\s+default: "192.168.1.1"}, output - end - end - - def test_schema_dump_includes_cidr_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_network_addresses"} =~ output - assert_match %r{t.cidr\s+"cidr_address",\s+default: "192.168.1.0/24"}, output - end - end - - def test_schema_dump_includes_macaddr_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_network_addresses"} =~ output - assert_match %r{t.macaddr\s+"mac_address",\s+default: "ff:ff:ff:ff:ff:ff"}, output - end - end - - def test_schema_dump_includes_uuid_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_uuids"} =~ output - assert_match %r{t.uuid "guid"}, output - end - end - - def test_schema_dump_includes_hstores_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_hstores"} =~ output - assert_match %r[t.hstore "hash_store", default: {}], output - end - end - - def test_schema_dump_includes_citext_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_citext"} =~ output - assert_match %r[t.citext "text_citext"], output - end - end - - def test_schema_dump_includes_ltrees_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_ltrees"} =~ output - assert_match %r[t.ltree "path"], output - end - end - - def test_schema_dump_includes_arrays_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_arrays"} =~ output - assert_match %r[t.text\s+"nicknames",\s+array: true], output - assert_match %r[t.integer\s+"commission_by_quarter",\s+array: true], output - end - end - - def test_schema_dump_includes_tsvector_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_tsvectors"} =~ output - assert_match %r{t.tsvector "text_vector"}, output - end - end end def test_schema_dump_keeps_large_precision_integer_columns_as_decimal diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 5bdebe6ac7..0738df1b54 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -143,6 +143,14 @@ class DefaultScopingTest < ActiveRecord::TestCase expected_5 = Developer.order('salary DESC').collect(&:name) received_5 = DeveloperOrderedBySalary.where.not("name" => ["Jamis", "David"]).unscope(where: :name).collect(&:name) assert_equal expected_5, received_5 + + expected_6 = Developer.order('salary DESC').collect(&:name) + received_6 = DeveloperOrderedBySalary.where(Developer.arel_table['name'].eq('David')).unscope(where: :name).collect(&:name) + assert_equal expected_6, received_6 + + expected_7 = Developer.order('salary DESC').collect(&:name) + received_7 = DeveloperOrderedBySalary.where(Developer.arel_table[:name].eq('David')).unscope(where: :name).collect(&:name) + assert_equal expected_7, received_7 end def test_unscope_multiple_where_clauses diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb index c6b58d469d..524f59876e 100644 --- a/activerecord/test/cases/validations/uniqueness_validation_test.rb +++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb @@ -30,11 +30,6 @@ class ReplyWithTitleObject < Reply def title; ReplyTitle.new; end end -class Employee < ActiveRecord::Base - self.table_name = 'postgresql_arrays' - validates_uniqueness_of :nicknames -end - class TopicWithUniqEvent < Topic belongs_to :event, foreign_key: :parent_id validates :event, uniqueness: true @@ -378,18 +373,6 @@ class UniquenessValidationTest < ActiveRecord::TestCase } end - if current_adapter? :PostgreSQLAdapter - def test_validate_uniqueness_with_array_column - e1 = Employee.create("nicknames" => ["john", "johnny"], "commission_by_quarter" => [1000, 1200]) - assert e1.persisted?, "Saving e1" - - e2 = Employee.create("nicknames" => ["john", "johnny"], "commission_by_quarter" => [2200]) - assert !e2.persisted?, "e2 shouldn't be valid" - assert e2.errors[:nicknames].any?, "Should have errors for nicknames" - assert_equal ["has already been taken"], e2.errors[:nicknames], "Should have uniqueness message for nicknames" - end - end - def test_validate_uniqueness_on_existing_relation event = Event.create assert TopicWithUniqEvent.create(event: event).valid? diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 3da3a1fd59..8c1f14bd36 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -50,9 +50,9 @@ class Author < ActiveRecord::Base has_many :sti_posts, :class_name => 'StiPost' has_many :sti_post_comments, :through => :sti_posts, :source => :comments - has_many :special_nonexistant_posts, -> { where("posts.body = 'nonexistant'") }, :class_name => "SpecialPost" - has_many :special_nonexistant_post_comments, -> { where('comments.post_id' => 0) }, :through => :special_nonexistant_posts, :source => :comments - has_many :nonexistant_comments, :through => :posts + has_many :special_nonexistent_posts, -> { where("posts.body = 'nonexistent'") }, :class_name => "SpecialPost" + has_many :special_nonexistent_post_comments, -> { where('comments.post_id' => 0) }, :through => :special_nonexistent_posts, :source => :comments + has_many :nonexistent_comments, :through => :posts has_many :hello_posts, -> { where "posts.body = 'hello'" }, :class_name => "Post" has_many :hello_post_comments, :through => :hello_posts, :source => :comments diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index a9996e5236..57e1818e49 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -80,7 +80,7 @@ class Post < ActiveRecord::Base has_one :very_special_comment_with_post, -> { includes(:post) }, :class_name => "VerySpecialComment" has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order('posts.id') }, class_name: "VerySpecialComment" has_many :special_comments - has_many :nonexistant_comments, -> { where 'comments.id < 0' }, :class_name => 'Comment' + has_many :nonexistent_comments, -> { where 'comments.id < 0' }, :class_name => 'Comment' has_many :special_comments_ratings, :through => :special_comments, :source => :ratings has_many :special_comments_ratings_taggings, :through => :special_comments_ratings, :source => :taggings diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index 7c44409d34..55360b9aa2 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -1,9 +1,7 @@ ActiveRecord::Schema.define do - %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times - postgresql_network_addresses postgresql_uuids postgresql_ltrees postgresql_oids postgresql_xml_data_type defaults - geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent - postgresql_citext).each do |table_name| + %w(postgresql_times postgresql_oids defaults postgresql_timestamp_with_zones + postgresql_partitioned_table postgresql_partitioned_table_parent).each do |table_name| execute "DROP TABLE IF EXISTS #{quote_table_name table_name}" end @@ -14,8 +12,6 @@ ActiveRecord::Schema.define do execute 'DROP FUNCTION IF EXISTS partitioned_insert_trigger()' - execute "DROP SCHEMA IF EXISTS schema_1 CASCADE" - %w(accounts_id_seq developers_id_seq projects_id_seq topics_id_seq customers_id_seq orders_id_seq).each do |seq_name| execute "SELECT setval('#{seq_name}', 100)" end @@ -39,82 +35,6 @@ ActiveRecord::Schema.define do ); _SQL - execute "CREATE SCHEMA schema_1" - execute "CREATE DOMAIN schema_1.text AS text" - execute "CREATE DOMAIN schema_1.varchar AS varchar" - execute "CREATE DOMAIN schema_1.bpchar AS bpchar" - - execute <<_SQL - CREATE TABLE geometrics ( - id serial primary key, - a_point point, - -- a_line line, (the line type is currently not implemented in postgresql) - a_line_segment lseg, - a_box box, - a_path path, - a_polygon polygon, - a_circle circle - ); -_SQL - - execute <<_SQL - CREATE TABLE postgresql_arrays ( - id SERIAL PRIMARY KEY, - commission_by_quarter INTEGER[], - nicknames TEXT[] - ); -_SQL - - execute <<_SQL - CREATE TABLE postgresql_uuids ( - id SERIAL PRIMARY KEY, - guid uuid, - compact_guid uuid - ); -_SQL - - execute <<_SQL - CREATE TABLE postgresql_tsvectors ( - id SERIAL PRIMARY KEY, - text_vector tsvector - ); -_SQL - - if 't' == select_value("select 'hstore'=ANY(select typname from pg_type)") - execute <<_SQL - CREATE TABLE postgresql_hstores ( - id SERIAL PRIMARY KEY, - hash_store hstore default ''::hstore - ); -_SQL - end - - if 't' == select_value("select 'ltree'=ANY(select typname from pg_type)") - execute <<_SQL - CREATE TABLE postgresql_ltrees ( - id SERIAL PRIMARY KEY, - path ltree - ); -_SQL - end - - if 't' == select_value("select 'citext'=ANY(select typname from pg_type)") - execute <<_SQL - CREATE TABLE postgresql_citext ( - id SERIAL PRIMARY KEY, - text_citext citext default ''::citext - ); -_SQL - end - - execute <<_SQL - CREATE TABLE postgresql_numbers ( - id SERIAL PRIMARY KEY, - single REAL, - double DOUBLE PRECISION - ); -_SQL - execute <<_SQL CREATE TABLE postgresql_times ( id SERIAL PRIMARY KEY, @@ -124,15 +44,6 @@ _SQL _SQL execute <<_SQL - CREATE TABLE postgresql_network_addresses ( - id SERIAL PRIMARY KEY, - cidr_address CIDR default '192.168.1.0/24', - inet_address INET default '192.168.1.1', - mac_address MACADDR default 'ff:ff:ff:ff:ff:ff' - ); -_SQL - - execute <<_SQL CREATE TABLE postgresql_oids ( id SERIAL PRIMARY KEY, obj_id OID diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 810fc22cf5..72bdd0c509 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1 +1,9 @@ +* Added `#verified` and `#valid_message?` methods to `ActiveSupport::MessageVerifier` + + Previously, the only way to decode a message with `ActiveSupport::MessageVerifier` was to use `#verify`, which would raise an exception on invalid messages. Now, `#verified` will return either `false` when it encounters an error or the message. + + Previously, there was no way to check if a message's format was valid without attempting to decode it. `#valid_message?` is a boolean convenience method that checks whether the message is valid without actually decoding it. + + *Logan Leger* + Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activesupport/CHANGELOG.md) for previous changes. diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index a8a8de5672..2c86190e7f 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -34,22 +34,33 @@ module ActiveSupport @serializer = options[:serializer] || Marshal end - def verify(signed_message) - raise InvalidSignature if signed_message.blank? + # FIXME: Document this method + def valid_message?(signed_message) + return if signed_message.blank? data, digest = signed_message.split("--") - if data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data)) + data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data)) + end + + # FIXME: Document this method + def verified(signed_message) + if valid_message?(signed_message) begin + data = signed_message.split("--")[0] @serializer.load(decode(data)) rescue ArgumentError => argument_error - raise InvalidSignature if argument_error.message =~ %r{invalid base64} + return if argument_error.message =~ %r{invalid base64} raise end - else - raise InvalidSignature end end + # FIXME: Document this method + def verify(signed_message) + verified(signed_message) || raise(InvalidSignature) + end + + # FIXME: Document this method def generate(value) data = encode(@serializer.dump(value)) "#{data}--#{generate_digest(data)}" diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 28035bc428..68f40fbb28 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -27,21 +27,29 @@ class MessageVerifierTest < ActiveSupport::TestCase @data = { :some => "data", :now => Time.local(2010) } end + def test_valid_message + data, hash = @verifier.generate(@data).split("--") + assert !@verifier.valid_message?(nil) + assert !@verifier.valid_message?("") + assert !@verifier.valid_message?("#{data.reverse}--#{hash}") + assert !@verifier.valid_message?("#{data}--#{hash.reverse}") + assert !@verifier.valid_message?("purejunk") + end + def test_simple_round_tripping message = @verifier.generate(@data) + assert_equal @data, @verifier.verified(message) assert_equal @data, @verifier.verify(message) end - - def test_missing_signature_raises - assert_not_verified(nil) - assert_not_verified("") + + def test_verified_returns_false_on_invalid_message + assert !@verifier.verified("purejunk") end - - def test_tampered_data_raises - data, hash = @verifier.generate(@data).split("--") - assert_not_verified("#{data.reverse}--#{hash}") - assert_not_verified("#{data}--#{hash.reverse}") - assert_not_verified("purejunk") + + def test_verify_exception_on_invalid_message + assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do + @verifier.verify("purejunk") + end end def test_alternative_serialization_method @@ -50,6 +58,7 @@ class MessageVerifierTest < ActiveSupport::TestCase verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!", :serializer => JSONSerializer.new) message = verifier.generate({ :foo => 123, 'bar' => Time.utc(2010) }) exp = { "foo" => 123, "bar" => "2010-01-01T00:00:00.000Z" } + assert_equal exp, verifier.verified(message) assert_equal exp, verifier.verify(message) ensure ActiveSupport.use_standard_json_time_format = prev @@ -63,6 +72,11 @@ class MessageVerifierTest < ActiveSupport::TestCase # valid_message = "BAh7BjoIZm9vbzonTWVzc2FnZVZlcmlmaWVyVGVzdDo6QXV0b2xvYWRDbGFzcwY6CUBmb29JIghmb28GOgZFVA==--f3ef39a5241c365083770566dc7a9eb5d6ace914" exception = assert_raise(ArgumentError, NameError) do + @verifier.verified(valid_message) + end + assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass", + "undefined class/module MessageVerifierTest::AutoloadClass"], exception.message + exception = assert_raise(ArgumentError, NameError) do @verifier.verify(valid_message) end assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass", @@ -75,12 +89,6 @@ class MessageVerifierTest < ActiveSupport::TestCase end assert_equal exception.message, 'Secret should not be nil.' end - - def assert_not_verified(message) - assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do - @verifier.verify(message) - end - end end end diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index 737922c283..1e9107a8c6 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -544,6 +544,17 @@ Please refer to the [Changelog][action-view] for detailed changes. * Placeholder I18n follows the same convention as `label` I18n. ([Pull Request](https://github.com/rails/rails/pull/16438)) +* When calling the `process` helpers in an integration test the path needs to have + a leading slash. Previously you could omit it but that was a byproduct of the + implementation and not an intentional feature, e.g.: + + ```ruby + test "list all posts" do + get "/posts" + assert_response :success + end + ``` + Action Mailer ------------- diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb index d492e68357..bf3da1fc4d 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb @@ -10,7 +10,9 @@ ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', <% end -%> require "rails/test_help" -Rails.backtrace_cleaner.remove_silencers! +# Filter out Minitest backtrace while allowing backtrace from other libraries +# to be shown. +Minitest.backtrace_filter = Minitest::BacktraceFilter.new # Load support files Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb index af5f2707b1..2f82d1285d 100644 --- a/railties/lib/rails/tasks.rb +++ b/railties/lib/rails/tasks.rb @@ -1,3 +1,5 @@ +require 'rake' + # Load Rails Rakefile extensions %w( annotations diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index f24fe36670..679190dad4 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -1158,7 +1158,7 @@ module ApplicationTests app_file 'config/environments/development.rb', <<-RUBY Rails.application.configure do - config.paths.add 'config/database', with: 'config/nonexistant.yml' + config.paths.add 'config/database', with: 'config/nonexistent.yml' config.paths['config/database'] << 'config/database.yml' end RUBY diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 4329c6e1a4..dbc87be614 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -57,6 +57,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "test/test_helper.rb" do |content| assert_match(/require.+test\/dummy\/config\/environment/, content) assert_match(/ActiveRecord::Migrator\.migrations_paths.+test\/dummy\/db\/migrate/, content) + assert_match(/Minitest\.backtrace_filter = Minitest::BacktraceFilter\.new/, content) end assert_file "test/bukkits_test.rb", /assert_kind_of Module, Bukkits/ end |