From 69fec3ab3fe1135ba949514b482c43ae659a3fa9 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 20 Jun 2010 23:29:37 +0200 Subject: adds parens to silence a warning in the test suite of AS --- activesupport/test/descendants_tracker_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/test/descendants_tracker_test.rb b/activesupport/test/descendants_tracker_test.rb index 3a424180de..ff24e310de 100644 --- a/activesupport/test/descendants_tracker_test.rb +++ b/activesupport/test/descendants_tracker_test.rb @@ -35,7 +35,7 @@ class DescendantsTrackerTest < Test::Unit::TestCase end def test_clear_with_autoloaded_parent_children_and_granchildren - mark_as_autoloaded *ALL do + mark_as_autoloaded(*ALL) do ActiveSupport::DescendantsTracker.clear assert ActiveSupport::DescendantsTracker.descendants.slice(*ALL).empty? end -- cgit v1.2.3 From b5fe014fdcc285f3bcb8779c4f7cfbc5a820856f Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 21 Jun 2010 00:39:32 +0200 Subject: files in the lib directory are no longer autoloaded Conceptually, the lib directory is closer 3rd party libraries than to the application itself. Thus, Rails adds it to Ruby's load path ($LOAD_PATH, $:) but it is no longer included in dependencies' load paths. To enable autoloading back put this in your config/application.rb config.load_paths += %W( #{config.root}/lib ) --- railties/lib/rails/engine/configuration.rb | 6 +++++- railties/test/application/initializers/load_path_test.rb | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index 446fe0bda9..620a723a04 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -20,15 +20,19 @@ module Rails paths.app.models "app/models", :eager_load => true paths.app.mailers "app/mailers", :eager_load => true paths.app.views "app/views" - paths.lib "lib", :load_path => true + + paths.lib "lib" paths.lib.tasks "lib/tasks", :glob => "**/*.rake" + paths.config "config" paths.config.initializers "config/initializers", :glob => "**/*.rb" paths.config.locales "config/locales", :glob => "*.{rb,yml}" paths.config.routes "config/routes.rb" + paths.public "public" paths.public.javascripts "public/javascripts" paths.public.stylesheets "public/stylesheets" + paths end end diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb index 714d62311d..d9aac8719c 100644 --- a/railties/test/application/initializers/load_path_test.rb +++ b/railties/test/application/initializers/load_path_test.rb @@ -19,7 +19,7 @@ module ApplicationTests assert $:.include?("#{app_path}/app/models") end - test "initializing an application allows to load code on lib path inside application class definitation" do + test "initializing an application allows to load code on lib path inside application class definition" do app_file "lib/foo.rb", <<-RUBY module Foo; end RUBY @@ -60,6 +60,8 @@ module ApplicationTests add_to_config <<-RUBY config.root = "#{app_path}" + config.cache_classes = true + config.load_paths << "#{app_path}/lib" config.eager_load_paths << "#{app_path}/lib" RUBY -- cgit v1.2.3 From 746a3856782293b7b81706498ebaf58b51da294e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 21 Jun 2010 01:03:26 +0200 Subject: Remove unused webrick_server file. --- railties/lib/rails/webrick_server.rb | 156 ----------------------------------- 1 file changed, 156 deletions(-) delete mode 100644 railties/lib/rails/webrick_server.rb diff --git a/railties/lib/rails/webrick_server.rb b/railties/lib/rails/webrick_server.rb deleted file mode 100644 index f3b74c28d3..0000000000 --- a/railties/lib/rails/webrick_server.rb +++ /dev/null @@ -1,156 +0,0 @@ -# Donated by Florian Gross - -require 'webrick' -require 'cgi' -require 'stringio' -require 'dispatcher' - -include WEBrick - -class CGI #:nodoc: - def stdinput - @stdin || $stdin - end - - def env_table - @env_table || ENV - end - - def initialize(type = "query", table = nil, stdin = nil) - @env_table, @stdin = table, stdin - - if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE") - Apache.request.setup_cgi_env - end - - extend QueryExtension - @multipart = false - if defined?(CGI_PARAMS) - warn "do not use CGI_PARAMS and CGI_COOKIES" - @params = CGI_PARAMS.dup - @cookies = CGI_COOKIES.dup - else - initialize_query() # set @params, @cookies - end - @output_cookies = nil - @output_hidden = nil - end -end - -# A custom dispatch servlet for use with WEBrick. It dispatches requests -# (using the Rails Dispatcher) to the appropriate controller/action. By default, -# it restricts WEBrick to a managing a single Rails request at a time, but you -# can change this behavior by setting ActionController::Base.allow_concurrency -# to true. -class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet - # Start the WEBrick server with the given options, mounting the - # DispatchServlet at /. - def self.dispatch(options = {}) - Socket.do_not_reverse_lookup = true # patch for OS X - - params = { :Port => options[:port].to_i, - :ServerType => options[:server_type], - :BindAddress => options[:ip] } - params[:MimeTypes] = options[:mime_types] if options[:mime_types] - - server = WEBrick::HTTPServer.new(params) - server.mount('/', DispatchServlet, options) - - trap("INT") { server.shutdown } - server.start - end - - def initialize(server, options) #:nodoc: - @server_options = options - @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root]) - # Change to the Rails.root, since Webrick::Daemon.start does a Dir::cwd("/") - # OPTIONS['working_directory'] is an absolute path of the Rails.root, set in railties/lib/commands/servers/webrick.rb - Dir.chdir(OPTIONS['working_directory']) if defined?(OPTIONS) && File.directory?(OPTIONS['working_directory']) - super - end - - def service(req, res) #:nodoc: - unless handle_file(req, res) - unless handle_dispatch(req, res) - raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." - end - end - end - - def handle_file(req, res) #:nodoc: - begin - req = req.dup - path = req.path.dup - - # Add .html if the last path piece has no . in it - path << '.html' if path != '/' && (%r{(^|/)[^./]+$} =~ path) - path.gsub!('+', ' ') # Unescape + since FileHandler doesn't do so. - - req.instance_variable_set(:@path_info, path) # Set the modified path... - - @file_handler.send(:service, req, res) - return true - rescue HTTPStatus::PartialContent, HTTPStatus::NotModified => err - res.set_error(err) - return true - rescue => err - return false - end - end - - def handle_dispatch(req, res, origin = nil) #:nodoc: - data = StringIO.new - Dispatcher.dispatch( - CGI.new("query", create_env_table(req, origin), StringIO.new(req.body || "")), - ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, - data - ) - - header, body = extract_header_and_body(data) - - set_charset(header) - assign_status(res, header) - res.cookies.concat(header.delete('set-cookie') || []) - header.each { |key, val| res[key] = val.join(", ") } - - res.body = body - return true - rescue => err - p err, err.backtrace - return false - end - - private - def create_env_table(req, origin) - env = req.meta_vars.clone - env.delete "SCRIPT_NAME" - env["QUERY_STRING"] = req.request_uri.query - env["REQUEST_URI"] = origin if origin - return env - end - - def extract_header_and_body(data) - data.rewind - data = data.read - - raw_header, body = *data.split(/^[\xd\xa]{2}/on, 2) - header = WEBrick::HTTPUtils::parse_header(raw_header) - - return header, body - end - - def set_charset(header) - ct = header["content-type"] - if ct.any? { |x| x =~ /^text\// } && ! ct.any? { |x| x =~ /charset=/ } - ch = @server_options[:charset] || "UTF-8" - ct.find { |x| x =~ /^text\// } << ("; charset=" + ch) - end - end - - def assign_status(res, header) - if /^(\d+)/ =~ header['status'][0] - res.status = $1.to_i - header.delete('status') - end - end -end -- cgit v1.2.3 From f81666698b703be69607a8ce1abb7d2347ea3667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 21 Jun 2010 01:08:50 +0200 Subject: Alias app to build_middleware_stack for clarity. --- railties/lib/rails/application.rb | 1 + railties/lib/rails/application/finisher.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 8b8ef20b1f..eca6802297 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -160,6 +160,7 @@ module Rails config.middleware.build(routes) end end + alias :build_middleware_stack :app def call(env) app.call(env.reverse_merge!(env_defaults)) diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index d7ff489336..11a3329de6 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -32,7 +32,7 @@ module Rails end initializer :build_middleware_stack do - app + build_middleware_stack end initializer :eager_load! do -- cgit v1.2.3 From 756d77622b20d671605d097c029e0414987765d2 Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion)" Date: Thu, 17 Jun 2010 15:41:23 +0200 Subject: Allow instrumentation of cache hits and misses. [#4888 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/cache.rb | 65 +++++++++++++++++++------------ 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index f04544c0c5..bef9c98ecf 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -267,27 +267,39 @@ module ActiveSupport # :bar # end # cache.fetch("foo") # => "bar" - def fetch(name, options = nil, &block) - options = merged_options(options) - key = namespaced_key(name, options) - entry = instrument(:read, name, options) { read_entry(key, options) } unless options[:force] - if entry && entry.expired? - race_ttl = options[:race_condition_ttl].to_f - if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl - entry.expires_at = Time.now + race_ttl - write_entry(key, entry, :expires_in => race_ttl * 2) - else - delete_entry(key, options) + def fetch(name, options = nil) + if block_given? + options = merged_options(options) + key = namespaced_key(name, options) + unless options[:force] + entry = instrument(:read, name, options) do |payload| + payload[:super_operation] = :fetch if payload + read_entry(key, options) + end + end + if entry && entry.expired? + race_ttl = options[:race_condition_ttl].to_f + if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl + entry.expires_at = Time.now + race_ttl + write_entry(key, entry, :expires_in => race_ttl * 2) + else + delete_entry(key, options) + end + entry = nil end - entry = nil - end - if entry - entry.value - elsif block_given? - result = instrument(:generate, name, options, &block) - write(name, result, options) - result + if entry + instrument(:fetch_hit, name, options) { |payload| } + entry.value + else + result = instrument(:generate, name, options) do |payload| + yield + end + write(name, result, options) + result + end + else + read(name, options) end end @@ -299,16 +311,19 @@ module ActiveSupport def read(name, options = nil) options = merged_options(options) key = namespaced_key(name, options) - instrument(:read, name, options) do + instrument(:read, name, options) do |payload| entry = read_entry(key, options) if entry if entry.expired? delete_entry(key, options) + payload[:hit] = false if payload nil else + payload[:hit] = true if payload entry.value end else + payload[:hit] = false if payload nil end end @@ -345,7 +360,7 @@ module ActiveSupport # +options+. def write(name, value, options = nil) options = merged_options(options) - instrument(:write, name, options) do + instrument(:write, name, options) do |payload| entry = Entry.new(value, options) write_entry(namespaced_key(name, options), entry, options) end @@ -356,7 +371,7 @@ module ActiveSupport # Options are passed to the underlying cache implementation. def delete(name, options = nil) options = merged_options(options) - instrument(:delete, name) do + instrument(:delete, name) do |payload| delete_entry(namespaced_key(name, options), options) end end @@ -366,7 +381,7 @@ module ActiveSupport # Options are passed to the underlying cache implementation. def exist?(name, options = nil) options = merged_options(options) - instrument(:exist?, name) do + instrument(:exist?, name) do |payload| entry = read_entry(namespaced_key(name, options), options) if entry && !entry.expired? true @@ -502,9 +517,9 @@ module ActiveSupport if self.class.instrument payload = { :key => key } payload.merge!(options) if options.is_a?(Hash) - ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield } + ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) } else - yield + yield(nil) end end -- cgit v1.2.3 From 7b7cedcb8d53110492e7d51405986f3e8e899fa4 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 20 Jun 2010 15:51:49 -0300 Subject: Don't waste time building relations if there are no values presents. [#4860 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation/query_methods.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 50e94134f5..60fa839df2 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -130,14 +130,14 @@ module ActiveRecord end end - arel = arel.having(*@having_values.uniq.select{|h| h.present?}) + arel = arel.having(*@having_values.uniq.select{|h| h.present?}) if @having_values.present? arel = arel.take(@limit_value) if @limit_value.present? arel = arel.skip(@offset_value) if @offset_value.present? - arel = arel.group(*@group_values.uniq.select{|g| g.present?}) + arel = arel.group(*@group_values.uniq.select{|g| g.present?}) if @group_values.present? - arel = arel.order(*@order_values.uniq.select{|o| o.present?}.map(&:to_s)) + arel = arel.order(*@order_values.uniq.select{|o| o.present?}.map(&:to_s)) if @order_values.present? selects = @select_values.uniq @@ -150,7 +150,7 @@ module ActiveRecord arel = arel.project(@klass.quoted_table_name + '.*') end - arel = @from_value.present? ? arel.from(@from_value) : arel.from(@klass.quoted_table_name) + arel = arel.from(@from_value) if @from_value.present? case @lock_value when TrueClass -- cgit v1.2.3 From b311dbb0ba2f3679a21fd7cb53b867c580e1e809 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 21 Jun 2010 01:46:24 +0200 Subject: Revert "files in the lib directory are no longer autoloaded" This patch is not consistent since it leaves similar directories in load_paths, needs more thought. This reverts commit b5fe014fdcc285f3bcb8779c4f7cfbc5a820856f. --- railties/lib/rails/engine/configuration.rb | 6 +----- railties/test/application/initializers/load_path_test.rb | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index 620a723a04..446fe0bda9 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -20,19 +20,15 @@ module Rails paths.app.models "app/models", :eager_load => true paths.app.mailers "app/mailers", :eager_load => true paths.app.views "app/views" - - paths.lib "lib" + paths.lib "lib", :load_path => true paths.lib.tasks "lib/tasks", :glob => "**/*.rake" - paths.config "config" paths.config.initializers "config/initializers", :glob => "**/*.rb" paths.config.locales "config/locales", :glob => "*.{rb,yml}" paths.config.routes "config/routes.rb" - paths.public "public" paths.public.javascripts "public/javascripts" paths.public.stylesheets "public/stylesheets" - paths end end diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb index d9aac8719c..714d62311d 100644 --- a/railties/test/application/initializers/load_path_test.rb +++ b/railties/test/application/initializers/load_path_test.rb @@ -19,7 +19,7 @@ module ApplicationTests assert $:.include?("#{app_path}/app/models") end - test "initializing an application allows to load code on lib path inside application class definition" do + test "initializing an application allows to load code on lib path inside application class definitation" do app_file "lib/foo.rb", <<-RUBY module Foo; end RUBY @@ -60,8 +60,6 @@ module ApplicationTests add_to_config <<-RUBY config.root = "#{app_path}" - config.cache_classes = true - config.load_paths << "#{app_path}/lib" config.eager_load_paths << "#{app_path}/lib" RUBY -- cgit v1.2.3 From 45b263cbf1256f09f5bd9c70303b3d5162c066d4 Mon Sep 17 00:00:00 2001 From: rohit Date: Mon, 21 Jun 2010 13:58:02 +0530 Subject: Initialize @last_update_at in file_update_checker to hide warnings in AS test suite. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/file_update_checker.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index c0b5ca4deb..5f5b264eb9 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -19,7 +19,7 @@ module ActiveSupport def initialize(paths, calculate=false, &block) @paths = paths @block = block - @last_update_at = updated_at if calculate + @last_update_at = calculate ? updated_at : nil end def updated_at @@ -34,4 +34,4 @@ module ActiveSupport end end end -end \ No newline at end of file +end -- cgit v1.2.3 From ead72b319f781ae3767a8695e3e29e9249388f7f Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 21 Jun 2010 00:46:23 +0200 Subject: Changed `ruby /path/to/rails myapp --dev` to `ruby /path/to/rails new myapp --dev` in the "Thor is not avalable" message. [#4915 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/generators/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb index bd2260fc29..67a9a6030d 100644 --- a/railties/lib/rails/generators/base.rb +++ b/railties/lib/rails/generators/base.rb @@ -3,7 +3,7 @@ begin rescue LoadError puts "Thor is not available.\nIf you ran this command from a git checkout " \ "of Rails, please make sure thor is installed,\nand run this command " \ - "as `ruby /path/to/rails myapp --dev`" + "as `ruby /path/to/rails new myapp --dev`" exit end -- cgit v1.2.3 From 26392c4ac57e27c63984d47c6326c13f502d5786 Mon Sep 17 00:00:00 2001 From: Jeroen van Dijk Date: Wed, 19 May 2010 15:25:46 +0100 Subject: Make ActiveModel::Errors#add_on_blank and #add_on_empty accept an options hash and make various Validators pass their (filtered) options. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes it possible to pass additional options through Validators to message generation. E.g. plugin authors want to add validates_presence_of :foo, :format => "some format". Also, cleanup the :default vs :message options confusion in ActiveModel validation message generation. Also, deprecate ActiveModel::Errors#add_on_blank(attributes, custom_message) in favor of ActiveModel::Errors#add_on_blank(attributes, options). Also, refactoring of ActiveModel and ActiveRecord Validation tests. Test are a lot more DRY now. Better test coverage as well now. The first four points were reapplied from an older patch of Sven Fuchs which didn't apply cleanly anymore and was not complete yet. Signed-off-by: José Valim --- activemodel/lib/active_model/errors.rb | 52 ++- .../lib/active_model/validations/acceptance.rb | 4 +- .../lib/active_model/validations/confirmation.rb | 6 +- .../lib/active_model/validations/exclusion.rb | 5 +- activemodel/lib/active_model/validations/format.rb | 6 +- .../lib/active_model/validations/inclusion.rb | 5 +- activemodel/lib/active_model/validations/length.rb | 8 +- .../lib/active_model/validations/numericality.rb | 13 +- .../lib/active_model/validations/presence.rb | 2 +- .../i18n_generate_message_validation_test.rb | 82 ++-- .../test/cases/validations/i18n_validation_test.rb | 507 +++++++-------------- .../lib/active_record/validations/associated.rb | 2 +- .../lib/active_record/validations/uniqueness.rb | 2 +- .../i18n_generate_message_validation_test.rb | 12 +- .../test/cases/validations/i18n_validation_test.rb | 50 +- 15 files changed, 315 insertions(+), 441 deletions(-) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 9efb683547..b4660f3587 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -182,25 +182,43 @@ module ActiveModel # If +message+ is a proc, it will be called, allowing for things like Time.now to be used within an error. def add(attribute, message = nil, options = {}) message ||= :invalid - message = generate_message(attribute, message, options) if message.is_a?(Symbol) + + validation_conditionals = [:if, :unless, :on] + + message = generate_message(attribute, message, options.except(*validation_conditionals)) if message.is_a?(Symbol) + message = message.call if message.is_a?(Proc) self[attribute] << message end # Will add an error message to each of the attributes in +attributes+ that is empty. - def add_on_empty(attributes, custom_message = nil) + def add_on_empty(attributes, options = {}) + if options && !options.is_a?(Hash) + options = { :message => options } + ActiveSupport::Deprecation.warn \ + "ActiveModel::Errors#add_on_empty(attributes, custom_message) has been deprecated.\n" + + "Instead of passing a custom_message pass an options Hash { :message => custom_message }." + end + [attributes].flatten.each do |attribute| value = @base.send(:read_attribute_for_validation, attribute) is_empty = value.respond_to?(:empty?) ? value.empty? : false - add(attribute, :empty, :default => custom_message) unless !value.nil? && !is_empty + add(attribute, :empty, options) if value.nil? || is_empty end end # Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?). - def add_on_blank(attributes, custom_message = nil) + def add_on_blank(attributes, options = {}) + if options && !options.is_a?(Hash) + options = { :message => options } + ActiveSupport::Deprecation.warn \ + "ActiveModel::Errors#add_on_blank(attributes, custom_message) has been deprecated.\n" + + "Instead of passing a custom_message pass an options Hash { :message => custom_message }." + end + [attributes].flatten.each do |attribute| value = @base.send(:read_attribute_for_validation, attribute) - add(attribute, :blank, :default => custom_message) if value.blank? + add(attribute, :blank, options) if value.blank? end end @@ -262,18 +280,26 @@ module ActiveModel #
  • errors.attributes.title.blank
  • #
  • errors.messages.blank
  • # - def generate_message(attribute, message = :invalid, options = {}) - message, options[:default] = options[:default], message if options[:default].is_a?(Symbol) + + def generate_message(attribute, type = :invalid, options = {}) + type = options.delete(:message) if options[:message].is_a?(Symbol) + + if options[:default] + ActiveSupport::Deprecation.warn \ + "ActiveModel::Errors#generate_message(attributes, custom_message) has been deprecated.\n" + + "Use ActiveModel::Errors#generate_message(attributes, :message => 'your message') instead." + options[:message] = options.delete(:default) + end defaults = @base.class.lookup_ancestors.map do |klass| - [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.attributes.#{attribute}.#{message}", - :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.#{message}" ] + [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.attributes.#{attribute}.#{type}", + :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.#{type}" ] end - defaults << options.delete(:default) - defaults << :"#{@base.class.i18n_scope}.errors.messages.#{message}" - defaults << :"errors.attributes.#{attribute}.#{message}" - defaults << :"errors.messages.#{message}" + defaults << options.delete(:message) + defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" + defaults << :"errors.attributes.#{attribute}.#{type}" + defaults << :"errors.messages.#{type}" defaults.compact! defaults.flatten! diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb index 77c401e0cc..99b8966def 100644 --- a/activemodel/lib/active_model/validations/acceptance.rb +++ b/activemodel/lib/active_model/validations/acceptance.rb @@ -9,10 +9,10 @@ module ActiveModel def validate_each(record, attribute, value) unless value == options[:accept] - record.errors.add(attribute, :accepted, :default => options[:message]) + record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil)) end end - + def setup(klass) # Note: instance_methods.map(&:to_s) is important for 1.9 compatibility # as instance_methods returns symbols unlike 1.8 which returns strings. diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb index 5e13db7491..3a80893866 100644 --- a/activemodel/lib/active_model/validations/confirmation.rb +++ b/activemodel/lib/active_model/validations/confirmation.rb @@ -6,11 +6,11 @@ module ActiveModel def validate_each(record, attribute, value) confirmed = record.send(:"#{attribute}_confirmation") return if confirmed.nil? || value == confirmed - record.errors.add(attribute, :confirmation, :default => options[:message]) + record.errors.add(attribute, :confirmation, options) end - + def setup(klass) - klass.send(:attr_accessor, *attributes.map { |attribute| :"#{attribute}_confirmation" }) + klass.send(:attr_accessor, *attributes.map { |attribute| :"#{attribute}_confirmation" }) end end diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb index 599623368f..6fe43c7219 100644 --- a/activemodel/lib/active_model/validations/exclusion.rb +++ b/activemodel/lib/active_model/validations/exclusion.rb @@ -9,8 +9,9 @@ module ActiveModel end def validate_each(record, attribute, value) - return unless options[:in].include?(value) - record.errors.add(attribute, :exclusion, :default => options[:message], :value => value) + if options[:in].include?(value) + record.errors.add(attribute, :exclusion, options.except(:in).merge(:value => value)) + end end end diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb index 3b57d4fd77..6f6933205f 100644 --- a/activemodel/lib/active_model/validations/format.rb +++ b/activemodel/lib/active_model/validations/format.rb @@ -5,12 +5,12 @@ module ActiveModel class FormatValidator < EachValidator def validate_each(record, attribute, value) if options[:with] && value.to_s !~ options[:with] - record.errors.add(attribute, :invalid, :default => options[:message], :value => value) + record.errors.add(attribute, :invalid, options.except(:with).merge(:value => value)) elsif options[:without] && value.to_s =~ options[:without] - record.errors.add(attribute, :invalid, :default => options[:message], :value => value) + record.errors.add(attribute, :invalid, options.except(:with).merge(:value => value)) end end - + def check_validity! unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or" raise ArgumentError, "Either :with or :without must be supplied (but not both)" diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb index e9940dbbf0..863f4da0ff 100644 --- a/activemodel/lib/active_model/validations/inclusion.rb +++ b/activemodel/lib/active_model/validations/inclusion.rb @@ -9,8 +9,9 @@ module ActiveModel end def validate_each(record, attribute, value) - return if options[:in].include?(value) - record.errors.add(attribute, :inclusion, :default => options[:message], :value => value) + unless options[:in].include?(value) + record.errors.add(attribute, :inclusion, options.except(:in).merge(:value => value)) + end end end diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index dc191d3150..77db437a33 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -39,7 +39,8 @@ module ActiveModel CHECKS.each do |key, validity_check| next unless check_value = options[key] - custom_message = options[:message] || options[MESSAGES[key]] + default_message = options[MESSAGES[key]] + options[:message] ||= default_message if default_message valid_value = if key == :maximum value.nil? || value.size.send(validity_check, check_value) @@ -48,7 +49,10 @@ module ActiveModel end next if valid_value - record.errors.add(attribute, MESSAGES[key], :default => custom_message, :count => check_value) + + reserved_options = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long] + record.errors.add(attribute, MESSAGES[key], + options.except(*reserved_options).merge(:count => check_value)) end end end diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index c2e7223939..dcc3befb08 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -28,13 +28,13 @@ module ActiveModel return if options[:allow_nil] && raw_value.nil? unless value = parse_raw_value_as_a_number(raw_value) - record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message]) + record.errors.add(attr_name, :not_a_number, filtered_options(raw_value)) return end if options[:only_integer] unless value = parse_raw_value_as_an_integer(raw_value) - record.errors.add(attr_name, :not_an_integer, :value => raw_value, :default => options[:message]) + record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value)) return end end @@ -43,14 +43,14 @@ module ActiveModel case option when :odd, :even unless value.to_i.send(CHECKS[option]) - record.errors.add(attr_name, option, :value => value, :default => options[:message]) + record.errors.add(attr_name, option, filtered_options(value)) end else option_value = option_value.call(record) if option_value.is_a?(Proc) option_value = record.send(option_value) if option_value.is_a?(Symbol) unless value.send(CHECKS[option], option_value) - record.errors.add(attr_name, option, :default => options[:message], :value => value, :count => option_value) + record.errors.add(attr_name, option, filtered_options(value).merge(:count => option_value)) end end end @@ -75,6 +75,11 @@ module ActiveModel raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/ end + def filtered_options(value) + reserved_options = [:allow_nil, :odd, :even, :not_an_integer, :only_integer, :allow_nil, :less_than] + options.except(*reserved_options).merge(:value => value) + end + end module HelperMethods diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb index e36bee8115..28c4640b17 100644 --- a/activemodel/lib/active_model/validations/presence.rb +++ b/activemodel/lib/active_model/validations/presence.rb @@ -6,7 +6,7 @@ module ActiveModel module Validations class PresenceValidator < EachValidator def validate(record) - record.errors.add_on_blank(attributes, options[:message]) + record.errors.add_on_blank(attributes, options) end end diff --git a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb index 58a8d179ad..0679e67f84 100644 --- a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb @@ -8,131 +8,131 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase @person = Person.new end - # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) + # validates_inclusion_of: generate_message(attr_name, :inclusion, :message => custom_message, :value => value) def test_generate_message_inclusion_with_default_message - assert_equal 'is not included in the list', @person.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') + assert_equal 'is not included in the list', @person.errors.generate_message(:title, :inclusion, :value => 'title') end def test_generate_message_inclusion_with_custom_message - assert_equal 'custom message title', @person.errors.generate_message(:title, :inclusion, :default => 'custom message %{value}', :value => 'title') + assert_equal 'custom message title', @person.errors.generate_message(:title, :inclusion, :message => 'custom message %{value}', :value => 'title') end - # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) + # validates_exclusion_of: generate_message(attr_name, :exclusion, :message => custom_message, :value => value) def test_generate_message_exclusion_with_default_message - assert_equal 'is reserved', @person.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title') + assert_equal 'is reserved', @person.errors.generate_message(:title, :exclusion, :value => 'title') end def test_generate_message_exclusion_with_custom_message - assert_equal 'custom message title', @person.errors.generate_message(:title, :exclusion, :default => 'custom message %{value}', :value => 'title') + assert_equal 'custom message title', @person.errors.generate_message(:title, :exclusion, :message => 'custom message %{value}', :value => 'title') end - # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) + # validates_format_of: generate_message(attr_name, :invalid, :message => custom_message, :value => value) def test_generate_message_invalid_with_default_message - assert_equal 'is invalid', @person.errors.generate_message(:title, :invalid, :default => nil, :value => 'title') + assert_equal 'is invalid', @person.errors.generate_message(:title, :invalid, :value => 'title') end def test_generate_message_invalid_with_custom_message - assert_equal 'custom message title', @person.errors.generate_message(:title, :invalid, :default => 'custom message %{value}', :value => 'title') + assert_equal 'custom message title', @person.errors.generate_message(:title, :invalid, :message => 'custom message %{value}', :value => 'title') end - # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message]) + # validates_confirmation_of: generate_message(attr_name, :confirmation, :message => custom_message) def test_generate_message_confirmation_with_default_message - assert_equal "doesn't match confirmation", @person.errors.generate_message(:title, :confirmation, :default => nil) + assert_equal "doesn't match confirmation", @person.errors.generate_message(:title, :confirmation) end def test_generate_message_confirmation_with_custom_message - assert_equal 'custom message', @person.errors.generate_message(:title, :confirmation, :default => 'custom message') + assert_equal 'custom message', @person.errors.generate_message(:title, :confirmation, :message => 'custom message') end - # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message]) + # validates_acceptance_of: generate_message(attr_name, :accepted, :message => custom_message) def test_generate_message_accepted_with_default_message - assert_equal "must be accepted", @person.errors.generate_message(:title, :accepted, :default => nil) + assert_equal "must be accepted", @person.errors.generate_message(:title, :accepted) end def test_generate_message_accepted_with_custom_message - assert_equal 'custom message', @person.errors.generate_message(:title, :accepted, :default => 'custom message') + assert_equal 'custom message', @person.errors.generate_message(:title, :accepted, :message => 'custom message') end - # add_on_empty: generate_message(attr, :empty, :default => custom_message) + # add_on_empty: generate_message(attr, :empty, :message => custom_message) def test_generate_message_empty_with_default_message - assert_equal "can't be empty", @person.errors.generate_message(:title, :empty, :default => nil) + assert_equal "can't be empty", @person.errors.generate_message(:title, :empty) end def test_generate_message_empty_with_custom_message - assert_equal 'custom message', @person.errors.generate_message(:title, :empty, :default => 'custom message') + assert_equal 'custom message', @person.errors.generate_message(:title, :empty, :message => 'custom message') end - # add_on_blank: generate_message(attr, :blank, :default => custom_message) + # add_on_blank: generate_message(attr, :blank, :message => custom_message) def test_generate_message_blank_with_default_message - assert_equal "can't be blank", @person.errors.generate_message(:title, :blank, :default => nil) + assert_equal "can't be blank", @person.errors.generate_message(:title, :blank) end def test_generate_message_blank_with_custom_message - assert_equal 'custom message', @person.errors.generate_message(:title, :blank, :default => 'custom message') + assert_equal 'custom message', @person.errors.generate_message(:title, :blank, :message => 'custom message') end - # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end) + # validates_length_of: generate_message(attr, :too_long, :message => custom_message, :count => option_value.end) def test_generate_message_too_long_with_default_message - assert_equal "is too long (maximum is 10 characters)", @person.errors.generate_message(:title, :too_long, :default => nil, :count => 10) + assert_equal "is too long (maximum is 10 characters)", @person.errors.generate_message(:title, :too_long, :count => 10) end def test_generate_message_too_long_with_custom_message - assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_long, :default => 'custom message %{count}', :count => 10) + assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_long, :message => 'custom message %{count}', :count => 10) end - # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) + # validates_length_of: generate_message(attr, :too_short, :default => custom_message, :count => option_value.begin) def test_generate_message_too_short_with_default_message - assert_equal "is too short (minimum is 10 characters)", @person.errors.generate_message(:title, :too_short, :default => nil, :count => 10) + assert_equal "is too short (minimum is 10 characters)", @person.errors.generate_message(:title, :too_short, :count => 10) end def test_generate_message_too_short_with_custom_message - assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_short, :default => 'custom message %{count}', :count => 10) + assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_short, :message => 'custom message %{count}', :count => 10) end - # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value) + # validates_length_of: generate_message(attr, :wrong_length, :message => custom_message, :count => option_value) def test_generate_message_wrong_length_with_default_message - assert_equal "is the wrong length (should be 10 characters)", @person.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10) + assert_equal "is the wrong length (should be 10 characters)", @person.errors.generate_message(:title, :wrong_length, :count => 10) end def test_generate_message_wrong_length_with_custom_message - assert_equal 'custom message 10', @person.errors.generate_message(:title, :wrong_length, :default => 'custom message %{count}', :count => 10) + assert_equal 'custom message 10', @person.errors.generate_message(:title, :wrong_length, :message => 'custom message %{count}', :count => 10) end - # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) + # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :message => custom_message) def test_generate_message_not_a_number_with_default_message - assert_equal "is not a number", @person.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title') + assert_equal "is not a number", @person.errors.generate_message(:title, :not_a_number, :value => 'title') end def test_generate_message_not_a_number_with_custom_message - assert_equal 'custom message title', @person.errors.generate_message(:title, :not_a_number, :default => 'custom message %{value}', :value => 'title') + assert_equal 'custom message title', @person.errors.generate_message(:title, :not_a_number, :message => 'custom message %{value}', :value => 'title') end - # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) + # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => custom_message) def test_generate_message_greater_than_with_default_message - assert_equal "must be greater than 10", @person.errors.generate_message(:title, :greater_than, :default => nil, :value => 'title', :count => 10) + assert_equal "must be greater than 10", @person.errors.generate_message(:title, :greater_than, :value => 'title', :count => 10) end def test_generate_message_greater_than_or_equal_to_with_default_message - assert_equal "must be greater than or equal to 10", @person.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + assert_equal "must be greater than or equal to 10", @person.errors.generate_message(:title, :greater_than_or_equal_to, :value => 'title', :count => 10) end def test_generate_message_equal_to_with_default_message - assert_equal "must be equal to 10", @person.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10) + assert_equal "must be equal to 10", @person.errors.generate_message(:title, :equal_to, :value => 'title', :count => 10) end def test_generate_message_less_than_with_default_message - assert_equal "must be less than 10", @person.errors.generate_message(:title, :less_than, :default => nil, :value => 'title', :count => 10) + assert_equal "must be less than 10", @person.errors.generate_message(:title, :less_than, :value => 'title', :count => 10) end def test_generate_message_less_than_or_equal_to_with_default_message - assert_equal "must be less than or equal to 10", @person.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + assert_equal "must be less than or equal to 10", @person.errors.generate_message(:title, :less_than_or_equal_to, :value => 'title', :count => 10) end def test_generate_message_odd_with_default_message - assert_equal "must be odd", @person.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10) + assert_equal "must be odd", @person.errors.generate_message(:title, :odd, :value => 'title', :count => 10) end def test_generate_message_even_with_default_message - assert_equal "must be even", @person.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10) + assert_equal "must be even", @person.errors.generate_message(:title, :even, :value => 'title', :count => 10) end end diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index 547d80f46e..e9f0e430fe 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -22,23 +22,23 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_add_on_empty_generates_message - @person.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) + @person.errors.expects(:generate_message).with(:title, :empty, {}) @person.errors.add_on_empty :title end def test_errors_add_on_empty_generates_message_with_custom_default_message - @person.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'}) - @person.errors.add_on_empty :title, 'custom' + @person.errors.expects(:generate_message).with(:title, :empty, {:message => 'custom'}) + @person.errors.add_on_empty :title, :message => 'custom' end def test_errors_add_on_blank_generates_message - @person.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) + @person.errors.expects(:generate_message).with(:title, :blank, {}) @person.errors.add_on_blank :title end def test_errors_add_on_blank_generates_message_with_custom_default_message - @person.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) - @person.errors.add_on_blank :title, 'custom' + @person.errors.expects(:generate_message).with(:title, :blank, {:message => 'custom'}) + @person.errors.add_on_blank :title, :message => 'custom' end def test_full_message_encoding @@ -62,441 +62,272 @@ class I18nValidationTest < ActiveModel::TestCase end # ActiveModel::Validations + + # A set of common cases for ActiveModel::Validations message generation that + # are used to generate tests to keep things DRY + # + COMMON_CASES = [ + # [ case, validation_options, generate_message_options] + [ "given no options", {}, {}], + [ "given custom message", {:message => "custom"}, {:message => "custom"}], + [ "given if condition", {:if => lambda { true }}, {}], + [ "given unless condition", {:unless => lambda { false }}, {}], + [ "given option that is not reserved", {:format => "jpg"}, {:format => "jpg" }] + ] + # validates_confirmation_of w/ mocha - def test_validates_confirmation_of_generates_message - Person.validates_confirmation_of :title - @person.title_confirmation = 'foo' - @person.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil}) - @person.valid? - end - def test_validates_confirmation_of_generates_message_with_custom_default_message - Person.validates_confirmation_of :title, :message => 'custom' - @person.title_confirmation = 'foo' - @person.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_confirmation_of on generated message #{name}" do + Person.validates_confirmation_of :title, validation_options + @person.title_confirmation = 'foo' + @person.errors.expects(:generate_message).with(:title, :confirmation, generate_message_options) + @person.valid? + end end # validates_acceptance_of w/ mocha - def test_validates_acceptance_of_generates_message - Person.validates_acceptance_of :title, :allow_nil => false - @person.errors.expects(:generate_message).with(:title, :accepted, {:default => nil}) - @person.valid? - end - - def test_validates_acceptance_of_generates_message_with_custom_default_message - Person.validates_acceptance_of :title, :message => 'custom', :allow_nil => false - @person.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_acceptance_of on generated message #{name}" do + Person.validates_acceptance_of :title, validation_options.merge(:allow_nil => false) + @person.errors.expects(:generate_message).with(:title, :accepted, generate_message_options) + @person.valid? + end end # validates_presence_of w/ mocha - def test_validates_presence_of_generates_message - Person.validates_presence_of :title - @person.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) - @person.valid? - end - - def test_validates_presence_of_generates_message_with_custom_default_message - Person.validates_presence_of :title, :message => 'custom' - @person.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_presence_of on generated message #{name}" do + Person.validates_presence_of :title, validation_options + @person.errors.expects(:generate_message).with(:title, :blank, generate_message_options) + @person.valid? + end end - # validates_length_of :within w/ mocha + # validates_length_of :within too short w/ mocha - def test_validates_length_of_within_generates_message_with_title_too_short - Person.validates_length_of :title, :within => 3..5 - @person.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_length_of for :withing on generated message when too short #{name}" do + Person.validates_length_of :title, validation_options.merge(:within => 3..5) + @person.errors.expects(:generate_message).with(:title, :too_short, generate_message_options.merge(:count => 3)) + @person.valid? + end end - def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message - Person.validates_length_of :title, :within => 3..5, :too_short => 'custom' - @person.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) - @person.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_long - Person.validates_length_of :title, :within => 3..5 - @person.title = 'this title is too long' - @person.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) - @person.valid? - end + # validates_length_of :within too long w/ mocha - def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message - Person.validates_length_of :title, :within => 3..5, :too_long => 'custom' - @person.title = 'this title is too long' - @person.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_length_of for :too_long generated message #{name}" do + Person.validates_length_of :title, validation_options.merge(:within => 3..5) + @person.title = 'this title is too long' + @person.errors.expects(:generate_message).with(:title, :too_long, generate_message_options.merge(:count => 5)) + @person.valid? + end end # validates_length_of :is w/ mocha - def test_validates_length_of_is_generates_message - Person.validates_length_of :title, :is => 5 - @person.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => nil}) - @person.valid? - end - - def test_validates_length_of_is_generates_message_with_custom_default_message - Person.validates_length_of :title, :is => 5, :message => 'custom' - @person.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_length_of for :is on generated message #{name}" do + Person.validates_length_of :title, validation_options.merge(:is => 5) + @person.errors.expects(:generate_message).with(:title, :wrong_length, generate_message_options.merge(:count => 5)) + @person.valid? + end end # validates_format_of w/ mocha - def test_validates_format_of_generates_message - Person.validates_format_of :title, :with => /^[1-9][0-9]*$/ - @person.title = '72x' - @person.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => nil}) - @person.valid? - end - - def test_validates_format_of_generates_message_with_custom_default_message - Person.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom' - @person.title = '72x' - @person.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_format_of on generated message #{name}" do + Person.validates_format_of :title, validation_options.merge(:with => /^[1-9][0-9]*$/) + @person.title = '72x' + @person.errors.expects(:generate_message).with(:title, :invalid, generate_message_options.merge(:value => '72x')) + @person.valid? + end end # validates_inclusion_of w/ mocha - def test_validates_inclusion_of_generates_message - Person.validates_inclusion_of :title, :in => %w(a b c) - @person.title = 'z' - @person.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => nil}) - @person.valid? - end - - def test_validates_inclusion_of_generates_message_with_custom_default_message - Person.validates_inclusion_of :title, :in => %w(a b c), :message => 'custom' - @person.title = 'z' - @person.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_inclusion_of on generated message #{name}" do + Person.validates_inclusion_of :title, validation_options.merge(:in => %w(a b c)) + @person.title = 'z' + @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(:value => 'z')) + @person.valid? + end end # validates_exclusion_of w/ mocha - def test_validates_exclusion_of_generates_message - Person.validates_exclusion_of :title, :in => %w(a b c) - @person.title = 'a' - @person.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil}) - @person.valid? - end - - def test_validates_exclusion_of_generates_message_with_custom_default_message - Person.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom' - @person.title = 'a' - @person.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_exclusion_of generated message #{name}" do + Person.validates_exclusion_of :title, validation_options.merge(:in => %w(a b c)) + @person.title = 'a' + @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(:value => 'a')) + @person.valid? + end end # validates_numericality_of without :only_integer w/ mocha - def test_validates_numericality_of_generates_message - Person.validates_numericality_of :title - @person.title = 'a' - @person.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) - @person.valid? - end - - def test_validates_numericality_of_generates_message_with_custom_default_message - Person.validates_numericality_of :title, :message => 'custom' - @person.title = 'a' - @person.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_numericality_of generated message #{name}" do + Person.validates_numericality_of :title, validation_options + @person.title = 'a' + @person.errors.expects(:generate_message).with(:title, :not_a_number, generate_message_options.merge(:value => 'a')) + @person.valid? + end end # validates_numericality_of with :only_integer w/ mocha - def test_validates_numericality_of_only_integer_generates_message - Person.validates_numericality_of :title, :only_integer => true - @person.title = '0.0' - @person.errors.expects(:generate_message).with(:title, :not_an_integer, {:value => '0.0', :default => nil}) - @person.valid? - end - - def test_validates_numericality_of_only_integer_generates_message_with_custom_default_message - Person.validates_numericality_of :title, :only_integer => true, :message => 'custom' - @person.title = '0.0' - @person.errors.expects(:generate_message).with(:title, :not_an_integer, {:value => '0.0', :default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_numericality_of for :only_integer on generated message #{name}" do + Person.validates_numericality_of :title, validation_options.merge(:only_integer => true) + @person.title = '0.0' + @person.errors.expects(:generate_message).with(:title, :not_an_integer, generate_message_options.merge(:value => '0.0')) + @person.valid? + end end # validates_numericality_of :odd w/ mocha - def test_validates_numericality_of_odd_generates_message - Person.validates_numericality_of :title, :only_integer => true, :odd => true - @person.title = 0 - @person.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil}) - @person.valid? - end - - def test_validates_numericality_of_odd_generates_message_with_custom_default_message - Person.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom' - @person.title = 0 - @person.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_numericality_of for :odd on generated message #{name}" do + Person.validates_numericality_of :title, validation_options.merge(:only_integer => true, :odd => true) + @person.title = 0 + @person.errors.expects(:generate_message).with(:title, :odd, generate_message_options.merge(:value => 0)) + @person.valid? + end end # validates_numericality_of :less_than w/ mocha - def test_validates_numericality_of_less_than_generates_message - Person.validates_numericality_of :title, :only_integer => true, :less_than => 0 - @person.title = 1 - @person.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => nil}) - @person.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_numericality_of for :less_than on generated message #{name}" do + Person.validates_numericality_of :title, validation_options.merge(:only_integer => true, :less_than => 0) + @person.title = 1 + @person.errors.expects(:generate_message).with(:title, :less_than, generate_message_options.merge(:value => 1, :count => 0)) + @person.valid? + end end - def test_validates_numericality_of_less_than_odd_generates_message_with_custom_default_message - Person.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom' - @person.title = 1 - @person.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'}) - @person.valid? - end - # validates_confirmation_of w/o mocha + # To make things DRY this macro is defined to define 3 tests for every validation case. + def self.set_expectations_for_validation(validation, error_type, &block_that_sets_validation) + # test "validates_confirmation_of finds custom model key translation when blank" + test "#{validation} finds custom model key translation when #{error_type}" do + I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {error_type => 'custom message'}}}}}} + I18n.backend.store_translations 'en', :errors => {:messages => {error_type => 'global message'}} - def test_validates_confirmation_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:confirmation => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:confirmation => 'global message'}} + yield(@person, {}) + @person.valid? + assert_equal ['custom message'], @person.errors[:title] + end - Person.validates_confirmation_of :title - @person.title_confirmation = 'foo' - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end + # test "validates_confirmation_of finds custom model key translation with interpolation when blank" + test "#{validation} finds custom model key translation with interpolation when #{error_type}" do + I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {error_type => 'custom message with %{extra}'}}}}}} + I18n.backend.store_translations 'en', :errors => {:messages => {error_type => 'global message'}} - def test_validates_confirmation_of_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:confirmation => 'global message'}} + yield(@person, {:extra => "extra information"}) + @person.valid? + assert_equal ['custom message with extra information'], @person.errors[:title] + end - Person.validates_confirmation_of :title - @person.title_confirmation = 'foo' - @person.valid? - assert_equal ['global message'], @person.errors[:title] - end + # test "validates_confirmation_of finds global default key translation when blank" + test "#{validation} finds global default key translation when #{error_type}" do + I18n.backend.store_translations 'en', :errors => {:messages => {error_type => 'global message'}} - # validates_acceptance_of w/o mocha - - def test_validates_acceptance_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:accepted => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:accepted => 'global message'}} - - Person.validates_acceptance_of :title, :allow_nil => false - @person.valid? - assert_equal ['custom message'], @person.errors[:title] + yield(@person, {}) + @person.valid? + assert_equal ['global message'], @person.errors[:title] + end end - def test_validates_acceptance_of_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:accepted => 'global message'}} + # validates_confirmation_of w/o mocha - Person.validates_acceptance_of :title, :allow_nil => false - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_confirmation_of", :confirmation do |person, options_to_merge| + Person.validates_confirmation_of :title, options_to_merge + person.title_confirmation = 'foo' end - # validates_presence_of w/o mocha - - def test_validates_presence_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:blank => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:blank => 'global message'}} + # validates_acceptance_of w/o mocha - Person.validates_presence_of :title - @person.valid? - assert_equal ['custom message'], @person.errors[:title] + set_expectations_for_validation "validates_acceptance_of", :accepted do |person, options_to_merge| + Person.validates_acceptance_of :title, options_to_merge.merge(:allow_nil => false) end - def test_validates_presence_of_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:blank => 'global message'}} + # validates_presence_of w/o mocha - Person.validates_presence_of :title - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_presence_of", :blank do |person, options_to_merge| + Person.validates_presence_of :title, options_to_merge end # validates_length_of :within w/o mocha - def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:too_short => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:too_short => 'global message'}} - - Person.validates_length_of :title, :within => 3..5 - @person.valid? - assert_equal ['custom message'], @person.errors[:title] + set_expectations_for_validation "validates_length_of", :too_short do |person, options_to_merge| + Person.validates_length_of :title, options_to_merge.merge(:within => 3..5) end - def test_validates_length_of_within_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:too_short => 'global message'}} - - Person.validates_length_of :title, :within => 3..5 - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_length_of", :too_long do |person, options_to_merge| + Person.validates_length_of :title, options_to_merge.merge(:within => 3..5) + person.title = "too long" end # validates_length_of :is w/o mocha - def test_validates_length_of_is_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:wrong_length => 'global message'}} - - Person.validates_length_of :title, :is => 5 - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_length_of_is_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:wrong_length => 'global message'}} - - Person.validates_length_of :title, :is => 5 - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_length_of", :wrong_length do |person, options_to_merge| + Person.validates_length_of :title, options_to_merge.merge(:is => 5) end # validates_format_of w/o mocha - def test_validates_format_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:invalid => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:invalid => 'global message'}} - - Person.validates_format_of :title, :with => /^[1-9][0-9]*$/ - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_format_of_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:invalid => 'global message'}} - - Person.validates_format_of :title, :with => /^[1-9][0-9]*$/ - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_format_of", :invalid do |person, options_to_merge| + Person.validates_format_of :title, options_to_merge.merge(:with => /^[1-9][0-9]*$/) end # validates_inclusion_of w/o mocha - def test_validates_inclusion_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:inclusion => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:inclusion => 'global message'}} - - Person.validates_inclusion_of :title, :in => %w(a b c) - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_inclusion_of_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:inclusion => 'global message'}} - - Person.validates_inclusion_of :title, :in => %w(a b c) - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_inclusion_of", :inclusion do |person, options_to_merge| + Person.validates_inclusion_of :title, options_to_merge.merge(:in => %w(a b c)) end # validates_exclusion_of w/o mocha - def test_validates_exclusion_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:exclusion => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:exclusion => 'global message'}} - - Person.validates_exclusion_of :title, :in => %w(a b c) - @person.title = 'a' - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_exclusion_of_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:exclusion => 'global message'}} - - Person.validates_exclusion_of :title, :in => %w(a b c) - @person.title = 'a' - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_exclusion_of", :exclusion do |person, options_to_merge| + Person.validates_exclusion_of :title, options_to_merge.merge(:in => %w(a b c)) + person.title = 'a' end # validates_numericality_of without :only_integer w/o mocha - def test_validates_numericality_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:not_a_number => 'global message'}} - - Person.validates_numericality_of :title - @person.title = 'a' - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_numericality_of_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:not_a_number => 'global message'}} - - Person.validates_numericality_of :title, :only_integer => true - @person.title = 'a' - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_numericality_of", :not_a_number do |person, options_to_merge| + Person.validates_numericality_of :title, options_to_merge + person.title = 'a' end # validates_numericality_of with :only_integer w/o mocha - def test_validates_numericality_of_only_integer_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:not_an_integer => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:not_an_integer => 'global message'}} - - Person.validates_numericality_of :title, :only_integer => true - @person.title = '1.0' - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_numericality_of_only_integer_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:not_an_integer => 'global message'}} - - Person.validates_numericality_of :title, :only_integer => true - @person.title = '1.0' - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_numericality_of", :not_an_integer do |person, options_to_merge| + Person.validates_numericality_of :title, options_to_merge.merge(:only_integer => true) + person.title = '1.0' end # validates_numericality_of :odd w/o mocha - def test_validates_numericality_of_odd_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:odd => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:odd => 'global message'}} - - Person.validates_numericality_of :title, :only_integer => true, :odd => true - @person.title = 0 - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_numericality_of_odd_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:odd => 'global message'}} - - Person.validates_numericality_of :title, :only_integer => true, :odd => true - @person.title = 0 - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_numericality_of", :odd do |person, options_to_merge| + Person.validates_numericality_of :title, options_to_merge.merge(:only_integer => true, :odd => true) + person.title = 0 end # validates_numericality_of :less_than w/o mocha - def test_validates_numericality_of_less_than_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:less_than => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :errors => {:messages => {:less_than => 'global message'}} - - Person.validates_numericality_of :title, :only_integer => true, :less_than => 0 - @person.title = 1 - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_numericality_of_less_than_finds_global_default_translation - I18n.backend.store_translations 'en', :errors => {:messages => {:less_than => 'global message'}} - - Person.validates_numericality_of :title, :only_integer => true, :less_than => 0 - @person.title = 1 - @person.valid? - assert_equal ['global message'], @person.errors[:title] + set_expectations_for_validation "validates_numericality_of", :less_than do |person, options_to_merge| + Person.validates_numericality_of :title, options_to_merge.merge(:only_integer => true, :less_than => 0) + person.title = 1 end # test with validates_with diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb index e41635134c..0b0f5682aa 100644 --- a/activerecord/lib/active_record/validations/associated.rb +++ b/activerecord/lib/active_record/validations/associated.rb @@ -3,7 +3,7 @@ module ActiveRecord class AssociatedValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all? - record.errors.add(attribute, :invalid, :default => options[:message], :value => value) + record.errors.add(attribute, :invalid, options.merge(:value => value)) end end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 6283bdd0d6..1c9ecc7b1b 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -32,7 +32,7 @@ module ActiveRecord end if relation.exists? - record.errors.add(attribute, :taken, :default => options[:message], :value => value) + record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value)) end end diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb index 8ee2a5868c..454e42ed37 100644 --- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb @@ -9,22 +9,22 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase I18n.backend = I18n::Backend::Simple.new end - # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) + # validates_associated: generate_message(attr_name, :invalid, :message => custom_message, :value => value) def test_generate_message_invalid_with_default_message - assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title') + assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :value => 'title') end def test_generate_message_invalid_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message %{value}', :value => 'title') + assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :message => 'custom message %{value}', :value => 'title') end - # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message]) + # validates_uniqueness_of: generate_message(attr_name, :taken, :message => custom_message) def test_generate_message_taken_with_default_message - assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title') + assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :value => 'title') end def test_generate_message_taken_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message %{value}', :value => 'title') + assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :message => 'custom message %{value}', :value => 'title') end # ActiveRecord#RecordInvalid exception diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb index 38fa2b821d..15b97c02c8 100644 --- a/activerecord/test/cases/validations/i18n_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_validation_test.rb @@ -31,34 +31,40 @@ class I18nValidationTest < ActiveRecord::TestCase end end - # validates_uniqueness_of w/ mocha + # A set of common cases for ActiveModel::Validations message generation that + # are used to generate tests to keep things DRY + # + COMMON_CASES = [ + # [ case, validation_options, generate_message_options] + [ "given no options", {}, {}], + [ "given custom message", {:message => "custom"}, {:message => "custom"}], + [ "given if condition", {:if => lambda { true }}, {}], + [ "given unless condition", {:unless => lambda { false }}, {}], + [ "given option that is not reserved", {:format => "jpg"}, {:format => "jpg" }] + # TODO Add :on case, but below doesn't work, because then the validation isn't run for some reason + # even when using .save instead .valid? + # [ "given on condition", {:on => :save}, {}] + ] - def test_validates_uniqueness_of_generates_message - Topic.validates_uniqueness_of :title - @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'}) - @topic.valid? - end + # validates_uniqueness_of w/ mocha - def test_validates_uniqueness_of_generates_message_with_custom_default_message - Topic.validates_uniqueness_of :title, :message => 'custom' - @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'}) - @topic.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_uniqueness_of on generated message #{name}" do + Topic.validates_uniqueness_of :title, validation_options + @topic.title = unique_topic.title + @topic.errors.expects(:generate_message).with(:title, :taken, generate_message_options.merge(:value => 'unique!')) + @topic.valid? + end end # validates_associated w/ mocha - def test_validates_associated_generates_message - Topic.validates_associated :replies - replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) - replied_topic.valid? - end - - def test_validates_associated_generates_message_with_custom_default_message - Topic.validates_associated :replies - replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) - replied_topic.valid? + COMMON_CASES.each do |name, validation_options, generate_message_options| + test "validates_associated on generated message #{name}" do + Topic.validates_associated :replies, validation_options + replied_topic.errors.expects(:generate_message).with(:replies, :invalid, generate_message_options.merge(:value => replied_topic.replies)) + replied_topic.save + end end # validates_associated w/o mocha -- cgit v1.2.3 From 0421fb7a913c1c8a7e07a395106bbc65e75e9d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 21 Jun 2010 12:17:24 +0200 Subject: Refactor previous commit a bit [#4057 state:resolved] --- activemodel/lib/active_model/errors.rb | 11 +++++++---- activemodel/lib/active_model/validations/exclusion.rb | 2 +- activemodel/lib/active_model/validations/format.rb | 4 ++-- activemodel/lib/active_model/validations/inclusion.rb | 2 +- activemodel/lib/active_model/validations/length.rb | 4 ++-- activemodel/lib/active_model/validations/numericality.rb | 6 +++--- activemodel/lib/active_model/validations/with.rb | 3 --- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index b4660f3587..d943e044a7 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -61,6 +61,8 @@ module ActiveModel class Errors < ActiveSupport::OrderedHash include DeprecatedErrorMethods + CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank] + # Pass in the instance of the object that is using the errors object. # # class Person @@ -183,11 +185,12 @@ module ActiveModel def add(attribute, message = nil, options = {}) message ||= :invalid - validation_conditionals = [:if, :unless, :on] - - message = generate_message(attribute, message, options.except(*validation_conditionals)) if message.is_a?(Symbol) + if message.is_a?(Symbol) + message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS)) + elsif message.is_a?(Proc) + message = message.call + end - message = message.call if message.is_a?(Proc) self[attribute] << message end diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb index 6fe43c7219..4138892786 100644 --- a/activemodel/lib/active_model/validations/exclusion.rb +++ b/activemodel/lib/active_model/validations/exclusion.rb @@ -10,7 +10,7 @@ module ActiveModel def validate_each(record, attribute, value) if options[:in].include?(value) - record.errors.add(attribute, :exclusion, options.except(:in).merge(:value => value)) + record.errors.add(attribute, :exclusion, options.except(:in).merge!(:value => value)) end end end diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb index 6f6933205f..104f403492 100644 --- a/activemodel/lib/active_model/validations/format.rb +++ b/activemodel/lib/active_model/validations/format.rb @@ -5,9 +5,9 @@ module ActiveModel class FormatValidator < EachValidator def validate_each(record, attribute, value) if options[:with] && value.to_s !~ options[:with] - record.errors.add(attribute, :invalid, options.except(:with).merge(:value => value)) + record.errors.add(attribute, :invalid, options.except(:with).merge!(:value => value)) elsif options[:without] && value.to_s =~ options[:without] - record.errors.add(attribute, :invalid, options.except(:with).merge(:value => value)) + record.errors.add(attribute, :invalid, options.except(:without).merge!(:value => value)) end end diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb index 863f4da0ff..049b093618 100644 --- a/activemodel/lib/active_model/validations/inclusion.rb +++ b/activemodel/lib/active_model/validations/inclusion.rb @@ -10,7 +10,7 @@ module ActiveModel def validate_each(record, attribute, value) unless options[:in].include?(value) - record.errors.add(attribute, :inclusion, options.except(:in).merge(:value => value)) + record.errors.add(attribute, :inclusion, options.except(:in).merge!(:value => value)) end end end diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index 77db437a33..c8a77ad666 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -7,6 +7,7 @@ module ActiveModel CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze DEFAULT_TOKENIZER = lambda { |value| value.split(//) } + RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long] def initialize(options) if range = (options.delete(:in) || options.delete(:within)) @@ -50,9 +51,8 @@ module ActiveModel next if valid_value - reserved_options = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long] record.errors.add(attribute, MESSAGES[key], - options.except(*reserved_options).merge(:count => check_value)) + options.except(*RESERVED_OPTIONS).merge!(:count => check_value)) end end end diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index dcc3befb08..b6aff7aa6b 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -7,6 +7,8 @@ module ActiveModel :equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=, :odd => :odd?, :even => :even? }.freeze + RESERVED_OPTIONS = CHECKS.keys + [:only_integer] + def initialize(options) super(options.reverse_merge(:only_integer => false, :allow_nil => false)) end @@ -76,10 +78,8 @@ module ActiveModel end def filtered_options(value) - reserved_options = [:allow_nil, :odd, :even, :not_an_integer, :only_integer, :allow_nil, :less_than] - options.except(*reserved_options).merge(:value => value) + options.except(*RESERVED_OPTIONS).merge!(:value => value) end - end module HelperMethods diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index a2e870d714..200efd4eb5 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -1,6 +1,4 @@ module ActiveModel - - # == Active Model validates_with method module Validations module HelperMethods private @@ -11,7 +9,6 @@ module ActiveModel end module ClassMethods - # Passes the record off to the class or classes specified and allows them # to add errors based on more complex conditions. # -- cgit v1.2.3 From dad71c6099e0e5cfb50f574853255e1abc255c34 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Mon, 21 Jun 2010 22:11:12 +0200 Subject: removed 'unless const_defined?' code smell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../action_dispatch/testing/assertions/selector.rb | 42 ++- .../lib/action_view/helpers/asset_tag_helper.rb | 2 +- actionpack/lib/action_view/helpers/date_helper.rb | 4 +- actionpack/lib/action_view/helpers/form_helper.rb | 18 +- .../lib/action_view/helpers/prototype_helper.rb | 14 +- .../action_view/helpers/scriptaculous_helper.rb | 6 +- .../core_ext/class/inheritable_attributes.rb | 2 +- .../lib/active_support/values/time_zone.rb | 294 ++++++++++----------- 8 files changed, 186 insertions(+), 196 deletions(-) diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb index b490547da7..2fc9e2b7d6 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb @@ -7,9 +7,7 @@ require 'action_controller/vendor/html-scanner' module ActionDispatch module Assertions - unless const_defined?(:NO_STRIP) - NO_STRIP = %w{pre script style textarea} - end + NO_STRIP = %w{pre script style textarea} # Adds the +assert_select+ method for use in Rails functional # test cases, which can be used to make assertions on the response HTML of a controller @@ -581,27 +579,25 @@ module ActionDispatch end protected - unless const_defined?(:RJS_STATEMENTS) - RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\"" - RJS_ANY_ID = "\"([^\"])*\"" - RJS_STATEMENTS = { - :chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)", - :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)", - :replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", - :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", - :redirect => "window.location.href = #{RJS_ANY_ID}" - } - [:remove, :show, :hide, :toggle].each do |action| - RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)" - end - RJS_INSERTIONS = ["top", "bottom", "before", "after"] - RJS_INSERTIONS.each do |insertion| - RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)" - end - RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)" - RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})") - RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/ + RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\"" + RJS_ANY_ID = "\"([^\"])*\"" + RJS_STATEMENTS = { + :chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)", + :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)", + :replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", + :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", + :redirect => "window.location.href = #{RJS_ANY_ID}" + } + [:remove, :show, :hide, :toggle].each do |action| + RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)" + end + RJS_INSERTIONS = ["top", "bottom", "before", "after"] + RJS_INSERTIONS.each do |insertion| + RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)" end + RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)" + RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})") + RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/ # +assert_select+ and +css_select+ call this to obtain the content in the HTML # page, or from all the RJS statements, depending on the type of response. diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index d094b0d8d8..63f9fcfcb2 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -194,7 +194,7 @@ module ActionView # RewriteEngine On # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L] module AssetTagHelper - JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) + JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be :rss (default) or diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 7d7b6a1d91..f097b9a5a3 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -582,10 +582,10 @@ module ActionView extend ActiveSupport::Memoizable include ActionView::Helpers::TagHelper - DEFAULT_PREFIX = 'date'.freeze unless const_defined?('DEFAULT_PREFIX') + DEFAULT_PREFIX = 'date'.freeze POSITION = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 - }.freeze unless const_defined?('POSITION') + }.freeze def initialize(datetime, options = {}, html_options = {}) @options = options.dup diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 8efed98bd2..b0b654d5ce 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -221,15 +221,15 @@ module ActionView # <% end %> # # === Unobtrusive JavaScript - # - # Specifying: - # + # + # Specifying: + # # :remote => true # # in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its - # behaviour. The expected default behaviour is an XMLHttpRequest in the background instead of the regular + # behaviour. The expected default behaviour is an XMLHttpRequest in the background instead of the regular # POST arrangement, but ultimately the behaviour is the choice of the JavaScript driver implementor. - # Even though it's using JavaScript to serialize the form elements, the form submission will work just like + # Even though it's using JavaScript to serialize the form elements, the form submission will work just like # a regular submission as viewed by the receiving side (all elements available in params). # # Example: @@ -839,9 +839,9 @@ module ActionView attr_reader :method_name, :object_name - DEFAULT_FIELD_OPTIONS = { "size" => 30 }.freeze unless const_defined?(:DEFAULT_FIELD_OPTIONS) - DEFAULT_RADIO_OPTIONS = { }.freeze unless const_defined?(:DEFAULT_RADIO_OPTIONS) - DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }.freeze unless const_defined?(:DEFAULT_TEXT_AREA_OPTIONS) + DEFAULT_FIELD_OPTIONS = { "size" => 30 }.freeze + DEFAULT_RADIO_OPTIONS = { }.freeze + DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }.freeze def initialize(object_name, method_name, template_object, object = nil) @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup @@ -1181,7 +1181,7 @@ module ActionView # <%= form_for @post do |f| %> # <%= f.submit %> # <% end %> - # + # # In the example above, if @post is a new record, it will use "Create Post" as # submit button label, otherwise, it uses "Update Post". # diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 3038b07143..bfb8f74a00 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -95,14 +95,12 @@ module ActionView # See JavaScriptGenerator for information on updating multiple elements # on the page in an Ajax response. module PrototypeHelper - unless const_defined? :CALLBACKS - CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded, - :interactive, :complete, :failure, :success ] + - (100..599).to_a) - AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url, - :asynchronous, :method, :insertion, :position, - :form, :with, :update, :script, :type ]).merge(CALLBACKS) - end + CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded, + :interactive, :complete, :failure, :success ] + + (100..599).to_a) + AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url, + :asynchronous, :method, :insertion, :position, + :form, :with, :update, :script, :type ]).merge(CALLBACKS) # Returns the JavaScript needed for a remote function. # Takes the same arguments as link_to_remote. diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index 7f7776e9c0..8610c2469e 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -4,7 +4,7 @@ require 'active_support/json' module ActionView # = Action View Scriptaculous Helpers module Helpers - # Provides a set of helpers for calling Scriptaculous[http://script.aculo.us/] + # Provides a set of helpers for calling Scriptaculous[http://script.aculo.us/] # JavaScript functions, including those which create Ajax controls and visual # effects. # @@ -18,9 +18,7 @@ module ActionView # See the documentation at http://script.aculo.us for more information on # using these helpers in your application. module ScriptaculousHelper - unless const_defined? :TOGGLE_EFFECTS - TOGGLE_EFFECTS = [:toggle_appear, :toggle_slide, :toggle_blind] - end + TOGGLE_EFFECTS = [:toggle_appear, :toggle_slide, :toggle_blind] # Returns a JavaScript snippet to be used on the Ajax callbacks for # starting visual effects. diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index 4be7eaf476..7aff05dcdf 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -128,7 +128,7 @@ class Class # :nodoc: private # Prevent this constant from being created multiple times - EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES) + EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze def inherited_with_inheritable_attributes(child) inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 7550d8909f..0a08016522 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -30,154 +30,152 @@ end # (if a recent version of the gem is installed locally, this will be used instead of the bundled version.) module ActiveSupport class TimeZone - unless const_defined?(:MAPPING) - # Keys are Rails TimeZone names, values are TZInfo identifiers - MAPPING = { - "International Date Line West" => "Pacific/Midway", - "Midway Island" => "Pacific/Midway", - "Samoa" => "Pacific/Pago_Pago", - "Hawaii" => "Pacific/Honolulu", - "Alaska" => "America/Juneau", - "Pacific Time (US & Canada)" => "America/Los_Angeles", - "Tijuana" => "America/Tijuana", - "Mountain Time (US & Canada)" => "America/Denver", - "Arizona" => "America/Phoenix", - "Chihuahua" => "America/Chihuahua", - "Mazatlan" => "America/Mazatlan", - "Central Time (US & Canada)" => "America/Chicago", - "Saskatchewan" => "America/Regina", - "Guadalajara" => "America/Mexico_City", - "Mexico City" => "America/Mexico_City", - "Monterrey" => "America/Monterrey", - "Central America" => "America/Guatemala", - "Eastern Time (US & Canada)" => "America/New_York", - "Indiana (East)" => "America/Indiana/Indianapolis", - "Bogota" => "America/Bogota", - "Lima" => "America/Lima", - "Quito" => "America/Lima", - "Atlantic Time (Canada)" => "America/Halifax", - "Caracas" => "America/Caracas", - "La Paz" => "America/La_Paz", - "Santiago" => "America/Santiago", - "Newfoundland" => "America/St_Johns", - "Brasilia" => "America/Sao_Paulo", - "Buenos Aires" => "America/Argentina/Buenos_Aires", - "Georgetown" => "America/Guyana", - "Greenland" => "America/Godthab", - "Mid-Atlantic" => "Atlantic/South_Georgia", - "Azores" => "Atlantic/Azores", - "Cape Verde Is." => "Atlantic/Cape_Verde", - "Dublin" => "Europe/Dublin", - "Edinburgh" => "Europe/London", - "Lisbon" => "Europe/Lisbon", - "London" => "Europe/London", - "Casablanca" => "Africa/Casablanca", - "Monrovia" => "Africa/Monrovia", - "UTC" => "Etc/UTC", - "Belgrade" => "Europe/Belgrade", - "Bratislava" => "Europe/Bratislava", - "Budapest" => "Europe/Budapest", - "Ljubljana" => "Europe/Ljubljana", - "Prague" => "Europe/Prague", - "Sarajevo" => "Europe/Sarajevo", - "Skopje" => "Europe/Skopje", - "Warsaw" => "Europe/Warsaw", - "Zagreb" => "Europe/Zagreb", - "Brussels" => "Europe/Brussels", - "Copenhagen" => "Europe/Copenhagen", - "Madrid" => "Europe/Madrid", - "Paris" => "Europe/Paris", - "Amsterdam" => "Europe/Amsterdam", - "Berlin" => "Europe/Berlin", - "Bern" => "Europe/Berlin", - "Rome" => "Europe/Rome", - "Stockholm" => "Europe/Stockholm", - "Vienna" => "Europe/Vienna", - "West Central Africa" => "Africa/Algiers", - "Bucharest" => "Europe/Bucharest", - "Cairo" => "Africa/Cairo", - "Helsinki" => "Europe/Helsinki", - "Kyiv" => "Europe/Kiev", - "Riga" => "Europe/Riga", - "Sofia" => "Europe/Sofia", - "Tallinn" => "Europe/Tallinn", - "Vilnius" => "Europe/Vilnius", - "Athens" => "Europe/Athens", - "Istanbul" => "Europe/Istanbul", - "Minsk" => "Europe/Minsk", - "Jerusalem" => "Asia/Jerusalem", - "Harare" => "Africa/Harare", - "Pretoria" => "Africa/Johannesburg", - "Moscow" => "Europe/Moscow", - "St. Petersburg" => "Europe/Moscow", - "Volgograd" => "Europe/Moscow", - "Kuwait" => "Asia/Kuwait", - "Riyadh" => "Asia/Riyadh", - "Nairobi" => "Africa/Nairobi", - "Baghdad" => "Asia/Baghdad", - "Tehran" => "Asia/Tehran", - "Abu Dhabi" => "Asia/Muscat", - "Muscat" => "Asia/Muscat", - "Baku" => "Asia/Baku", - "Tbilisi" => "Asia/Tbilisi", - "Yerevan" => "Asia/Yerevan", - "Kabul" => "Asia/Kabul", - "Ekaterinburg" => "Asia/Yekaterinburg", - "Islamabad" => "Asia/Karachi", - "Karachi" => "Asia/Karachi", - "Tashkent" => "Asia/Tashkent", - "Chennai" => "Asia/Kolkata", - "Kolkata" => "Asia/Kolkata", - "Mumbai" => "Asia/Kolkata", - "New Delhi" => "Asia/Kolkata", - "Kathmandu" => "Asia/Kathmandu", - "Astana" => "Asia/Dhaka", - "Dhaka" => "Asia/Dhaka", - "Sri Jayawardenepura" => "Asia/Colombo", - "Almaty" => "Asia/Almaty", - "Novosibirsk" => "Asia/Novosibirsk", - "Rangoon" => "Asia/Rangoon", - "Bangkok" => "Asia/Bangkok", - "Hanoi" => "Asia/Bangkok", - "Jakarta" => "Asia/Jakarta", - "Krasnoyarsk" => "Asia/Krasnoyarsk", - "Beijing" => "Asia/Shanghai", - "Chongqing" => "Asia/Chongqing", - "Hong Kong" => "Asia/Hong_Kong", - "Urumqi" => "Asia/Urumqi", - "Kuala Lumpur" => "Asia/Kuala_Lumpur", - "Singapore" => "Asia/Singapore", - "Taipei" => "Asia/Taipei", - "Perth" => "Australia/Perth", - "Irkutsk" => "Asia/Irkutsk", - "Ulaan Bataar" => "Asia/Ulaanbaatar", - "Seoul" => "Asia/Seoul", - "Osaka" => "Asia/Tokyo", - "Sapporo" => "Asia/Tokyo", - "Tokyo" => "Asia/Tokyo", - "Yakutsk" => "Asia/Yakutsk", - "Darwin" => "Australia/Darwin", - "Adelaide" => "Australia/Adelaide", - "Canberra" => "Australia/Melbourne", - "Melbourne" => "Australia/Melbourne", - "Sydney" => "Australia/Sydney", - "Brisbane" => "Australia/Brisbane", - "Hobart" => "Australia/Hobart", - "Vladivostok" => "Asia/Vladivostok", - "Guam" => "Pacific/Guam", - "Port Moresby" => "Pacific/Port_Moresby", - "Magadan" => "Asia/Magadan", - "Solomon Is." => "Asia/Magadan", - "New Caledonia" => "Pacific/Noumea", - "Fiji" => "Pacific/Fiji", - "Kamchatka" => "Asia/Kamchatka", - "Marshall Is." => "Pacific/Majuro", - "Auckland" => "Pacific/Auckland", - "Wellington" => "Pacific/Auckland", - "Nuku'alofa" => "Pacific/Tongatapu" - }.each { |name, zone| name.freeze; zone.freeze } - MAPPING.freeze - end + # Keys are Rails TimeZone names, values are TZInfo identifiers + MAPPING = { + "International Date Line West" => "Pacific/Midway", + "Midway Island" => "Pacific/Midway", + "Samoa" => "Pacific/Pago_Pago", + "Hawaii" => "Pacific/Honolulu", + "Alaska" => "America/Juneau", + "Pacific Time (US & Canada)" => "America/Los_Angeles", + "Tijuana" => "America/Tijuana", + "Mountain Time (US & Canada)" => "America/Denver", + "Arizona" => "America/Phoenix", + "Chihuahua" => "America/Chihuahua", + "Mazatlan" => "America/Mazatlan", + "Central Time (US & Canada)" => "America/Chicago", + "Saskatchewan" => "America/Regina", + "Guadalajara" => "America/Mexico_City", + "Mexico City" => "America/Mexico_City", + "Monterrey" => "America/Monterrey", + "Central America" => "America/Guatemala", + "Eastern Time (US & Canada)" => "America/New_York", + "Indiana (East)" => "America/Indiana/Indianapolis", + "Bogota" => "America/Bogota", + "Lima" => "America/Lima", + "Quito" => "America/Lima", + "Atlantic Time (Canada)" => "America/Halifax", + "Caracas" => "America/Caracas", + "La Paz" => "America/La_Paz", + "Santiago" => "America/Santiago", + "Newfoundland" => "America/St_Johns", + "Brasilia" => "America/Sao_Paulo", + "Buenos Aires" => "America/Argentina/Buenos_Aires", + "Georgetown" => "America/Guyana", + "Greenland" => "America/Godthab", + "Mid-Atlantic" => "Atlantic/South_Georgia", + "Azores" => "Atlantic/Azores", + "Cape Verde Is." => "Atlantic/Cape_Verde", + "Dublin" => "Europe/Dublin", + "Edinburgh" => "Europe/London", + "Lisbon" => "Europe/Lisbon", + "London" => "Europe/London", + "Casablanca" => "Africa/Casablanca", + "Monrovia" => "Africa/Monrovia", + "UTC" => "Etc/UTC", + "Belgrade" => "Europe/Belgrade", + "Bratislava" => "Europe/Bratislava", + "Budapest" => "Europe/Budapest", + "Ljubljana" => "Europe/Ljubljana", + "Prague" => "Europe/Prague", + "Sarajevo" => "Europe/Sarajevo", + "Skopje" => "Europe/Skopje", + "Warsaw" => "Europe/Warsaw", + "Zagreb" => "Europe/Zagreb", + "Brussels" => "Europe/Brussels", + "Copenhagen" => "Europe/Copenhagen", + "Madrid" => "Europe/Madrid", + "Paris" => "Europe/Paris", + "Amsterdam" => "Europe/Amsterdam", + "Berlin" => "Europe/Berlin", + "Bern" => "Europe/Berlin", + "Rome" => "Europe/Rome", + "Stockholm" => "Europe/Stockholm", + "Vienna" => "Europe/Vienna", + "West Central Africa" => "Africa/Algiers", + "Bucharest" => "Europe/Bucharest", + "Cairo" => "Africa/Cairo", + "Helsinki" => "Europe/Helsinki", + "Kyiv" => "Europe/Kiev", + "Riga" => "Europe/Riga", + "Sofia" => "Europe/Sofia", + "Tallinn" => "Europe/Tallinn", + "Vilnius" => "Europe/Vilnius", + "Athens" => "Europe/Athens", + "Istanbul" => "Europe/Istanbul", + "Minsk" => "Europe/Minsk", + "Jerusalem" => "Asia/Jerusalem", + "Harare" => "Africa/Harare", + "Pretoria" => "Africa/Johannesburg", + "Moscow" => "Europe/Moscow", + "St. Petersburg" => "Europe/Moscow", + "Volgograd" => "Europe/Moscow", + "Kuwait" => "Asia/Kuwait", + "Riyadh" => "Asia/Riyadh", + "Nairobi" => "Africa/Nairobi", + "Baghdad" => "Asia/Baghdad", + "Tehran" => "Asia/Tehran", + "Abu Dhabi" => "Asia/Muscat", + "Muscat" => "Asia/Muscat", + "Baku" => "Asia/Baku", + "Tbilisi" => "Asia/Tbilisi", + "Yerevan" => "Asia/Yerevan", + "Kabul" => "Asia/Kabul", + "Ekaterinburg" => "Asia/Yekaterinburg", + "Islamabad" => "Asia/Karachi", + "Karachi" => "Asia/Karachi", + "Tashkent" => "Asia/Tashkent", + "Chennai" => "Asia/Kolkata", + "Kolkata" => "Asia/Kolkata", + "Mumbai" => "Asia/Kolkata", + "New Delhi" => "Asia/Kolkata", + "Kathmandu" => "Asia/Kathmandu", + "Astana" => "Asia/Dhaka", + "Dhaka" => "Asia/Dhaka", + "Sri Jayawardenepura" => "Asia/Colombo", + "Almaty" => "Asia/Almaty", + "Novosibirsk" => "Asia/Novosibirsk", + "Rangoon" => "Asia/Rangoon", + "Bangkok" => "Asia/Bangkok", + "Hanoi" => "Asia/Bangkok", + "Jakarta" => "Asia/Jakarta", + "Krasnoyarsk" => "Asia/Krasnoyarsk", + "Beijing" => "Asia/Shanghai", + "Chongqing" => "Asia/Chongqing", + "Hong Kong" => "Asia/Hong_Kong", + "Urumqi" => "Asia/Urumqi", + "Kuala Lumpur" => "Asia/Kuala_Lumpur", + "Singapore" => "Asia/Singapore", + "Taipei" => "Asia/Taipei", + "Perth" => "Australia/Perth", + "Irkutsk" => "Asia/Irkutsk", + "Ulaan Bataar" => "Asia/Ulaanbaatar", + "Seoul" => "Asia/Seoul", + "Osaka" => "Asia/Tokyo", + "Sapporo" => "Asia/Tokyo", + "Tokyo" => "Asia/Tokyo", + "Yakutsk" => "Asia/Yakutsk", + "Darwin" => "Australia/Darwin", + "Adelaide" => "Australia/Adelaide", + "Canberra" => "Australia/Melbourne", + "Melbourne" => "Australia/Melbourne", + "Sydney" => "Australia/Sydney", + "Brisbane" => "Australia/Brisbane", + "Hobart" => "Australia/Hobart", + "Vladivostok" => "Asia/Vladivostok", + "Guam" => "Pacific/Guam", + "Port Moresby" => "Pacific/Port_Moresby", + "Magadan" => "Asia/Magadan", + "Solomon Is." => "Asia/Magadan", + "New Caledonia" => "Pacific/Noumea", + "Fiji" => "Pacific/Fiji", + "Kamchatka" => "Asia/Kamchatka", + "Marshall Is." => "Pacific/Majuro", + "Auckland" => "Pacific/Auckland", + "Wellington" => "Pacific/Auckland", + "Nuku'alofa" => "Pacific/Tongatapu" + }.each { |name, zone| name.freeze; zone.freeze } + MAPPING.freeze UTC_OFFSET_WITH_COLON = '%s%02d:%02d' UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '') -- cgit v1.2.3 From fd03f1738e089900a4501bdb86fe4aca5354e85e Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Tue, 22 Jun 2010 00:35:38 +0200 Subject: removed default Formatter in logger, not needed with 1.8.7 upwards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_support/core_ext/logger.rb | 38 ---------------------- 1 file changed, 38 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index c4994ca2ee..d023b4bcff 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -72,44 +72,6 @@ class Logger @formatter ||= SimpleFormatter.new end - unless const_defined? :Formatter - class Formatter - Format = "%s, [%s#%d] %5s -- %s: %s\n" - - attr_accessor :datetime_format - - def initialize - @datetime_format = nil - end - - def call(severity, time, progname, msg) - Format % [severity[0..0], format_datetime(time), $$, severity, progname, - msg2str(msg)] - end - - private - def format_datetime(time) - if @datetime_format.nil? - time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec - else - time.strftime(@datetime_format) - end - end - - def msg2str(msg) - case msg - when ::String - msg - when ::Exception - "#{ msg.message } (#{ msg.class })\n" << - (msg.backtrace || []).join("\n") - else - msg.inspect - end - end - end - end - # Simple formatter which only displays the message. class SimpleFormatter < Logger::Formatter # This method is invoked when a log event occurs -- cgit v1.2.3 From 1b369be02ffe52b9b278c41c762eedc35d52b348 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 21 Jun 2010 18:28:00 -0700 Subject: Bump rack-mount to 0.6.5 for tokenizer speedup --- actionpack/actionpack.gemspec | 2 +- railties/guides/source/initialization.textile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 8e95315252..1dede257f9 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |s| s.add_dependency('i18n', '~> 0.4.1') s.add_dependency('rack', '~> 1.1.0') s.add_dependency('rack-test', '~> 0.5.4') - s.add_dependency('rack-mount', '~> 0.6.4') + s.add_dependency('rack-mount', '~> 0.6.5') s.add_dependency('tzinfo', '~> 0.3.16') s.add_dependency('erubis', '~> 2.6.5') end diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index bc3a2d0f0b..e458413b35 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -148,7 +148,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This * mime-types-1.16.gem * polyglot-0.3.1.gem * rack-1.1.0.gem -* rack-mount-0.6.4.gem +* rack-mount-0.6.5.gem * rack-test-0.5.4.gem * rails-3.0.0.beta4.gem * railties-3.0.0.beta4.gem -- cgit v1.2.3 From 02399a1184d0f87b14af461fab731120d92c5ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 22 Jun 2010 07:57:02 +0200 Subject: Ensure overwritten callbacks conditions in controllers work [#4761 state:resolved] [#3913 state:resolved] --- actionpack/test/abstract/callbacks_test.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb index 0ce1dc506b..232a1679e0 100644 --- a/actionpack/test/abstract/callbacks_test.rb +++ b/actionpack/test/abstract/callbacks_test.rb @@ -47,8 +47,12 @@ module AbstractController end def index - self.response_body = @text - end + self.response_body = @text.to_s + end + end + + class Callback2Overwrite < Callback2 + before_filter :first, :except => :index end class TestCallbacks2 < ActiveSupport::TestCase @@ -70,6 +74,12 @@ module AbstractController @controller.process(:index) assert_equal "FIRSTSECOND", @controller.instance_variable_get("@aroundz") end + + test "before_filter with overwritten condition" do + @controller = Callback2Overwrite.new + result = @controller.process(:index) + assert_equal "", @controller.response_body + end end class Callback3 < ControllerWithCallbacks -- cgit v1.2.3 From 0576ec4ddd5f806c6ae98184d28391fc239db1ef Mon Sep 17 00:00:00 2001 From: David Chelimsky Date: Tue, 22 Jun 2010 01:40:22 -0500 Subject: Add support for specifying locals in view tests with assert template [#4927 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_controller/test_case.rb | 16 +++++++++++++--- actionpack/lib/action_view/test_case.rb | 15 +++++++++++++++ actionpack/test/template/test_case_test.rb | 18 ++++++++++++++++-- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 7f9eb2cfd1..26a385011f 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -40,7 +40,7 @@ module ActionController ActiveSupport::Notifications.unsubscribe("!render_template.action_view") end - # Asserts that the request was rendered with the appropriate template file or partials + # Asserts that the request was rendered with the appropriate template file or partials. # # ==== Examples # @@ -53,6 +53,12 @@ module ActionController # # assert that no partials were rendered # assert_template :partial => false # + # In a view test case, you can also assert that specific locals are passed + # to partials: + # + # # assert that the "_customer" partial was rendered with a specific object + # assert_template :partial => '_customer', :locals => { :customer => @customer } + # def assert_template(options = {}, message = nil) validate_request! @@ -72,9 +78,13 @@ module ActionController end when Hash if expected_partial = options[:partial] - if expected_count = options[:count] + if expected_locals = options[:locals] + actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')] + expected_locals.each_pair do |k,v| + assert_equal(v, actual_locals[k]) + end + elsif expected_count = options[:count] actual_count = @partials[expected_partial] - # actual_count = found.nil? ? 0 : found[1] msg = build_message(message, "expecting ? to be rendered ? time(s) but rendered ? time(s)", expected_partial, expected_count, actual_count) diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index b698b4cfec..fcfbb92157 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -103,6 +103,10 @@ module ActionView output end + def locals + @locals ||= {} + end + included do setup :setup_with_controller end @@ -132,12 +136,23 @@ module ActionView end end + module Locals + attr_accessor :locals + + def _render_partial(options) + locals[options[:partial]] = options[:locals] + super(options) + end + end + def _view @_view ||= begin view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller) view.singleton_class.send :include, _helpers view.singleton_class.send :include, @controller._router.url_helpers view.singleton_class.send :delegate, :alert, :notice, :to => "request.flash" + view.extend(Locals) + view.locals = self.locals view.output_buffer = self.output_buffer view end diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index c365aec841..d86dc7b185 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -220,10 +220,24 @@ module ActionView end class RenderTemplateTest < ActionView::TestCase - test "render template" do + test "render template supports specifying partials" do controller.controller_path = "test" render(:template => "test/calling_partial_with_layout") - assert_template "partial_for_use_in_layout" + assert_template :partial => "_partial_for_use_in_layout" + end + + test "render template supports specifying locals (passing)" do + controller.controller_path = "test" + render(:template => "test/calling_partial_with_layout") + assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "David" } + end + + test "render template supports specifying locals (failing)" do + controller.controller_path = "test" + render(:template => "test/calling_partial_with_layout") + assert_raise Test::Unit::AssertionFailedError, /Somebody else.*David/m do + assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "Somebody Else" } + end end end end -- cgit v1.2.3 From 6ea967729fef4e3155fb7a50813b503a0bfdc07f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 22 Jun 2010 08:13:58 -0300 Subject: Avoid method redefined warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activemodel/test/cases/validations/callbacks_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activemodel/test/cases/validations/callbacks_test.rb b/activemodel/test/cases/validations/callbacks_test.rb index 67b21eb106..1cf09758f9 100644 --- a/activemodel/test/cases/validations/callbacks_test.rb +++ b/activemodel/test/cases/validations/callbacks_test.rb @@ -5,7 +5,8 @@ class Dog include ActiveModel::Validations include ActiveModel::Validations::Callbacks - attr_accessor :name, :history + attr_accessor :name + attr_writer :history def history @history ||= [] -- cgit v1.2.3 From dcfb9805f2013853eb4d6cdd12f0301bb16cbc26 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 22 Jun 2010 09:02:10 -0400 Subject: test for #4862 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/test/template/form_helper_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 8de1e782c0..11a3283092 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -300,6 +300,11 @@ class FormHelperTest < ActionView::TestCase ) end + def test_radio_button_with_negative_integer_value + assert_dom_equal('', + radio_button("post", "secret", "-1")) + end + def test_radio_button_respects_passed_in_id assert_dom_equal('', radio_button("post", "secret", "1", :id=>"foo") -- cgit v1.2.3 From 70c932f794073812818a1bd6b9d7de422b5ef679 Mon Sep 17 00:00:00 2001 From: RainerBlessing Date: Thu, 17 Jun 2010 20:12:19 +0200 Subject: adds minus to value part of id [#4862 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_view/helpers/form_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index b0b654d5ce..d44bae9c3e 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1031,7 +1031,7 @@ module ActionView private def add_default_name_and_id_for_value(tag_value, options) unless tag_value.nil? - pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase + pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase specified_id = options["id"] add_default_name_and_id(options) options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present? -- cgit v1.2.3 From 9be0b509628388a290b117e6b02df871d97f8c2d Mon Sep 17 00:00:00 2001 From: rohit Date: Tue, 22 Jun 2010 18:44:46 +0530 Subject: Added 4 tests for Rails Runner. 2 failing tests for $0 and $PROGRAM_NAME [#2244 state:open] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/test/application/runner_test.rb | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 railties/test/application/runner_test.rb diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb new file mode 100644 index 0000000000..d37b7649e2 --- /dev/null +++ b/railties/test/application/runner_test.rb @@ -0,0 +1,49 @@ +require 'isolation/abstract_unit' + +module ApplicationTests + class RunnerTest < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app + boot_rails + + # Lets create a model so we have something to play with + app_file "app/models/user.rb", <<-MODEL + class User + def self.count + 42 + end + end + MODEL + end + + def test_should_run_ruby_statement + assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "puts User.count"` } + end + + def test_should_run_file + app_file "script/count_users.rb", <<-SCRIPT + puts User.count + SCRIPT + + assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "script/count_users.rb"` } + end + + def test_should_set_dollar_0_to_file + app_file "script/dollar0.rb", <<-SCRIPT + puts $0 + SCRIPT + + assert_match "script/dollar0.rb", Dir.chdir(app_path) { `bundle exec rails runner "script/dollar0.rb"` } + end + + def test_should_set_dollar_program_name_to_file + app_file "script/program_name.rb", <<-SCRIPT + puts $PROGRAM_NAME + SCRIPT + + assert_match "script/program_name.rb", Dir.chdir(app_path) { `bundle exec rails runner "script/program_name.rb"` } + end + end +end -- cgit v1.2.3 From 40bf76165c392a15cd74321dd7d1cbc96c77bd27 Mon Sep 17 00:00:00 2001 From: rohit Date: Tue, 22 Jun 2010 18:45:58 +0530 Subject: Rails Runner now sets $0 and $PROGRAM_NAME to name of file being run [#2244 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/commands/runner.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb index 278548558e..b97ff086b6 100644 --- a/railties/lib/rails/commands/runner.rb +++ b/railties/lib/rails/commands/runner.rb @@ -44,6 +44,7 @@ begin $stderr.puts "Run '#{$0} -h' for help." exit 1 elsif File.exist?(code_or_file) + $0 = code_or_file eval(File.read(code_or_file), nil, code_or_file) else eval(code_or_file) -- cgit v1.2.3 From 256a33d0fb78cb4fb337938c82a52d042dd4fa2a Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 21 Jun 2010 19:02:04 +0200 Subject: defines ORIG_ARGV in Active Support's abstract_unit.rb (used in isolation.rb) [#4922 state:resolved] Signed-off-by: Xavier Noria --- activesupport/test/abstract_unit.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index dd84860a91..b032331a0b 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -1,3 +1,5 @@ +ORIG_ARGV = ARGV.dup + begin old, $VERBOSE = $VERBOSE, nil require File.expand_path('../../../load_paths', __FILE__) -- cgit v1.2.3 From b8330a22619f647f16a671c1ad58730d8896f806 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 21 Jun 2010 18:19:12 +0200 Subject: preventing memcached initialization errors with default servers list [#4921 state:resolved] As of this writing the JRuby client does not support a default port. Signed-off-by: Xavier Noria --- activesupport/lib/active_support/cache/mem_cache_store.rb | 2 +- activesupport/test/abstract_unit.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index e3a2688e2f..3c46ae5424 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -35,7 +35,7 @@ module ActiveSupport def self.build_mem_cache(*addresses) addresses = addresses.flatten options = addresses.extract_options! - addresses = ["localhost"] if addresses.empty? + addresses = ["localhost:11211"] if addresses.empty? MemCache.new(addresses, options) end diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index b032331a0b..6db21f9e12 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -25,7 +25,7 @@ require 'active_support/ruby/shim' if RUBY_VERSION < '1.8.7' def uses_memcached(test_name) require 'memcache' begin - MemCache.new('localhost').stats + MemCache.new('localhost:11211').stats yield rescue MemCache::MemCacheError $stderr.puts "Skipping #{test_name} tests. Start memcached and try again." -- cgit v1.2.3 From df595b577edf807c3b9315feb882d12d9b249af9 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 22 Jun 2010 10:29:59 -0300 Subject: Use the new query API in AR performance script. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/examples/performance.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index f69576b240..f7d358337c 100644 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -116,15 +116,15 @@ RBench.run(TIMES) do end report 'Model.all limit(100)', (TIMES / 10).ceil do - ar { Exhibit.look Exhibit.all(:limit => 100) } + ar { Exhibit.look Exhibit.limit(100) } end report 'Model.all limit(100) with relationship', (TIMES / 10).ceil do - ar { Exhibit.feel Exhibit.all(:limit => 100, :include => :user) } + ar { Exhibit.feel Exhibit.limit(100).includes(:user) } end report 'Model.all limit(10,000)', (TIMES / 1000).ceil do - ar { Exhibit.look Exhibit.all(:limit => 10000) } + ar { Exhibit.look Exhibit.limit(10000) } end exhibit = { -- cgit v1.2.3 From 7df105b1e604846f5dfe184128f99499da477bbe Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 22 Jun 2010 10:46:12 -0300 Subject: Fix order method to accept relation attributes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- activerecord/test/cases/relations_test.rb | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 60fa839df2..43032ba9d8 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -137,7 +137,7 @@ module ActiveRecord arel = arel.group(*@group_values.uniq.select{|g| g.present?}) if @group_values.present? - arel = arel.order(*@order_values.uniq.select{|o| o.present?}.map(&:to_s)) if @order_values.present? + arel = arel.order(*@order_values.uniq.select{|o| o.present?}) if @order_values.present? selects = @select_values.uniq diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 43519db976..ece287d771 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -503,13 +503,13 @@ class RelationTest < ActiveRecord::TestCase def test_many posts = Post.scoped - + assert_queries(2) do assert posts.many? # Uses COUNT() assert posts.many? {|p| p.id > 0 } assert ! posts.many? {|p| p.id < 2 } end - + assert posts.loaded? end @@ -593,4 +593,8 @@ class RelationTest < ActiveRecord::TestCase assert_equal "lifo", relation.author assert_equal "lifo", relation.limit(1).author end + + def test_order_by_relation_attribute + assert_equal Post.order(Post.arel_table[:title]).all, Post.order("title").all + end end -- cgit v1.2.3 From 12a9e8479958bcada73b3df6b254ce69cb02305c Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 22 Jun 2010 10:51:09 -0300 Subject: Clean CHANGELOG whitespace. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/CHANGELOG | 508 ++++++++++++++++++++++++------------------------- 1 file changed, 254 insertions(+), 254 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index def519c05d..09f897eaac 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -309,7 +309,7 @@ * Add first/last methods to associations/named_scope. Resolved #226. [Ryan Bates] -* Added SQL escaping for :limit and :offset #288 [Aaron Bedra, Steven Bristol, Jonathan Wiess] +* Added SQL escaping for :limit and :offset #288 [Aaron Bedra, Steven Bristol, Jonathan Wiess] * Added first/last methods to associations/named_scope. Resolved #226. [Ryan Bates] @@ -620,7 +620,7 @@ so newlines etc are escaped #10385 [Norbert Crombach] * Ensure that mysql quotes table names with database names correctly. Closes #9911 [crayz] - "foo.bar" => "`foo`.`bar`" + "foo.bar" => "`foo`.`bar`" * Complete the assimilation of Sexy Migrations from ErrFree [Chris Wanstrath, PJ Hyett] http://errtheblog.com/post/2381 @@ -755,7 +755,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea # Ensure that has_many :through associations use a count query instead of loading the target when #size is called. Closes #8800 [Pratik Naik] -* Added :unless clause to validations #8003 [monki]. Example: +* Added :unless clause to validations #8003 [monki]. Example: def using_open_id? !identity_url.blank? @@ -775,7 +775,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea OLD belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is user_id - + NEW belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is visitor_id @@ -881,9 +881,9 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea t.column "created_at", :datetime t.column "updated_at", :datetime end - + ...can now be written as: - + create_table :products do |t| t.integer :shop_id, :creator_id t.string :name, :value, :default => "Untitled" @@ -959,7 +959,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Lütke] Task.cache { Task.find(1); Task.find(1) } #=> 1 query - + * When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck] * Oracle: fix lob and text default handling. #7344 [gfriedrich, Michael Schoen] @@ -1228,7 +1228,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael Schoen] -* Add documentation for how to disable timestamps on a per model basis. Closes #5684. [matt@mattmargolis.net Marcel Molina Jr.] +* Add documentation for how to disable timestamps on a per model basis. Closes #5684. [matt@mattmargolis.net Marcel Molina Jr.] * Don't save has_one associations unnecessarily. #5735 [Jonathan Viney] @@ -1248,8 +1248,8 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples: - assert (Topic.exists?(:author_name => "David")) - assert (Topic.exists?(:author_name => "Mary", :approved => true)) + assert (Topic.exists?(:author_name => "David")) + assert (Topic.exists?(:author_name => "Mary", :approved => true)) assert (Topic.exists?(["parent_id = ?", 1])) * Schema dumper quotes date :default values. [Dave Thomas] @@ -1327,14 +1327,14 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example: - Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2) + Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2) ...is the same as: Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2) - + This makes it easier to pass in the options from a form or otherwise outside. - + * Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz@gmail.com] @@ -1362,7 +1362,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [Josh Susser] -* Provide Association Extensions access to the instance that the association is being accessed from. +* Provide Association Extensions access to the instance that the association is being accessed from. Closes #4433 [Josh Susser] * Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell] @@ -1381,7 +1381,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 [info@loobmedia.com] -* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk] +* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk] * PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan@thebasement.org] @@ -1391,7 +1391,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.] -* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com] +* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com] * Replace superfluous name_to_class_name variant with camelize. [Marcel Molina Jr.] @@ -1401,7 +1401,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac ] -* Update FrontBase adapter to check binding version. Closes #4920. [mlaster@metavillage.com] +* Update FrontBase adapter to check binding version. Closes #4920. [mlaster@metavillage.com] * New Frontbase connections don't start in auto-commit mode. Closes #4922. [mlaster@metavillage.com] @@ -1413,9 +1413,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil ] -* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić] +* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić] -* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu] +* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu] * Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism@yahoo.com] @@ -1466,7 +1466,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] *1.15.3* (March 12th, 2007) - * Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool] + * Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool] * Consistently quote primary key column names. #7763 [toolmantim] @@ -1705,8 +1705,8 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples: - assert (Topic.exists?(:author_name => "David")) - assert (Topic.exists?(:author_name => "Mary", :approved => true)) + assert (Topic.exists?(:author_name => "David")) + assert (Topic.exists?(:author_name => "Mary", :approved => true)) assert (Topic.exists?(["parent_id = ?", 1])) * Schema dumper quotes date :default values. [Dave Thomas] @@ -1780,7 +1780,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example: - Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2) + Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2) ...is the same as: @@ -1815,7 +1815,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [Josh Susser] -* Provide Association Extensions access to the instance that the association is being accessed from. +* Provide Association Extensions access to the instance that the association is being accessed from. Closes #4433 [Josh Susser] * Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell] @@ -1834,7 +1834,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 [info@loobmedia.com] -* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk] +* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk] * PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan@thebasement.org] @@ -1844,7 +1844,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.] -* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com] +* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com] * Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac ] @@ -1854,9 +1854,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil ] -* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić] +* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić] -* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu] +* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu] * Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism@yahoo.com] @@ -1904,7 +1904,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Add warning about the proper way to validate the presence of a foreign key. #4147 [Francois Beausoleil ] -* Fix syntax error in documentation. #4679 [Mislav Marohnić] +* Fix syntax error in documentation. #4679 [Mislav Marohnić] * Update inconsistent migrations documentation. #4683 [machomagna@gmail.com] @@ -2200,7 +2200,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] 1 50 - + * Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck] @@ -2390,17 +2390,17 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing] * Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber] -* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron] +* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron] -* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke] +* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke] -* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke] +* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke] -* made .find() work on :through relations. [Tobias Lütke] +* made .find() work on :through relations. [Tobias Lütke] * Fix typo in association docs. #3296. [Blair Zajac] -* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke] +* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke] *1.13.2* (December 13th, 2005) @@ -2663,7 +2663,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 [purestorm@ggnore.net] -* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell ] +* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell ] * Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around. @@ -2818,7 +2818,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio def self.search(q) find(:all, :conditions => ["body = ?", q]) end - end + end class Post < AR:B has_many :comments @@ -2877,7 +2877,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio def evaluate_velocity(developer) ... end - end + end ..raising an exception will cause the object not to be added (or removed, with before_remove). @@ -2913,10 +2913,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio ...should instead be: Developer.find( - :all, - :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id', + :all, + :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id', :conditions => 'project_id=1' - ) + ) * Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina Jr.] @@ -2953,7 +2953,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Return PostgreSQL columns in the order they were declared #1374 [perlguy@gmail.com] -* Allow before/after update hooks to work on models using optimistic locking +* Allow before/after update hooks to work on models using optimistic locking * Eager loading of dependent has_one associations won't delete the association #1212 @@ -3133,10 +3133,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example: - Developer.find_all nil, 'id ASC', 5 # return the first five developers + Developer.find_all nil, 'id ASC', 5 # return the first five developers Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward - This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged. + This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged. * Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson] @@ -3171,8 +3171,8 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation: - * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the - +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+ + * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the + +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+ objects that should be inspected to determine which attributes triggered the errors. * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method. You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error. @@ -3258,11 +3258,11 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn] - 1. Created a new columns method that is much cleaner. - 2. Corrected a problem with the select and select_all methods - that didn't account for the LIMIT clause being passed into raw SQL statements. - 3. Implemented the string_to_time method in order to create proper instances of the time class. - 4. Added logic to the simplified_type method that allows the database to specify the scale of float data. + 1. Created a new columns method that is much cleaner. + 2. Corrected a problem with the select and select_all methods + that didn't account for the LIMIT clause being passed into raw SQL statements. + 3. Implemented the string_to_time method in order to create proper instances of the time class. + 4. Added logic to the simplified_type method that allows the database to specify the scale of float data. 5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string. * Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow] @@ -3385,9 +3385,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed binary support for PostgreSQL #444 [alex@byzantine.no] -* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the - collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If - it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards, +* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the + collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If + it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length. * Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de] @@ -3400,7 +3400,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper] -* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower] +* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower] * Added that Observers can use the observes class method instead of overwriting self.observed_class(). @@ -3463,7 +3463,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio class Post < ActiveRecord::Base has_one :tagging, :as => :taggable end - + Post.find :all, :include => :tagging * Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick Olson] @@ -3474,7 +3474,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio has_many :photos, :as => :photographic belongs_to :firm end - + class Firm < ActiveRecord::Base has_many :photo_collections has_many :photos, :through => :photo_collections @@ -3536,7 +3536,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio In this example, :include => false disables the default eager association from loading. :select changes the standard select clause. :joins specifies a join that is added to the end of the has_many :through query. - + class Post < ActiveRecord::Base has_many :tags, :through => :taggings, :include => :tagging do def add_joins_and_select @@ -3545,7 +3545,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio end end end - + * Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [David Heinemeier Hansson] * Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [Jonathan Viney] @@ -3562,12 +3562,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio has_many :clients has_many :invoices, :through => :clients end - + class Client < ActiveRecord::Base belongs_to :firm has_many :invoices end - + class Invoice < ActiveRecord::Base belongs_to :client end @@ -3619,19 +3619,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio class CachedModel < ActiveRecord::Base self.abstract_class = true end - + class Post < CachedModel end - + CachedModel.abstract_class? => true - + Post.abstract_class? => false Post.base_class => Post - + Post.table_name => 'posts' @@ -3656,9 +3656,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added Base#to_xml that'll turn the current record into a XML representation [David Heinemeier Hansson]. Example: topic.to_xml - + ...returns: - + The First Topic @@ -3673,13 +3673,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio 2004-04-15 - + ...and you can configure with: - + topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ]) - + ...that'll return: - + The First Topic David @@ -3689,13 +3689,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio 2004-04-15 - + You can even do load first-level associations as part of the document: - + firm.to_xml :include => [ :account, :clients ] - + ...that'll return something like: - + 1 @@ -3715,7 +3715,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio 1 50 - + * Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck] @@ -3733,9 +3733,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio * The first time a table is referenced in a join, no alias is used. * After that, the parent class name and the reflection name are used. - + Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ... - + * Any additional join references get a numerical suffix like '_2', '_3', etc. * Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to. @@ -3747,28 +3747,28 @@ in effect. Added :readonly finder constraint. Calling an association collectio => authors +- posts +- comments - + # cascaded in two levels and normal association >> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations]) => authors +- posts +- comments +- categorizations - + # cascaded in two levels with two has_many associations >> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}) => authors +- posts +- comments +- categorizations - + # cascaded in three levels >> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}}) => companies +- groups +- members +- favorites - + * Make counter cache work when replacing an association #3245 [eugenol@gmail.com] * Make migrations verbose [Jamis Buck] @@ -3905,17 +3905,17 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber] -* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron] +* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron] -* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke] +* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke] -* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke] +* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke] -* made .find() work on :through relations. [Tobias Lütke] +* made .find() work on :through relations. [Tobias Lütke] * Fix typo in association docs. #3296. [Blair Zajac] -* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke] +* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke] *1.13.2* (December 13th, 2005) @@ -3928,7 +3928,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio class Post has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author end - + post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors @@ -4077,9 +4077,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio # Associated with :post_id Comment.create :body => "Hello world" end - + This is rarely used directly, but allows for find_or_create on associations. So you can do: - + # If the tag doesn't exist, a new one is created that's associated with the person person.tags.find_or_create_by_name("Summer") @@ -4087,7 +4087,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio # No 'Summer' tag exists Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") - + # Now the 'Summer' tag does exist Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer") @@ -4178,7 +4178,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 [purestorm@ggnore.net] -* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell ] +* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell ] * Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around. @@ -4333,14 +4333,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio def self.search(q) find(:all, :conditions => ["body = ?", q]) end - end + end class Post < AR:B has_many :comments end Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi' - + NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself @@ -4388,14 +4388,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio class Project has_and_belongs_to_many :developers, :before_add => :evaluate_velocity - + def evaluate_velocity(developer) ... end - end - + end + ..raising an exception will cause the object not to be added (or removed, with before_remove). - + * Fixed Base.content_columns call for SQL Server adapter #1450 [DeLynn Berry] @@ -4424,14 +4424,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie@gmail.com]. What used to be: Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1' - + ...should instead be: - + Developer.find( - :all, - :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id', + :all, + :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id', :conditions => 'project_id=1' - ) + ) * Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina Jr.] @@ -4450,12 +4450,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")] david.save - + If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new project is saved when david.save is called. - + Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do: - + david.project_ids = [1, 5, 7] * Corrected typo in find SQL for has_and_belongs_to_many. #1312 [ben@bensinclair.com] @@ -4468,7 +4468,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Return PostgreSQL columns in the order they were declared #1374 [perlguy@gmail.com] -* Allow before/after update hooks to work on models using optimistic locking +* Allow before/after update hooks to work on models using optimistic locking * Eager loading of dependent has_one associations won't delete the association #1212 @@ -4499,7 +4499,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio Conditional validations can also solve the salted login generator problem: validates_confirmation_of :password, :if => :new_password? - + Using blocks: validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 } @@ -4562,11 +4562,11 @@ in effect. Added :readonly finder constraint. Calling an association collectio puts "Written by: " + post.author.name puts "Last comment on: " + post.comments.first.created_on end - + This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as: - + for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ]) - + ...and the number of database queries needed is now 1. * Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples: @@ -4648,10 +4648,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example: - Developer.find_all nil, 'id ASC', 5 # return the first five developers + Developer.find_all nil, 'id ASC', 5 # return the first five developers Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward - - This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged. + + This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged. * Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson] @@ -4686,8 +4686,8 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation: - * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the - +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+ + * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the + +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+ objects that should be inspected to determine which attributes triggered the errors. * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method. You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error. @@ -4715,16 +4715,16 @@ in effect. Added :readonly finder constraint. Calling an association collectio Validates whether the value of the specified attribute is numeric by trying to convert it to a float with Kernel.Float (if integer is false) or applying it to the regular expression /^[\+\-]?\d+$/ (if integer is set to true). - + class Person < ActiveRecord::Base validates_numericality_of :value, :on => :create end - + Configuration options: * message - A custom error message (default is: "is not a number") * on Specifies when this validation is active (default is :save, other options :create, :update) * only_integer Specifies whether the value has to be an integer, e.g. an integral value (default is false) - + * Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 [Scott Barron] @@ -4773,17 +4773,17 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn] - 1. Created a new columns method that is much cleaner. - 2. Corrected a problem with the select and select_all methods - that didn't account for the LIMIT clause being passed into raw SQL statements. - 3. Implemented the string_to_time method in order to create proper instances of the time class. - 4. Added logic to the simplified_type method that allows the database to specify the scale of float data. + 1. Created a new columns method that is much cleaner. + 2. Corrected a problem with the select and select_all methods + that didn't account for the LIMIT clause being passed into raw SQL statements. + 3. Implemented the string_to_time method in order to create proper instances of the time class. + 4. Added logic to the simplified_type method that allows the database to specify the scale of float data. 5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string. * Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow] * Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example: - + class Person < ActiveRecord::Base validates_each :first_name, :last_name do |record, attr| record.errors.add attr, 'starts with z.' if attr[0] == ?z @@ -4861,19 +4861,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio class Book < ActiveRecord::Base has_many :pages belongs_to :library - + validates_associated :pages, :library end - + * Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition: == Unsaved objects and associations - + You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be aware of, mostly involving the saving of associated objects. - + === One-to-one associations - + * Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in order to update their primary keys - except if the parent object is unsaved (new_record? == true). * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment @@ -4881,9 +4881,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below). * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does not save the parent either. - + === Collections - + * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object (the owner of the collection) is not yet stored in the database. * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false. @@ -4900,9 +4900,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed binary support for PostgreSQL #444 [alex@byzantine.no] -* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the - collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If - it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards, +* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the + collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If + it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length. * Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de] @@ -4915,7 +4915,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper] -* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower] +* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower] * Added that Observers can use the observes class method instead of overwriting self.observed_class(). @@ -4923,7 +4923,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio class ListSweeper < ActiveRecord::Base def self.observed_class() [ List, Item ] end - + After: class ListSweeper < ActiveRecord::Base observes List, Item @@ -4943,7 +4943,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio page.views # => 1 page.increment!(:views) # executes an UPDATE statement page.views # => 2 - + page.increment(:views).increment!(:views) page.views # => 4 @@ -4955,32 +4955,32 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added automated optimistic locking if the field lock_version is present. Each update to the record increments the lock_version column and the locking facilities ensure that records instantiated twice will let the last one saved raise a StaleObjectError if the first was also updated. Example: - + p1 = Person.find(1) p2 = Person.find(1) - + p1.first_name = "Michael" p1.save - + p2.first_name = "should fail" p2.save # Raises a ActiveRecord::StaleObjectError - + You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, or otherwise apply the business logic needed to resolve the conflict. #384 [Michael Koziarski] -* Added dynamic attribute-based finders as a cleaner way of getting objects by simple queries without turning to SQL. +* Added dynamic attribute-based finders as a cleaner way of getting objects by simple queries without turning to SQL. They work by appending the name of an attribute to find_by_, so you get finders like Person.find_by_user_name, Payment.find_by_transaction_id. So instead of writing Person.find_first(["user_name = ?", user_name]), you just do Person.find_by_user_name(user_name). - + It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like Person.find_by_user_name_and_password or even Payment.find_by_purchaser_and_state_and_country. So instead of writing - Person.find_first(["user_name = ? AND password = ?", user_name, password]), you just do + Person.find_first(["user_name = ? AND password = ?", user_name, password]), you just do Person.find_by_user_name_and_password(user_name, password). - While primarily a construct for easier find_firsts, it can also be used as a construct for find_all by using calls like + While primarily a construct for easier find_firsts, it can also be used as a construct for find_all by using calls like Payment.find_all_by_amount(50) that is turned into Payment.find_all(["amount = ?", 50]). This is something not as equally useful, though, as it's not possible to specify the order in which the objects are returned. @@ -4988,13 +4988,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio Before: before_destroy(Proc.new{ |record| Person.destroy_all "firm_id = #{record.id}" }) - + After: before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" } * Added :counter_cache option to acts_as_tree that works just like the one you can define on belongs_to #371 [Josh Peek] -* Added Base.default_timezone accessor that determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates +* Added Base.default_timezone accessor that determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database. This is set to :local by default. * Added the possibility for adapters to overwrite add_limit! to implement a different limiting scheme than "LIMIT X" used by MySQL, PostgreSQL, and SQLite. @@ -5013,7 +5013,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed a bug in the Ruby/MySQL that caused binary content to be escaped badly and come back mangled #405 [Tobias Lütke] -* Fixed that the const_missing autoload assumes the requested constant is set by require_association and calls const_get to retrieve it. +* Fixed that the const_missing autoload assumes the requested constant is set by require_association and calls const_get to retrieve it. If require_association did not set the constant then const_get will call const_missing, resulting in an infinite loop #380 [Jeremy Kemper] * Fixed broken transactions that were actually only running object-level and not db level transactions [andreas] @@ -5033,12 +5033,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio require_association 'person' class Employee < Person end - + after: class Employee < Person end - This also reduces the usefulness of Controller.model in Action Pack to currently only being for documentation purposes. + This also reduces the usefulness of Controller.model in Action Pack to currently only being for documentation purposes. * Added that Base.update_all and Base.delete_all return an integer of the number of affected rows #341 @@ -5056,12 +5056,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added more informative exceptions in establish_connection #356 [Jeremy Kemper] -* Added Base#update_attributes that'll accept a hash of attributes and save the record (returning true if it passed validation, false otherwise). +* Added Base#update_attributes that'll accept a hash of attributes and save the record (returning true if it passed validation, false otherwise). Before: person.attributes = @params["person"] person.save - + Now: person.update_attributes(@params["person"]) @@ -5073,7 +5073,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed that options[:counter_sql] was overwritten with interpolated sql rather than original sql #355 [Jeremy Kemper] -* Fixed that overriding an attribute's accessor would be disregarded by add_on_empty and add_on_boundary_breaking because they simply used +* Fixed that overriding an attribute's accessor would be disregarded by add_on_empty and add_on_boundary_breaking because they simply used the attributes[] hash instead of checking for @base.respond_to?(attr.to_s). [Marten] * Fixed that Base.table_name would expect a parameter when used in has_and_belongs_to_many joins [Anna Lissa Cruz] @@ -5107,10 +5107,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio belongs_to :todo_list end -* Added acts_as_tree that can decorates an existing class with a many to many relationship with itself. Perfect for categories in +* Added acts_as_tree that can decorates an existing class with a many to many relationship with itself. Perfect for categories in categories and the likes. [Tobias Lütke] -* Added that Active Records will automatically record creation and/or update timestamps of database objects if fields of the names +* Added that Active Records will automatically record creation and/or update timestamps of database objects if fields of the names created_at/created_on or updated_at/updated_on are present. [Tobias Lütke] * Added Base.default_error_messages as a hash of all the error messages used in the validates_*_of so they can be changed in one place [Tobias Lütke] @@ -5124,14 +5124,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added a better exception for when a type column is used in a table without the intention of triggering single-table inheritance. Example: ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'bad_class!'. - This error is raised because the column 'type' is reserved for storing the class in case of inheritance. - Please rename this column if you didn't intend it to be used for storing the inheritance class or + This error is raised because the column 'type' is reserved for storing the class in case of inheritance. + Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Company.inheritance_column to use another column for that information. * Added that single-table inheritance will only kick in if the inheritance_column (by default "type") is present. Otherwise, inheritance won't have any magic side effects. -* Added the possibility of marking fields as being in error without adding a message (using nil) to it that'll get displayed wth full_messages #208 [mjobin] +* Added the possibility of marking fields as being in error without adding a message (using nil) to it that'll get displayed wth full_messages #208 [mjobin] * Fixed Base.errors to be indifferent as to whether strings or symbols are used. Examples: @@ -5145,7 +5145,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio errors.on(:name) # => "must be shorter" errors.on("name") # => "must be shorter" -* Added Base.validates_format_of that Validates whether the value of the specified attribute is of the correct form by matching +* Added Base.validates_format_of that Validates whether the value of the specified attribute is of the correct form by matching it against the regular expression provided. [Marcel Molina Jr.] class Person < ActiveRecord::Base @@ -5155,14 +5155,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added Base.validates_length_of that delegates to add_on_boundary_breaking #312 [Tobias Lütke]. Example: Validates that the specified attribute matches the length restrictions supplied in either: - + - configuration[:minimum] - configuration[:maximum] - configuration[:is] - configuration[:within] (aka. configuration[:in]) - + Only one option can be used at a time. - + class Person < ActiveRecord::Base validates_length_of :first_name, :maximum=>30 validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind" @@ -5170,53 +5170,53 @@ in effect. Added :readonly finder constraint. Calling an association collectio validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character" validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me." end - + * Added Base.validate_presence as an alternative to implementing validate and doing errors.add_on_empty yourself. -* Added Base.validates_uniqueness_of that alidates whether the value of the specified attributes are unique across the system. +* Added Base.validates_uniqueness_of that alidates whether the value of the specified attributes are unique across the system. Useful for making sure that only one user can be named "davidhh". - + class Person < ActiveRecord::Base validates_uniqueness_of :user_name end - + When the record is created, a check is performed to make sure that no record exist in the database with the given value for the specified attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself. * Added Base.validates_confirmation_of that encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example: - + Model: class Person < ActiveRecord::Base validates_confirmation_of :password end - + View: <%= password_field "person", "password" %> <%= password_field "person", "password_confirmation" %> - + The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual. It exists only as an in-memory variable for validating the password. This check is performed both on create and update. * Added Base.validates_acceptance_of that encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example: - + class Person < ActiveRecord::Base validates_acceptance_of :terms_of_service end - + The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed both on create and update. NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox. - + * Added validation macros to make the stackable just like the lifecycle callbacks. Examples: class Person < ActiveRecord::Base validate { |record| record.errors.add("name", "too short") unless name.size > 10 } validate { |record| record.errors.add("name", "too long") unless name.size < 20 } validate_on_create :validate_password - + private def validate_password errors.add("password", "too short") unless password.size > 6 @@ -5232,7 +5232,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio through the database's own quoting routine. This should hopefully make it lots easier for new adapters that doesn't accept '1' for integer columns. -* Fixed has_and_belongs_to_many guessing of foreign key so that keys are generated correctly for models like SomeVerySpecialClient +* Fixed has_and_belongs_to_many guessing of foreign key so that keys are generated correctly for models like SomeVerySpecialClient [Florian Weber] * Added counter_sql option for has_many associations [Jeremy Kemper]. Documentation: @@ -5299,9 +5299,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio fixtures/developers/fixtures.yaml fixtures/accounts/fixtures.yaml - + ...you now need to do: - + fixtures/developers.yaml fixtures/accounts.yaml @@ -5328,13 +5328,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio name: David Heinemeier Hansson birthday: 1979-10-15 profession: Systems development - + steve: id: 2 name: Steve Ross Kellock birthday: 1974-09-27 profession: guy with keyboard - + The change is NOT backwards compatible. Fixtures written in the old YAML style needs to be rewritten! * All associations will now attempt to require the classes that they associate to. Relieving the need for most explicit 'require' statements. @@ -5342,7 +5342,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio *1.1.0* (34) -* Added automatic fixture setup and instance variable availability. Fixtures can also be automatically +* Added automatic fixture setup and instance variable availability. Fixtures can also be automatically instantiated in instance variables relating to their names using the following style: class FixturesTest < Test::Unit::TestCase @@ -5359,10 +5359,10 @@ in effect. Added :readonly finder constraint. Calling an association collectio attributes. This is really useful when you have information that's only relevant to the join itself, such as a "added_on" column for an association between post and category. The added attributes will automatically be injected into objects retrieved through the association similar to the piggy-back approach: - + post.categories.push_with_attributes(category, :added_on => Date.today) post.categories.first.added_on # => Date.today - + NOTE: The categories table doesn't have a added_on column, it's the categories_post join table that does! * Fixed that :exclusively_dependent and :dependent can't be activated at the same time on has_many associations [Jeremy Kemper] @@ -5415,8 +5415,8 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Improved the speed of respond_to? by placing the dynamic methods lookup table in a hash [geech] -* Added that any additional fields added to the join table in a has_and_belongs_to_many association - will be placed as attributes when pulling records out through has_and_belongs_to_many associations. +* Added that any additional fields added to the join table in a has_and_belongs_to_many association + will be placed as attributes when pulling records out through has_and_belongs_to_many associations. This is helpful when have information about the association itself that you want available on retrival. * Added better loading exception catching and RubyGems retries to the database adapters [alexeyv] @@ -5425,9 +5425,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed Base#transaction so that it returns the result of the last expression in the transaction block [alexeyv] -* Added Fixture#find to find the record corresponding to the fixture id. The record +* Added Fixture#find to find the record corresponding to the fixture id. The record class name is guessed by using Inflector#classify (also new) on the fixture directory name. - + Before: Document.find(@documents["first"]["id"]) After : @documents["first"].find @@ -5437,7 +5437,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed quoting problems on SQLite by adding quote_string to the AbstractAdapter that can be overwritten by the concrete adapters for a call to the dbm. [Andreas Schwarz] - + * Removed RubyGems backup strategy for requiring SQLite-adapter -- if people want to use gems, they're already doing it with AR. @@ -5453,19 +5453,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added serialize as a new class method to control when text attributes should be YAMLized or not. This means that automated serialization of hashes, arrays, and so on WILL NO LONGER HAPPEN (#10). You need to do something like this: - + class User < ActiveRecord::Base serialize :settings end - + This will assume that settings is a text column and will now YAMLize any object put in that attribute. You can also specify an optional :class_name option that'll raise an exception if a serialized object is retrieved as a descendant of a class not in the hierarchy. Example: - + class User < ActiveRecord::Base serialize :settings, :class_name => "Hash" end - + user = User.create("settings" => %w( one two three )) User.find(user.id).settings # => raises SerializationTypeMismatch @@ -5480,7 +5480,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added the possibility to chain the return of what happened inside a logged block [geech]: - This now works: + This now works: log { ... }.map { ... } Instead of doing: @@ -5490,7 +5490,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added "socket" option for the MySQL adapter, so you can change it to something else than "/tmp/mysql.sock" [Anna Lissa Cruz] -* Added respond_to? answers for all the attribute methods. So if Person has a name attribute retrieved from the table schema, +* Added respond_to? answers for all the attribute methods. So if Person has a name attribute retrieved from the table schema, person.respond_to? "name" will return true. * Added Base.benchmark which can be used to aggregate logging and benchmark, so you can measure and represent multiple statements in a single block. @@ -5511,7 +5511,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added compatibility with 2.x series of sqlite-ruby drivers. [Jamis Buck] * Added type safety for association assignments, so a ActiveRecord::AssociationTypeMismatch will be raised if you attempt to - assign an object that's not of the associated class. This cures the problem with nil giving id = 4 and fixnums giving id = 1 on + assign an object that's not of the associated class. This cures the problem with nil giving id = 4 and fixnums giving id = 1 on mistaken association assignments. [Reported by Andreas Schwarz] * Added the option to keep many fixtures in one single YAML document [what-a-day] @@ -5523,7 +5523,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added the option to observer more than one class at the time by specifying observed_class as an array -* Added auto-id propagation support for tables with arbitrary primary keys that have autogenerated sequences associated with them +* Added auto-id propagation support for tables with arbitrary primary keys that have autogenerated sequences associated with them on PostgreSQL. [Dave Steinberg] * Changed that integer and floats set to "" through attributes= remain as NULL. This was especially a problem for scaffolding and postgresql. (#49) @@ -5565,8 +5565,8 @@ in effect. Added :readonly finder constraint. Calling an association collectio directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used to guess the table name from even when called on Reply. The guessing rules are as follows: * Class name ends in "x", "ch" or "ss": "es" is appended, so a Search class becomes a searches table. - * Class name ends in "y" preceded by a consonant or "qu": The "y" is replaced with "ies", - so a Category class becomes a categories table. + * Class name ends in "y" preceded by a consonant or "qu": The "y" is replaced with "ies", + so a Category class becomes a categories table. * Class name ends in "fe": The "fe" is replaced with "ves", so a Wife class becomes a wives table. * Class name ends in "lf" or "rf": The "f" is replaced with "ves", so a Half class becomes a halves table. * Class name ends in "person": The "person" is replaced with "people", so a Salesperson class becomes a salespeople table. @@ -5579,14 +5579,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Class name with word compositions: Compositions are underscored, so CreditCard class becomes a credit_cards table. Additionally, the class-level table_name_prefix is prepended to the table_name and the table_name_suffix is appended. So if you have "myapp_" as a prefix, the table name guess for an Account class becomes "myapp_accounts". - + You can also overwrite this class method to allow for unguessable links, such as a Mouse class with a link to a "mice" table. Example: - + class Mouse < ActiveRecord::Base def self.table_name() "mice" end end - + This conversion is now done through an external class called Inflector residing in lib/active_record/support/inflector.rb. * Added find_all_in_collection to has_many defined collections. Works like this: @@ -5594,7 +5594,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio class Firm < ActiveRecord::Base has_many :clients end - + firm.id # => 1 firm.find_all_in_clients "revenue > 1000" # SELECT * FROM clients WHERE firm_id = 1 AND revenue > 1000 @@ -5610,11 +5610,11 @@ in effect. Added :readonly finder constraint. Calling an association collectio errors.add_on_boundry_breaking "password", 3..20 end end - + This will add an error to the tune of "is too short (minimum is 3 characters)" or "is too long (minimum is 20 characters)" if the password is outside the boundry. The messages can be changed by passing a third and forth parameter as message strings. -* Implemented a clone method that works properly with AR. It returns a clone of the record that +* Implemented a clone method that works properly with AR. It returns a clone of the record that hasn't been assigned an id yet and is treated as a new record. * Allow for domain sockets in PostgreSQL by not assuming localhost when no host is specified [Scott Barron] @@ -5645,12 +5645,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio *0.9.3* -* Fixed bug with using a different primary key name together with has_and_belongs_to_many [Investigation by Scott] +* Fixed bug with using a different primary key name together with has_and_belongs_to_many [Investigation by Scott] * Added :exclusively_dependent option to the has_many association macro. The doc reads: If set to true all the associated object are deleted in one SQL statement without having their - before_destroy callback run. This should only be used on associations that depend solely on + before_destroy callback run. This should only be used on associations that depend solely on this class and don't need to do any clean-up in before_destroy. The upside is that it's much faster, especially if there's a counter_cache involved. @@ -5662,7 +5662,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Fixed the use of floats (was broken since 0.9.0+) -* Fixed PostgreSQL adapter so default values are displayed properly when used in conjunction with +* Fixed PostgreSQL adapter so default values are displayed properly when used in conjunction with Action Pack scaffolding. * Fixed booleans support for PostgreSQL (use real true/false on boolean fields instead of 0/1 on tinyints) [radsaq] @@ -5688,21 +5688,21 @@ in effect. Added :readonly finder constraint. Calling an association collectio class Event < ActiveRecord::Base has_one_and_belongs_to_many :sponsors end - + class Sponsor < ActiveRecord::Base has_one_and_belongs_to_many :sponsors end Earlier, you'd have to use synthetic methods for creating associations between two objects of the above class: - + roskilde_festival.add_to_sponsors(carlsberg) roskilde_festival.remove_from_sponsors(carlsberg) nike.add_to_events(world_cup) nike.remove_from_events(world_cup) - + Now you can use regular array-styled methods: - + roskilde_festival.sponsors << carlsberg roskilde_festival.sponsors.delete(carlsberg) @@ -5711,7 +5711,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Added delete method for has_many associations. Using this will nullify an association between the has_many and the belonging object by setting the foreign key to null. Consider this model: - + class Post < ActiveRecord::Base has_many :comments end @@ -5732,7 +5732,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio * Active Record is now thread safe! (So you can use it with Cerise and WEBrick applications) [Implementation idea by Michael Neumann, debugging assistance by Jamis Buck] -* Improved performance by roughly 400% on a basic test case of pulling 100 records and querying one attribute. +* Improved performance by roughly 400% on a basic test case of pulling 100 records and querying one attribute. This brings the tax for using Active Record instead of "riding on the metal" (using MySQL-ruby C-driver directly) down to ~50%. Done by doing lazy type conversions and caching column information on the class-level. @@ -5749,46 +5749,46 @@ in effect. Added :readonly finder constraint. Calling an association collectio end Iterating over 100 posts like this: - + <% for post in @posts %> <%= post.title %> has <%= post.comments_count %> comments <% end %> - + Will generate 100 SQL count queries -- one for each call to post.comments_count. If you instead add a "comments_count" int column to the posts table and rewrite the comments association macro with: class Comment < ActiveRecord::Base belongs_to :post, :counter_cache => true end - + Those 100 SQL count queries will be reduced to zero. Beware that counter caching is only appropriate for objects that begin life with the object it's specified to belong with and is destroyed like that as well. Typically objects where you would also specify :dependent => true. If your objects switch from one belonging to another (like a post that can be move from one category to another), - you'll have to manage the counter yourself. + you'll have to manage the counter yourself. * Added natural object-style assignment for has_one and belongs_to associations. Consider the following model: class Project < ActiveRecord::Base has_one :manager end - + class Manager < ActiveRecord::Base belongs_to :project end - + Earlier, assignments would work like following regardless of which way the assignment told the best story: - + active_record.manager_id = david.id - + Now you can do it either from the belonging side: david.project = active_record - + ...or from the having side: - + active_record.manager = david - - If the assignment happens from the having side, the assigned object is automatically saved. So in the example above, the + + If the assignment happens from the having side, the assigned object is automatically saved. So in the example above, the project_id attribute on david would be set to the id of active_record, then david would be saved. * Added natural object-style assignment for has_many associations [Florian Weber]. Consider the following model: @@ -5796,36 +5796,36 @@ in effect. Added :readonly finder constraint. Calling an association collectio class Project < ActiveRecord::Base has_many :milestones end - + class Milestone < ActiveRecord::Base belongs_to :project end - + Earlier, assignments would work like following regardless of which way the assignment told the best story: - + deadline.project_id = active_record.id - + Now you can do it either from the belonging side: deadline.project = active_record - + ...or from the having side: - + active_record.milestones << deadline - + The milestone is automatically saved with the new foreign key. * API CHANGE: Attributes for text (or blob or similar) columns will now have unknown classes stored using YAML instead of using to_s. (Known classes that won't be yamelized are: String, NilClass, TrueClass, FalseClass, Fixnum, Date, and Time). Likewise, data pulled out of text-based attributes will be attempted converged using Yaml if they have the "--- " header. This was primarily done to be enable the storage of hashes and arrays without wrapping them in aggregations, so now you can do: - + user = User.find(1) user.preferences = { "background" => "black", "display" => large } user.save - + User.find(1).preferences # => { "background" => "black", "display" => large } - + Please note that this method should only be used when you don't care about representing the object in proper columns in the database. A money object consisting of an amount and a currency is still a much better fit for a value object done through aggregations than this new option. @@ -5833,14 +5833,14 @@ in effect. Added :readonly finder constraint. Calling an association collectio * POSSIBLE CODE BREAKAGE: As a consequence of the lazy type conversions, it's a bad idea to reference the @attributes hash directly (it always was, but now it's paramount that you don't). If you do, you won't get the type conversion. So to implement new accessors for existing attributes, use read_attribute(attr_name) and write_attribute(attr_name, value) instead. Like this: - + class Song < ActiveRecord::Base # Uses an integer of seconds to hold the length of the song - + def length=(minutes) write_attribute("length", minutes * 60) end - + def length read_attribute("length") / 60 end @@ -5876,7 +5876,7 @@ _Misc_ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the # parenteses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float, # s for String, and a for Array. - + This is incredibly useful for assigning dates from HTML drop-downs of month, year, and day. * Fixed bug with custom primary key column name and Base.find on multiple parameters. @@ -5898,17 +5898,17 @@ _Transactions_ _Mapping_ * Added support for non-integer primary keys [Aredridel/earlier work by Michael Neumann] - + User.find "jdoe" Product.find "PDKEY-INT-12" * Added option to specify naming method for primary key column. ActiveRecord::Base.primary_key_prefix_type can either be set to nil, :table_name, or :table_name_with_underscore. :table_name will assume that Product class has a primary key of "productid" and :table_name_with_underscore will assume "product_id". The default nil will just give "id". - -* Added an overwriteable primary_key method that'll instruct AR to the name of the + +* Added an overwriteable primary_key method that'll instruct AR to the name of the id column [Aredridele/earlier work by Guan Yang] - + class Project < ActiveRecord::Base def self.primary_key() "project_id" end end @@ -5929,8 +5929,8 @@ _Mapping_ _Misc_ * Added freeze call to value object assignments to ensure they remain immutable [Spotted by Gavin Sinclair] - -* Changed interface for specifying observed class in observers. Was OBSERVED_CLASS constant, now is + +* Changed interface for specifying observed class in observers. Was OBSERVED_CLASS constant, now is observed_class() class method. This is more consistant with things like self.table_name(). Works like this: class AuditObserver < ActiveRecord::Observer @@ -5957,20 +5957,20 @@ _Misc_ * Added inheritable callback queues that can ensure that certain callback methods or inline fragments are run throughout the entire inheritance hierarchy. Regardless of whether a descendant overwrites the callback method: - + class Topic < ActiveRecord::Base before_destroy :destroy_author, 'puts "I'm an inline fragment"' end - + Learn more in link:classes/ActiveRecord/Callbacks.html -* Added :dependent option to has_many and has_one, which will automatically destroy associated objects when +* Added :dependent option to has_many and has_one, which will automatically destroy associated objects when the holder is destroyed: - + class Album < ActiveRecord::Base has_many :tracks, :dependent => true end - + All the associated tracks are destroyed when the album is. * Added Base.create as a factory that'll create, save, and return a new object in one step. @@ -5980,7 +5980,7 @@ _Misc_ * Fixed the install.rb to include simple.rb [Spotted by Kevin Bullock] -* Modified block syntax to better follow our code standards outlined in +* Modified block syntax to better follow our code standards outlined in http://www.rubyonrails.org/CodingStandards @@ -5991,7 +5991,7 @@ _Misc_ * Changed adapter-specific connection methods to use centralized ActiveRecord::Base.establish_connection, which is parametized through a config hash with symbol keys instead of a regular parameter list. This will allow for database connections to be opened in a more generic fashion. (Luke) - + NOTE: This requires all *_connections to be updated! Read more in: http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000081 @@ -6000,7 +6000,7 @@ _Misc_ * Fixed SQLite adapter so dates are returned as Date objects, not Time objects [Spotted by Gavin Sinclair] -* Fixed requirement of date class, so date conversions are succesful regardless of whether you +* Fixed requirement of date class, so date conversions are succesful regardless of whether you manually require date or not. @@ -6008,10 +6008,10 @@ _Misc_ * Added transactions -* Changed Base.find to also accept either a list (1, 5, 6) or an array of ids ([5, 7]) +* Changed Base.find to also accept either a list (1, 5, 6) or an array of ids ([5, 7]) as parameter and then return an array of objects instead of just an object -* Fixed method has_collection? for has_and_belongs_to_many macro to behave as a +* Fixed method has_collection? for has_and_belongs_to_many macro to behave as a collection, not an association * Fixed SQLite adapter so empty or nil values in columns of datetime, date, or time type -- cgit v1.2.3 From f29fb0dd9ef3e563f9813ee09e7a9b826f70e3f2 Mon Sep 17 00:00:00 2001 From: David Chelimsky Date: Tue, 22 Jun 2010 09:45:44 -0500 Subject: update the list of instance variables that are excluded from assigns in view tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4930 state:resolved] Signed-off-by: José Valim --- actionpack/lib/action_view/test_case.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index fcfbb92157..00c7386d23 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -159,24 +159,25 @@ module ActionView end EXCLUDE_IVARS = %w{ + @_assertion_wrapped @_result + @_view + @controller + @layouts + @locals + @method_name @output_buffer + @partials @rendered + @request + @routes @templates - @view_context_class - @layouts - @partials - @controller - - @method_name - @fixture_cache - @loaded_fixtures @test_passed + @view_context_class } def _instance_variables instance_variables - EXCLUDE_IVARS - instance_variables end def _assigns -- cgit v1.2.3 From 6c4bab306ae1ee47b7fe5933867d321b68c7699a Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Thu, 10 Jun 2010 19:47:54 -0400 Subject: Missing require for the class/attribute.rb [4812 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/core_ext/class.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activesupport/lib/active_support/core_ext/class.rb b/activesupport/lib/active_support/core_ext/class.rb index f2ca9c7cc9..6f308a0d62 100644 --- a/activesupport/lib/active_support/core_ext/class.rb +++ b/activesupport/lib/active_support/core_ext/class.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/class/inheritable_attributes' require 'active_support/core_ext/class/delegating_attributes' -- cgit v1.2.3 From ec37452f6839d13181a90cf0d4944dc8f97fa1d9 Mon Sep 17 00:00:00 2001 From: Paco Guzman Date: Sun, 20 Jun 2010 18:59:49 +0200 Subject: Remove unused variable on assert_recognizes [#4912 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/testing/assertions/routing.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index 1499c03bdf..9338fa9e70 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -53,7 +53,6 @@ module ActionDispatch extras.each_key { |key| expected_options.delete key } unless extras.nil? expected_options.stringify_keys! - routing_diff = expected_options.diff(request.path_parameters) msg = build_message(message, "The recognized options did not match , difference: ", request.path_parameters, expected_options, expected_options.diff(request.path_parameters)) assert_block(msg) { request.path_parameters == expected_options } -- cgit v1.2.3 From 7033be5f72a2b68223eff5d3946aba51e80b4c0c Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 21 Jun 2010 16:40:45 -0400 Subject: test for #4917 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/relations_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ece287d771..165b2e1e6c 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -16,6 +16,13 @@ class RelationTest < ActiveRecord::TestCase fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments, :taggings + def test_apply_relation_as_where_id + posts = Post.arel_table + post_authors = posts.where(posts[:author_id].eq(1)).project(posts[:id]) + assert_equal 5, post_authors.to_a.size + assert_equal 5, Post.where(:id => post_authors).size + end + def test_scoped topics = Topic.scoped assert_kind_of ActiveRecord::Relation, topics -- cgit v1.2.3 From d0df7f1196ee633a0cea7b5b8cf8b65a8269df19 Mon Sep 17 00:00:00 2001 From: James Harton Date: Mon, 21 Jun 2010 14:21:54 +1200 Subject: Fix small bug where ActiveRecord::PredicateBuilder#build_from_hash didn't test for Arel::Relation as right hand value. [#4917 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation/predicate_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index d0efa2189d..d853fd63d1 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -28,7 +28,7 @@ module ActiveRecord when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation values = value.to_a attribute.in(values) - when Range + when Range, Arel::Relation attribute.in(value) else attribute.eq(value) -- cgit v1.2.3 From 64987d6711747495cb874aa0f5ab827ca01b4009 Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Tue, 22 Jun 2010 11:42:35 -0500 Subject: Note that 'rails server' allows specifying mongrel, thin, etc [#4845 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/commands/server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index cb9b871875..9d9dd48ea9 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -9,7 +9,7 @@ module Rails args, options = args.dup, {} opt_parser = OptionParser.new do |opts| - opts.banner = "Usage: rails server [options]" + opts.banner = "Usage: rails server [mongrel, thin, etc] [options]" opts.on("-p", "--port=port", Integer, "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v } opts.on("-b", "--binding=ip", String, -- cgit v1.2.3 From f61ac899479543859cab67e4ad833284dbf982e9 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 22 Jun 2010 16:00:02 -0300 Subject: Avoid method redefined warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/active_schema_test_postgresql.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/active_schema_test_postgresql.rb b/activerecord/test/cases/active_schema_test_postgresql.rb index 4f04c6735c..f106e14319 100644 --- a/activerecord/test/cases/active_schema_test_postgresql.rb +++ b/activerecord/test/cases/active_schema_test_postgresql.rb @@ -10,7 +10,10 @@ class PostgresqlActiveSchemaTest < Test::Unit::TestCase end def teardown - ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:alias_method, :execute, :real_execute) + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do + remove_method :execute + alias_method :execute, :real_execute + end end def test_create_database_with_encoding -- cgit v1.2.3 From c0fc084e937ff045a75227cee52c801589517435 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 22 Jun 2010 14:00:21 -0400 Subject: should be able to run ActiveModel validations_test [#4933 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activemodel/test/cases/helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index a81584bbad..a32f11484a 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -6,6 +6,7 @@ $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'config' require 'active_model' require 'active_support/core_ext/string/access' +require 'active_support/core_ext/hash/except' # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true -- cgit v1.2.3 From 62c4e4d3856b38ee9869f4ad6342e712788c8635 Mon Sep 17 00:00:00 2001 From: Edgars Beigarts Date: Tue, 22 Jun 2010 11:48:11 +0300 Subject: Fix connection reloading in development mode. [#4929 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/connection_adapters/abstract/connection_pool.rb | 4 +++- .../lib/active_record/connection_adapters/abstract_adapter.rb | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 979ed52f4a..c2d79a421d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -144,7 +144,9 @@ module ActiveRecord @connections.each do |conn| conn.disconnect! if conn.requires_reloading? end - @connections = [] + @connections.delete_if do |conn| + conn.requires_reloading? + end end # Verify active connections and remove and disconnect connections diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 4ee9fee4a9..4567539566 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -142,9 +142,10 @@ module ActiveRecord # this should be overridden by concrete adapters end - # Returns true if its safe to reload the connection between requests for development mode. + # Returns true if its required to reload the connection between requests for development mode. + # This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite. def requires_reloading? - true + false end # Checks whether the connection to the database is still active (i.e. not stale). -- cgit v1.2.3 From fef5cf92ed883daf663299258549bc7aa454db1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 22 Jun 2010 23:23:18 +0200 Subject: Deprecate :name_prefix in the new router DSL. --- actionpack/CHANGELOG | 2 ++ actionpack/lib/action_dispatch/routing/mapper.rb | 42 +++++++++++++----------- actionpack/test/dispatch/routing_test.rb | 6 ++-- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index a00f63dcd4..2b3e6a5714 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* +* Deprecate :name_prefix in the new router DSL [José Valim] + * Add shallow routes back to the new router [Diego Carrion, Andrew White] resources :posts do diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index c31f681411..e0d18b74d8 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -299,6 +299,11 @@ module ActionDispatch options = args.extract_options! options = options.dup + if name_prefix = options.delete(:name_prefix) + options[:as] ||= name_prefix + ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller + end + case args.first when String options[:path] = args.first @@ -343,7 +348,7 @@ module ActionDispatch def namespace(path) path = path.to_s - scope(:path => path, :name_prefix => path, :module => path, :shallow_path => path, :shallow_prefix => path) { yield } + scope(:path => path, :as => path, :module => path, :shallow_path => path, :shallow_prefix => path) { yield } end def constraints(constraints = {}) @@ -359,10 +364,10 @@ module ActionDispatch options = (@scope[:options] || {}).merge(options) - if @scope[:name_prefix] && !options[:as].blank? - options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}" - elsif @scope[:name_prefix] && options[:as] == "" - options[:as] = @scope[:name_prefix].to_s + if @scope[:as] && !options[:as].blank? + options[:as] = "#{@scope[:as]}_#{options[:as]}" + elsif @scope[:as] && options[:as] == "" + options[:as] = @scope[:as].to_s end args.push(options) @@ -382,7 +387,7 @@ module ActionDispatch Mapper.normalize_path("#{parent}/#{child}") end - def merge_name_prefix_scope(parent, child) + def merge_as_scope(parent, child) parent ? "#{parent}_#{child}" : child end @@ -435,6 +440,7 @@ module ActionDispatch @name = entities.to_s @path = options.delete(:path) || @name @controller = options.delete(:controller) || @name.to_s.pluralize + @as = options.delete(:as) @options = options end @@ -453,7 +459,7 @@ module ActionDispatch end def name - options[:as] || @name + @as || @name end def plural @@ -505,7 +511,7 @@ module ActionDispatch def nested_options {}.tap do |opts| - opts[:name_prefix] = member_name + opts[:as] = member_name opts["#{singular}_id".to_sym] = id_constraint if id_constraint? opts[:options] = { :shallow => shallow? } unless options[:shallow].nil? end @@ -537,14 +543,10 @@ module ActionDispatch [:show, :create, :update, :destroy, :new, :edit] end - def initialize(entity, options = {}) - super - end - def member_name name end - alias_method :collection_name, :member_name + alias :collection_name :member_name def nested_path path @@ -552,7 +554,7 @@ module ActionDispatch def nested_options {}.tap do |opts| - opts[:name_prefix] = member_name + opts[:as] = member_name opts[:options] = { :shallow => shallow? } unless @options[:shallow].nil? end end @@ -673,7 +675,7 @@ module ActionDispatch if @scope[:shallow_path].blank? scope(*parent_resource.nested_scope) { yield } else - scope(@scope[:shallow_path], :name_prefix => @scope[:shallow_prefix]) do + scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do scope(*parent_resource.nested_scope) { yield } end end @@ -758,7 +760,7 @@ module ActionDispatch def root(options={}) if @scope[:scope_level] == :resources with_scope_level(:nested) do - scope(parent_resource.path, :name_prefix => parent_resource.collection_name) do + scope(parent_resource.path, :as => parent_resource.collection_name) do super(options) end end @@ -806,14 +808,14 @@ module ActionDispatch def with_exclusive_scope begin - old_name_prefix, old_path = @scope[:name_prefix], @scope[:path] - @scope[:name_prefix], @scope[:path] = nil, nil + old_name_prefix, old_path = @scope[:as], @scope[:path] + @scope[:as], @scope[:path] = nil, nil with_scope_level(:exclusive) do yield end ensure - @scope[:name_prefix], @scope[:path] = old_name_prefix, old_path + @scope[:as], @scope[:path] = old_name_prefix, old_path end end @@ -908,7 +910,7 @@ module ActionDispatch end def name_for_action(action) - name_prefix = @scope[:name_prefix].blank? ? "" : "#{@scope[:name_prefix]}_" + name_prefix = @scope[:as].blank? ? "" : "#{@scope[:as]}_" shallow_prefix = @scope[:shallow_prefix].blank? ? "" : "#{@scope[:shallow_prefix]}_" case action diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 495255c22b..e250810168 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -70,7 +70,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get 'admin/passwords' => "queenbee#passwords", :constraints => ::TestRoutingMapper::IpRestrictor - scope 'pt', :name_prefix => 'pt' do + scope 'pt', :as => 'pt' do resources :projects, :path_names => { :edit => 'editar', :new => 'novo' }, :path => 'projetos' do post :preview, :on => :new end @@ -243,9 +243,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end controller :articles do - scope '/articles', :name_prefix => 'article' do + scope '/articles', :as => 'article' do scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do - match '/:id', :to => :with_id + match '/:id', :to => :with_id, :as => "" end end end -- cgit v1.2.3 From aacb83143f24ff44c046d18930270d650efffec5 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Tue, 22 Jun 2010 17:26:35 -0300 Subject: Allow namespace accept options in routes [#4936 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/mapper.rb | 8 +++++--- actionpack/test/dispatch/routing_test.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index e0d18b74d8..77740b0f53 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -346,9 +346,11 @@ module ActionDispatch scope(controller.to_sym) { yield } end - def namespace(path) + def namespace(path, options = {}) path = path.to_s - scope(:path => path, :as => path, :module => path, :shallow_path => path, :shallow_prefix => path) { yield } + options = { :path => path, :as => path, :module => path, + :shallow_path => path, :shallow_prefix => path }.merge!(options) + scope(options) { yield } end def constraints(constraints = {}) @@ -686,7 +688,7 @@ module ActionDispatch end end - def namespace(path) + def namespace(path, options = {}) if resource_scope? nested { super } else diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index e250810168..51b241eb1f 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -242,6 +242,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + namespace :users, :path => 'usuarios' do + root :to => 'home#index' + end + controller :articles do scope '/articles', :as => 'article' do scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do @@ -932,6 +936,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_namespace_with_options + with_test_routes do + get '/usuarios' + assert_equal '/usuarios', users_root_path + assert_equal 'users/home#index', @response.body + end + end + def test_articles_with_id with_test_routes do get '/articles/rails/1' -- cgit v1.2.3 From e5444f84dfa0237b1ffa758c940723852f366a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 22 Jun 2010 23:52:12 +0200 Subject: Stop filling the blocks scope array with nil. --- actionpack/lib/action_dispatch/routing/mapper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 77740b0f53..2005382b8e 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -418,7 +418,9 @@ module ActionDispatch end def merge_blocks_scope(parent, child) - (parent || []) + [child] + merged = parent ? parent.dup : [] + merged << child if child + merged end def merge_options_scope(parent, child) -- cgit v1.2.3 From 61317b643ae4ff61d458eb273aa86f6f7e780433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Jun 2010 00:43:25 +0200 Subject: Provive resources_path_names helpers in the router. --- actionpack/lib/action_dispatch/routing/mapper.rb | 4 ++++ actionpack/test/dispatch/routing_test.rb | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 2005382b8e..f2c8e9e97e 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -577,6 +577,10 @@ module ActionDispatch @scope[:path_names] = @set.resources_path_names end + def resources_path_names(options) + @scope[:path_names].merge!(options) + end + def resource(*resources, &block) options = resources.extract_options! options = (@scope[:options] || {}).merge(options) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 51b241eb1f..4277321707 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -16,6 +16,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest Routes = routes Routes.draw do default_url_options :host => "rubyonrails.org" + resources_path_names :correlation_indexes => "info_about_correlation_indexes" controller :sessions do get 'login' => :new @@ -86,6 +87,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest resources :projects, :controller => :project do resources :involvements, :attachments + get :correlation_indexes, :on => :collection resources :participants do put :update_all, :on => :collection @@ -112,6 +114,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end member do + get :some_path_with_name put :accessible_projects post :resend, :generate_new_password end @@ -710,6 +713,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_projects_with_resources_path_names + with_test_routes do + get '/projects/info_about_correlation_indexes' + assert_equal 'project#correlation_indexes', @response.body + assert_equal '/projects/info_about_correlation_indexes', correlation_indexes_projects_path + end + end + def test_projects_posts with_test_routes do get '/projects/1/posts' -- cgit v1.2.3 From 9651ca751c71cb132c47a32291b9647365b617f5 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Tue, 22 Jun 2010 19:29:07 -0300 Subject: Add the :path option to match routes when given as symbols. This is specially useful in http helpers for generating routes in scenarios like: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resources :users, :path => 'usuarios' do get :search, :on => :collection, :path => 'pesquisar' end Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/mapper.rb | 17 ++++++++--------- actionpack/test/dispatch/routing_test.rb | 21 ++++++++++++++++++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index f2c8e9e97e..e001d10c0f 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -736,10 +736,10 @@ module ActionDispatch return member { match(*args) } end - path_names = options.delete(:path_names) + path = options.delete(:path) if args.first.is_a?(Symbol) - path = path_for_action(args.first, path_names) + path = path_for_action(args.first, path) options = options_for_action(args.first, options) with_exclusive_scope do @@ -860,7 +860,7 @@ module ActionDispatch end end - def path_for_action(action, path_names) + def path_for_action(action, path) case action when :index, :create "#{@scope[:path]}(.:format)" @@ -881,12 +881,12 @@ module ActionDispatch else case @scope[:scope_level] when :collection, :new - "#{@scope[:path]}/#{action_path(action)}(.:format)" + "#{@scope[:path]}/#{action_path(action, path)}(.:format)" else if parent_resource.shallow? - "#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(action)}(.:format)" + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(action, path)}(.:format)" else - "#{@scope[:path]}/#{action_path(action)}(.:format)" + "#{@scope[:path]}/#{action_path(action, path)}(.:format)" end end end @@ -905,9 +905,8 @@ module ActionDispatch end end - def action_path(name, path_names = nil) - path_names ||= @scope[:path_names] - path_names[name.to_sym] || name.to_s + def action_path(name, path = nil) + path || @scope[:path_names][name.to_sym] || name.to_s end def options_for_action(action, options) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 4277321707..58a1fa0518 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -74,9 +74,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest scope 'pt', :as => 'pt' do resources :projects, :path_names => { :edit => 'editar', :new => 'novo' }, :path => 'projetos' do post :preview, :on => :new + put :close, :on => :member, :path => 'fechar' + get :open, :on => :new, :path => 'abrir' end - resource :admin, :path_names => { :new => 'novo' }, :path => 'administrador' do + resource :admin, :path_names => { :new => 'novo', :activate => 'ativar' }, :path => 'administrador' do post :preview, :on => :new + put :activate, :on => :member end resources :products, :path_names => { :new => 'novo' } do new do @@ -854,6 +857,22 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/pt/administrador/novo' assert_equal 'admins#new', @response.body assert_equal '/pt/administrador/novo', new_pt_admin_path + + put '/pt/administrador/ativar' + assert_equal 'admins#activate', @response.body + assert_equal '/pt/administrador/ativar', activate_pt_admin_path + end + end + + def test_path_option_override + with_test_routes do + get '/pt/projetos/novo/abrir' + assert_equal 'projects#open', @response.body + assert_equal '/pt/projetos/novo/abrir', open_new_pt_project_path + + put '/pt/projetos/1/fechar' + assert_equal 'projects#close', @response.body + assert_equal '/pt/projetos/1/fechar', close_pt_project_path(1) end end -- cgit v1.2.3 From a6eeb0865ed174a2670c002fe8c771c7a12666e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Jun 2010 00:52:20 +0200 Subject: Update CHANGELOG. --- actionpack/CHANGELOG | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 2b3e6a5714..b17a48003c 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,11 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* +* Allow :path to be given to match/get/post/put/delete instead of :path_names in the new router [Carlos Antônio da Silva] + +* Added resources_path_names to the new router DSL [José Valim] + +* Allow options to be given to the namespace method in the new router [Carlos Antônio da Silva] + * Deprecate :name_prefix in the new router DSL [José Valim] * Add shallow routes back to the new router [Diego Carrion, Andrew White] -- cgit v1.2.3 From 97b773303d1fd3c585957b9d4d2bf9485664f892 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Wed, 23 Jun 2010 00:30:22 +0200 Subject: refactored the javascript asset tag helpers and moved the default scripts setup within the railtie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_view/helpers/asset_tag_helper.rb | 19 +++++-------------- actionpack/lib/action_view/railtie.rb | 9 +++++++++ actionpack/test/template/asset_tag_helper_test.rb | 22 +++++++--------------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 63f9fcfcb2..fc256e0ba9 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -194,7 +194,6 @@ module ActionView # RewriteEngine On # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L] module AssetTagHelper - JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be :rss (default) or @@ -351,7 +350,7 @@ module ActionView end end - @@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup } + @@javascript_expansions = { } # Register one or more javascript files to be included when symbol # is passed to javascript_include_tag. This method is typically intended @@ -368,6 +367,10 @@ module ActionView @@javascript_expansions.merge!(expansions) end + def self.reset_javascript_expansions #:nodoc: + @@javascript_expansions = {} + end + @@stylesheet_expansions = {} # Register one or more stylesheet files to be included when symbol @@ -385,18 +388,6 @@ module ActionView @@stylesheet_expansions.merge!(expansions) end - # Register one or more additional JavaScript files to be included when - # javascript_include_tag :defaults is called. This method is - # typically intended to be called from plugin initialization to register additional - # .js files that the plugin installed in public/javascripts. - def self.register_javascript_include_default(*sources) - @@javascript_expansions[:defaults].concat(sources) - end - - def self.reset_javascript_include_default #:nodoc: - @@javascript_expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup - end - # Computes the path to a stylesheet asset in the public stylesheets directory. # If the +source+ filename has no extension, .css will be appended (except for explicit URIs). # Full paths from the document root will be passed through. diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb index e8ea15f47c..d453f30882 100644 --- a/actionpack/lib/action_view/railtie.rb +++ b/actionpack/lib/action_view/railtie.rb @@ -6,6 +6,9 @@ module ActionView class Railtie < Rails::Railtie config.action_view = ActiveSupport::OrderedOptions.new + # setup default js includes + config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] } + require "action_view/railties/log_subscriber" log_subscriber :action_view, ActionView::Railties::LogSubscriber.new @@ -17,6 +20,12 @@ module ActionView end end + initializer "action_view.javascript_expansions" do |app| + ActiveSupport.on_load(:action_view) do + ActionView::Helpers::AssetTagHelper.register_javascript_expansion(app.config.action_view.javascript_expansions) + end + end + initializer "action_view.set_configs" do |app| ActiveSupport.on_load(:action_view) do app.config.action_view.each do |k,v| diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 633641514e..620f975dec 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -44,7 +44,7 @@ class AssetTagHelperTest < ActionView::TestCase @controller.request = @request - ActionView::Helpers::AssetTagHelper::reset_javascript_include_default + ActionView::Helpers::AssetTagHelper::register_javascript_expansion :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] end def url_for(*args) @@ -256,19 +256,6 @@ class AssetTagHelperTest < ActionView::TestCase assert javascript_include_tag("prototype").html_safe? end - def test_register_javascript_include_default - ENV["RAILS_ASSET_ID"] = "" - ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' - assert_dom_equal %(\n\n\n\n\n\n), javascript_include_tag(:defaults) - end - - def test_register_javascript_include_default_mixed_defaults - ENV["RAILS_ASSET_ID"] = "" - ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' - ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'robber', '/elsewhere/cools.js' - assert_dom_equal %(\n\n\n\n\n\n\n\n), javascript_include_tag(:defaults) - end - def test_custom_javascript_expansions ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"] @@ -286,6 +273,11 @@ class AssetTagHelperTest < ActionView::TestCase assert_raise(ArgumentError) { javascript_include_tag('first', :monkey, 'last') } end + def test_reset_javascript_expansions + ActionView::Helpers::AssetTagHelper::reset_javascript_expansions + assert_raise(ArgumentError) { javascript_include_tag(:defaults) } + end + def test_stylesheet_path ENV["RAILS_ASSET_ID"] = "" StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } @@ -923,7 +915,7 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase @request = Struct.new(:protocol).new("gopher://") @controller.request = @request - ActionView::Helpers::AssetTagHelper::reset_javascript_include_default + ActionView::Helpers::AssetTagHelper::reset_javascript_expansions end def url_for(options) -- cgit v1.2.3 From ed5c096d60efc432d6f731f86a8ea3c2cb9aeb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Jun 2010 01:34:25 +0200 Subject: Tidy up asset tag a bit and make railties tests green again. --- .../lib/action_view/helpers/asset_tag_helper.rb | 38 ++++++++-------------- actionpack/lib/action_view/railtie.rb | 11 +++++-- actionpack/test/template/asset_tag_helper_test.rb | 4 +-- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index fc256e0ba9..a3c43d3e93 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -194,6 +194,19 @@ module ActionView # RewriteEngine On # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L] module AssetTagHelper + mattr_reader :javascript_expansions + @@javascript_expansions = { } + + mattr_reader :stylesheet_expansions + @@stylesheet_expansions = {} + + # You can enable or disable the asset tag timestamps cache. + # With the cache enabled, the asset tag helper methods will make fewer + # expensive file system calls. However this prevents you from modifying + # any asset files while the server is running. + # + # ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false + mattr_accessor :cache_asset_timestamps # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be :rss (default) or @@ -350,8 +363,6 @@ module ActionView end end - @@javascript_expansions = { } - # Register one or more javascript files to be included when symbol # is passed to javascript_include_tag. This method is typically intended # to be called from plugin initialization to register javascript files @@ -367,12 +378,6 @@ module ActionView @@javascript_expansions.merge!(expansions) end - def self.reset_javascript_expansions #:nodoc: - @@javascript_expansions = {} - end - - @@stylesheet_expansions = {} - # Register one or more stylesheet files to be included when symbol # is passed to stylesheet_link_tag. This method is typically intended # to be called from plugin initialization to register stylesheet files @@ -698,23 +703,8 @@ module ActionView tag("audio", options) end - def self.cache_asset_timestamps - @@cache_asset_timestamps - end - - # You can enable or disable the asset tag timestamps cache. - # With the cache enabled, the asset tag helper methods will make fewer - # expensive file system calls. However this prevents you from modifying - # any asset files while the server is running. - # - # ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false - def self.cache_asset_timestamps=(value) - @@cache_asset_timestamps = value - end - - @@cache_asset_timestamps = true - private + def rewrite_extension?(source, dir, ext) source_ext = File.extname(source)[1..-1] ext && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(config.assets_dir, dir, "#{source}.#{ext}")))) diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb index d453f30882..8c7b7d64c2 100644 --- a/actionpack/lib/action_view/railtie.rb +++ b/actionpack/lib/action_view/railtie.rb @@ -5,8 +5,7 @@ module ActionView # = Action View Railtie class Railtie < Rails::Railtie config.action_view = ActiveSupport::OrderedOptions.new - - # setup default js includes + config.action_view.stylesheet_expansions = {} config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] } require "action_view/railties/log_subscriber" @@ -22,7 +21,13 @@ module ActionView initializer "action_view.javascript_expansions" do |app| ActiveSupport.on_load(:action_view) do - ActionView::Helpers::AssetTagHelper.register_javascript_expansion(app.config.action_view.javascript_expansions) + ActionView::Helpers::AssetTagHelper.register_javascript_expansion( + app.config.action_view.delete(:javascript_expansions) + ) + + ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion( + app.config.action_view.delete(:stylesheet_expansions) + ) end end diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 620f975dec..6d5e4893c4 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -274,7 +274,7 @@ class AssetTagHelperTest < ActionView::TestCase end def test_reset_javascript_expansions - ActionView::Helpers::AssetTagHelper::reset_javascript_expansions + ActionView::Helpers::AssetTagHelper.javascript_expansions.clear assert_raise(ArgumentError) { javascript_include_tag(:defaults) } end @@ -915,7 +915,7 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase @request = Struct.new(:protocol).new("gopher://") @controller.request = @request - ActionView::Helpers::AssetTagHelper::reset_javascript_expansions + ActionView::Helpers::AssetTagHelper.javascript_expansions.clear end def url_for(options) -- cgit v1.2.3 From 995b1a243cf8450b976eb40613f5cb703a22159a Mon Sep 17 00:00:00 2001 From: knapo Date: Tue, 22 Jun 2010 20:47:31 +0200 Subject: Fix controller_name for non default controller paths [#4901 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_controller/metal.rb | 2 +- actionpack/test/controller/new_base/base_test.rb | 38 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 159d1f0748..2281c500c5 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -61,7 +61,7 @@ module ActionController # ==== Returns # String def self.controller_name - @controller_name ||= controller_path.split("/").last + @controller_name ||= self.name.demodulize.sub(/Controller$/, '').underscore end # Delegates to the class' #controller_name diff --git a/actionpack/test/controller/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb index 0b40f8ce95..8fa5d20372 100644 --- a/actionpack/test/controller/new_base/base_test.rb +++ b/actionpack/test/controller/new_base/base_test.rb @@ -31,9 +31,17 @@ module Dispatching end class EmptyController < ActionController::Base ; end + class SubEmptyController < EmptyController ; end + class NonDefaultPathController < ActionController::Base + def self.controller_path; "i_am_not_default"; end + end module Submodule class ContainedEmptyController < ActionController::Base ; end + class ContainedSubEmptyController < ContainedEmptyController ; end + class ContainedNonDefaultPathController < ActionController::Base + def self.controller_path; "i_am_extremly_not_default"; end + end end class BaseTest < Rack::TestCase @@ -65,16 +73,46 @@ module Dispatching assert_equal EmptyController.controller_path, EmptyController.new.controller_path end + test "non-default controller path" do + assert_equal 'i_am_not_default', NonDefaultPathController.controller_path + assert_equal NonDefaultPathController.controller_path, NonDefaultPathController.new.controller_path + end + + test "sub controller path" do + assert_equal 'dispatching/sub_empty', SubEmptyController.controller_path + assert_equal SubEmptyController.controller_path, SubEmptyController.new.controller_path + end + test "namespaced controller path" do assert_equal 'dispatching/submodule/contained_empty', Submodule::ContainedEmptyController.controller_path assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path end + test "namespaced non-default controller path" do + assert_equal 'i_am_extremly_not_default', Submodule::ContainedNonDefaultPathController.controller_path + assert_equal Submodule::ContainedNonDefaultPathController.controller_path, Submodule::ContainedNonDefaultPathController.new.controller_path + end + + test "namespaced sub controller path" do + assert_equal 'dispatching/submodule/contained_sub_empty', Submodule::ContainedSubEmptyController.controller_path + assert_equal Submodule::ContainedSubEmptyController.controller_path, Submodule::ContainedSubEmptyController.new.controller_path + end + test "controller name" do assert_equal 'empty', EmptyController.controller_name assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name end + test "non-default path controller name" do + assert_equal 'non_default_path', NonDefaultPathController.controller_name + assert_equal 'contained_non_default_path', Submodule::ContainedNonDefaultPathController.controller_name + end + + test "sub controller name" do + assert_equal 'sub_empty', SubEmptyController.controller_name + assert_equal 'contained_sub_empty', Submodule::ContainedSubEmptyController.controller_name + end + test "action methods" do assert_equal Set.new(%w( index -- cgit v1.2.3 From e639536ea80e94f5d72493267c8aec21d305cf74 Mon Sep 17 00:00:00 2001 From: Jeff Dean Date: Wed, 9 Jun 2010 00:13:24 -0400 Subject: remove_column should raise an ArgumentError when no columns are passed [#4803 state:resolved] Signed-off-by: Michael Koziarski --- .../active_record/connection_adapters/abstract/schema_statements.rb | 1 + activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb | 1 + activerecord/test/cases/migration_test.rb | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index d3499cea72..638e5d7236 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -205,6 +205,7 @@ module ActiveRecord # remove_column(:suppliers, :qualification) # remove_columns(:suppliers, :qualification, :experience) def remove_column(table_name, *column_names) + raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty? column_names.flatten.each do |column_name| execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}" end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index deb62e3802..ad6314c530 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -246,6 +246,7 @@ module ActiveRecord end def remove_column(table_name, *column_names) #:nodoc: + raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty? column_names.flatten.each do |column_name| alter_table(table_name) do |definition| definition.columns.delete(definition[column_name]) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 6fe3b01281..9ece2fbab1 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -748,6 +748,10 @@ if ActiveRecord::Base.connection.supports_migrations? ActiveRecord::Base.connection.drop_table(:hats) end + def test_remove_column_no_second_parameter_raises_exception + assert_raise(ArgumentError) { Person.connection.remove_column("funny") } + end + def test_change_type_of_not_null_column assert_nothing_raised do Topic.connection.change_column "topics", "written_on", :datetime, :null => false -- cgit v1.2.3 From ac8d3e3acabf3ece495f591a7195d794d6c611e1 Mon Sep 17 00:00:00 2001 From: Jeff Dean Date: Fri, 11 Jun 2010 20:25:58 -0400 Subject: make text_field and hidden_field omit the value attribute if the developer explicitly passes in :value => nil [#4839 state:resolved] Signed-off-by: Michael Koziarski --- actionpack/lib/action_view/helpers/form_helper.rb | 8 ++++---- actionpack/test/template/form_helper_test.rb | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index d44bae9c3e..2bbe11fef2 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -898,7 +898,7 @@ module ActionView options.delete("size") end options["type"] ||= field_type - options["value"] ||= value_before_type_cast(object) unless field_type == "file" + options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file" options["value"] &&= html_escape(options["value"]) add_default_name_and_id(options) tag("input", options) @@ -1043,14 +1043,14 @@ module ActionView def add_default_name_and_id(options) if options.has_key?("index") options["name"] ||= tag_name_with_index(options["index"]) - options["id"] = options.fetch("id", tag_id_with_index(options["index"])) + options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) } options.delete("index") elsif defined?(@auto_index) options["name"] ||= tag_name_with_index(@auto_index) - options["id"] = options.fetch("id", tag_id_with_index(@auto_index)) + options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) } else options["name"] ||= tag_name + (options.has_key?('multiple') ? '[]' : '') - options["id"] = options.fetch("id", tag_id) + options["id"] = options.fetch("id"){ tag_id } end end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 11a3283092..6ba407e230 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -188,6 +188,11 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, text_field("post", "title", :maxlength => 35, :size => nil) end + def test_text_field_with_nil_value + expected = '' + assert_dom_equal expected, text_field("post", "title", :value => nil) + end + def test_text_field_doesnt_change_param_values object_name = 'post[]' expected = '' @@ -208,6 +213,11 @@ class FormHelperTest < ActionView::TestCase hidden_field("post", "title") end + def test_hidden_field_with_nil_value + expected = '' + assert_dom_equal expected, hidden_field("post", "title", :value => nil) + end + def test_text_field_with_options assert_dom_equal '', hidden_field("post", "title", :value => "Something Else") -- cgit v1.2.3 From ef7a04ea85783b94dfa033cdcd5269c125096684 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 22 Jun 2010 21:14:21 -0300 Subject: Fixes encoding mistakes on old_base/mail_service tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionmailer/test/old_base/mail_service_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionmailer/test/old_base/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb index 527b37218a..831adf3e32 100644 --- a/actionmailer/test/old_base/mail_service_test.rb +++ b/actionmailer/test/old_base/mail_service_test.rb @@ -674,7 +674,7 @@ The body EOF mail = Mail.new(msg) assert_equal "testing testing \326\244", mail.subject - assert_equal "Subject: testing testing =?UTF-8?Q?_=D6=A4=?=\r\n", mail[:subject].encoded + assert_equal "Subject: =?UTF-8?Q?testing_testing_=D6=A4?=\r\n", mail[:subject].encoded end def test_unquote_7bit_subject @@ -863,7 +863,7 @@ EOF def test_multipart_with_utf8_subject mail = TestMailer.multipart_with_utf8_subject(@recipient) - regex = Regexp.escape('Subject: Foo =?UTF-8?Q?=C3=A1=C3=AB=C3=B4=?= =?UTF-8?Q?_=C3=AE=C3=BC=?=') + regex = Regexp.escape('Subject: =?UTF-8?Q?Foo_=C3=A1=C3=AB=C3=B4_=C3=AE=C3=BC?=') assert_match(/#{regex}/, mail.encoded) string = "Foo áëô îü" assert_match(string, mail.subject) @@ -871,7 +871,7 @@ EOF def test_implicitly_multipart_with_utf8 mail = TestMailer.implicitly_multipart_with_utf8 - regex = Regexp.escape('Subject: Foo =?UTF-8?Q?=C3=A1=C3=AB=C3=B4=?= =?UTF-8?Q?_=C3=AE=C3=BC=?=') + regex = Regexp.escape('Subject: =?UTF-8?Q?Foo_=C3=A1=C3=AB=C3=B4_=C3=AE=C3=BC?=') assert_match(/#{regex}/, mail.encoded) string = "Foo áëô îü" assert_match(string, mail.subject) -- cgit v1.2.3 From d132dd33520ba61f7bfa9ba6fdd1b7b2bebd27f3 Mon Sep 17 00:00:00 2001 From: Paul Gillard Date: Fri, 18 Jun 2010 00:02:31 +0100 Subject: Don't clone associations [#4894 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloning an active record object should be shallow in that it should copy attributes but not associations. This was no longer true as a result of #3164. Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 1 + activerecord/test/cases/base_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3f81ca7555..3a7db97a6a 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1433,6 +1433,7 @@ module ActiveRecord #:nodoc: end clear_aggregation_cache + clear_association_cache @attributes_cache = {} @new_record = true ensure_proper_type diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 7c74d87b61..c24a494c85 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1401,6 +1401,14 @@ class BasicsTest < ActiveRecord::TestCase assert_not_equal clone.id, dev.id end + def test_clone_does_not_clone_associations + author = authors(:david) + assert_not_equal [], author.posts + + author_clone = author.clone + assert_equal [], author_clone.posts + end + def test_clone_preserves_subtype clone = nil assert_nothing_raised { clone = Company.find(3).clone } -- cgit v1.2.3 From 7008911222826eef07a338bf4cab27b83fe90ce1 Mon Sep 17 00:00:00 2001 From: "Mohammed Siddick.E" Date: Wed, 23 Jun 2010 12:33:34 +0530 Subject: Patch for Namespace problem in Scaffold. [#4763 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/model/model_generator.rb | 2 +- .../erb/scaffold/templates/_form.html.erb | 8 +- .../erb/scaffold/templates/edit.html.erb | 4 +- .../erb/scaffold/templates/index.html.erb | 14 +-- .../generators/erb/scaffold/templates/new.html.erb | 2 +- .../erb/scaffold/templates/show.html.erb | 4 +- railties/lib/rails/generators/named_base.rb | 18 +++- railties/lib/rails/generators/rails/model/USAGE | 2 +- .../rails/resource/resource_generator.rb | 5 +- .../scaffold_controller/templates/controller.rb | 54 +++++------ railties/lib/rails/generators/resource_helpers.rb | 2 +- .../generators/test_unit/model/model_generator.rb | 2 +- .../scaffold/templates/functional_test.rb | 24 ++--- .../test/generators/scaffold_generator_test.rb | 106 +++++++++++++++++++++ 14 files changed, 186 insertions(+), 61 deletions(-) diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb index 539c2517ee..960c29c49c 100644 --- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb +++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb @@ -22,7 +22,7 @@ module ActiveRecord def create_module_file return if class_path.empty? - template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb") + template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb") if behavior == :invoke end hook_for :test_framework diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb index 9b83207b3f..d12b2ff0e5 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb @@ -1,10 +1,10 @@ -<%%= form_for(@<%= singular_name %>) do |f| %> - <%% if @<%= singular_name %>.errors.any? %> +<%%= form_for(@<%= singular_table_name %>) do |f| %> + <%% if @<%= singular_table_name %>.errors.any? %>
    -

    <%%= pluralize(@<%= singular_name %>.errors.count, "error") %> prohibited this <%= singular_name %> from being saved:

    +

    <%%= pluralize(@<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:

      - <%% @<%= singular_name %>.errors.full_messages.each do |msg| %> + <%% @<%= singular_table_name %>.errors.full_messages.each do |msg| %>
    • <%%= msg %>
    • <%% end %>
    diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb index 415f820206..e58b9fbd08 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb @@ -1,6 +1,6 @@ -

    Editing <%= singular_name %>

    +

    Editing <%= singular_table_name %>

    <%%= render 'form' %> -<%%= link_to 'Show', @<%= singular_name %> %> | +<%%= link_to 'Show', @<%= singular_table_name %> %> | <%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb index d30d306d42..4c46db4d67 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb @@ -1,4 +1,4 @@ -

    Listing <%= plural_name %>

    +

    Listing <%= plural_table_name %>

    @@ -10,18 +10,18 @@ -<%% @<%= plural_name %>.each do |<%= singular_name %>| %> +<%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> <% for attribute in attributes -%> - + <% end -%> - - - + + + <%% end %>
    <%%= <%= singular_name %>.<%= attribute.name %> %><%%= <%= singular_table_name %>.<%= attribute.name %> %><%%= link_to 'Show', <%= singular_name %> %><%%= link_to 'Edit', edit_<%= singular_name %>_path(<%= singular_name %>) %><%%= link_to 'Destroy', <%= singular_name %>, :confirm => 'Are you sure?', :method => :delete %><%%= link_to 'Show', <%= singular_table_name %> %><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %><%%= link_to 'Destroy', <%= singular_table_name %>, :confirm => 'Are you sure?', :method => :delete %>

    -<%%= link_to 'New <%= human_name %>', new_<%= singular_name %>_path %> +<%%= link_to 'New <%= human_name %>', new_<%= singular_table_name %>_path %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb index ddabc9d349..02ae4d015e 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb @@ -1,4 +1,4 @@ -

    New <%= singular_name %>

    +

    New <%= singular_table_name %>

    <%%= render 'form' %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb index 31b8253b35..c0e5ccff1e 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb @@ -3,10 +3,10 @@ <% for attribute in attributes -%>

    <%= attribute.human_name %>: - <%%= @<%= singular_name %>.<%= attribute.name %> %> + <%%= @<%= singular_table_name %>.<%= attribute.name %> %>

    <% end -%> -<%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> | +<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(@<%= singular_table_name %>) %> | <%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 72ec2856d0..44f831e6f3 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -51,7 +51,23 @@ module Rails end def index_helper - uncountable? ? "#{plural_name}_index" : plural_name + uncountable? ? "#{plural_table_name}_index" : plural_table_name + end + + def singular_table_name + @singular_table_name ||= table_name.singularize + end + + def plural_table_name + @plural_table_name ||= table_name.pluralize + end + + def plural_file_name + @plural_file_name ||= file_name.pluralize + end + + def route_url + @route_url ||= class_path.collect{|dname| "/" + dname }.join('') + "/" + plural_file_name end # Tries to retrieve the application name or simple return application. diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE index db98a2dd1b..67f76aad01 100644 --- a/railties/lib/rails/generators/rails/model/USAGE +++ b/railties/lib/rails/generators/rails/model/USAGE @@ -40,6 +40,6 @@ Examples: Module: app/models/admin.rb Model: app/models/admin/account.rb Test: test/unit/admin/account_test.rb - Fixtures: test/fixtures/admin_accounts.yml + Fixtures: test/fixtures/admin/accounts.yml Migration: db/migrate/XXX_add_admin_accounts.rb diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb index 8a46708009..ee302b8aad 100644 --- a/railties/lib/rails/generators/rails/resource/resource_generator.rb +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -18,7 +18,10 @@ module Rails def add_resource_route return if options[:actions].present? - route "resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}" + route_config = class_path.collect{|namespace| "namespace :#{namespace} do " }.join(" ") + route_config << "resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}" + route_config << " end" * class_path.size + route route_config end protected diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb index b5f19b6d15..84cf58d7c4 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb @@ -1,53 +1,53 @@ class <%= controller_class_name %>Controller < ApplicationController <% unless options[:singleton] -%> - # GET /<%= table_name %> - # GET /<%= table_name %>.xml + # GET <%= route_url %> + # GET <%= route_url %>.xml def index - @<%= table_name %> = <%= orm_class.all(class_name) %> + @<%= plural_table_name %> = <%= orm_class.all(class_name) %> respond_to do |format| format.html # index.html.erb - format.xml { render :xml => @<%= table_name %> } + format.xml { render :xml => @<%= plural_table_name %> } end end <% end -%> - # GET /<%= table_name %>/1 - # GET /<%= table_name %>/1.xml + # GET <%= route_url %>/1 + # GET <%= route_url %>/1.xml def show - @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> respond_to do |format| format.html # show.html.erb - format.xml { render :xml => @<%= file_name %> } + format.xml { render :xml => @<%= singular_table_name %> } end end - # GET /<%= table_name %>/new - # GET /<%= table_name %>/new.xml + # GET <%= route_url %>/new + # GET <%= route_url %>/new.xml def new - @<%= file_name %> = <%= orm_class.build(class_name) %> + @<%= singular_table_name %> = <%= orm_class.build(class_name) %> respond_to do |format| format.html # new.html.erb - format.xml { render :xml => @<%= file_name %> } + format.xml { render :xml => @<%= singular_table_name %> } end end - # GET /<%= table_name %>/1/edit + # GET <%= route_url %>/1/edit def edit - @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> end - # POST /<%= table_name %> - # POST /<%= table_name %>.xml + # POST <%= route_url %> + # POST <%= route_url %>.xml def create - @<%= file_name %> = <%= orm_class.build(class_name, "params[:#{file_name}]") %> + @<%= singular_table_name %> = <%= orm_class.build(class_name, "params[:#{singular_table_name}]") %> respond_to do |format| if @<%= orm_instance.save %> - format.html { redirect_to(@<%= file_name %>, :notice => '<%= human_name %> was successfully created.') } - format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> } + format.html { redirect_to(@<%= singular_table_name %>, :notice => '<%= human_name %> was successfully created.') } + format.xml { render :xml => @<%= singular_table_name %>, :status => :created, :location => @<%= singular_table_name %> } else format.html { render :action => "new" } format.xml { render :xml => @<%= orm_instance.errors %>, :status => :unprocessable_entity } @@ -55,14 +55,14 @@ class <%= controller_class_name %>Controller < ApplicationController end end - # PUT /<%= table_name %>/1 - # PUT /<%= table_name %>/1.xml + # PUT <%= route_url %>/1 + # PUT <%= route_url %>/1.xml def update - @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> respond_to do |format| - if @<%= orm_instance.update_attributes("params[:#{file_name}]") %> - format.html { redirect_to(@<%= file_name %>, :notice => '<%= human_name %> was successfully updated.') } + if @<%= orm_instance.update_attributes("params[:#{singular_table_name}]") %> + format.html { redirect_to(@<%= singular_table_name %>, :notice => '<%= human_name %> was successfully updated.') } format.xml { head :ok } else format.html { render :action => "edit" } @@ -71,10 +71,10 @@ class <%= controller_class_name %>Controller < ApplicationController end end - # DELETE /<%= table_name %>/1 - # DELETE /<%= table_name %>/1.xml + # DELETE <%= route_url %>/1 + # DELETE <%= route_url %>/1.xml def destroy - @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> @<%= orm_instance.destroy %> respond_to do |format| diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb index 3a98a8f9c1..829f4b200a 100644 --- a/railties/lib/rails/generators/resource_helpers.rb +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -72,7 +72,7 @@ module Rails end # Initialize ORM::Generators::ActiveModel to access instance methods. - def orm_instance(name=file_name) + def orm_instance(name=singular_table_name) @orm_instance ||= @orm_class.new(name) end end diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb index 609b815683..c1dd535dd3 100644 --- a/railties/lib/rails/generators/test_unit/model/model_generator.rb +++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb @@ -16,7 +16,7 @@ module TestUnit def create_fixture_file if options[:fixture] && options[:fixture_replacement].nil? - template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml") + template 'fixtures.yml', File.join('test/fixtures', class_path, "#{plural_file_name}.yml") end end end diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb index d5d3d6d5cd..957ebaa522 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb @@ -2,7 +2,7 @@ require 'test_helper' class <%= controller_class_name %>ControllerTest < ActionController::TestCase setup do - @<%= file_name %> = <%= table_name %>(:one) + @<%= singular_table_name %> = <%= table_name %>(:one) end <% unless options[:singleton] -%> @@ -18,32 +18,32 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase assert_response :success end - test "should create <%= file_name %>" do + test "should create <%= singular_table_name %>" do assert_difference('<%= class_name %>.count') do - post :create, :<%= file_name %> => @<%= file_name %>.attributes + post :create, :<%= singular_table_name %> => @<%= singular_table_name %>.attributes end - assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>)) + assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>)) end - test "should show <%= file_name %>" do - get :show, :id => @<%= file_name %>.to_param + test "should show <%= singular_table_name %>" do + get :show, :id => @<%= singular_table_name %>.to_param assert_response :success end test "should get edit" do - get :edit, :id => @<%= file_name %>.to_param + get :edit, :id => @<%= singular_table_name %>.to_param assert_response :success end - test "should update <%= file_name %>" do - put :update, :id => @<%= file_name %>.to_param, :<%= file_name %> => @<%= file_name %>.attributes - assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>)) + test "should update <%= singular_table_name %>" do + put :update, :id => @<%= singular_table_name %>.to_param, :<%= singular_table_name %> => @<%= singular_table_name %>.attributes + assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>)) end - test "should destroy <%= file_name %>" do + test "should destroy <%= singular_table_name %>" do assert_difference('<%= class_name %>.count', -1) do - delete :destroy, :id => @<%= file_name %>.to_param + delete :destroy, :id => @<%= singular_table_name %>.to_param end assert_redirected_to <%= index_helper %>_path diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index e8e622fe5c..ea469cb3c8 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -110,4 +110,110 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase # Stylesheets (should not be removed) assert_file "public/stylesheets/scaffold.css" end + + def test_scaffold_with_namespace_on_invoke + run_generator [ "admin/role", "name:string", "description:string" ] + + # Model + assert_file "app/models/admin.rb", /module Admin/ + assert_file "app/models/admin/role.rb", /class Admin::Role < ActiveRecord::Base/ + assert_file "test/unit/admin/role_test.rb", /class Admin::RoleTest < ActiveSupport::TestCase/ + assert_file "test/fixtures/admin/roles.yml" + assert_migration "db/migrate/create_admin_roles.rb" + + # Route + assert_file "config/routes.rb" do |route| + assert_match /namespace :admin do resources :roles end$/, route + end + + # Controller + assert_file "app/controllers/admin/roles_controller.rb" do |content| + assert_match /class Admin::RolesController < ApplicationController/, content + + assert_instance_method :index, content do |m| + assert_match /@admin_roles = Admin::Role\.all/, m + end + + assert_instance_method :show, content do |m| + assert_match /@admin_role = Admin::Role\.find\(params\[:id\]\)/, m + end + + assert_instance_method :new, content do |m| + assert_match /@admin_role = Admin::Role\.new/, m + end + + assert_instance_method :edit, content do |m| + assert_match /@admin_role = Admin::Role\.find\(params\[:id\]\)/, m + end + + assert_instance_method :create, content do |m| + assert_match /@admin_role = Admin::Role\.new\(params\[:admin_role\]\)/, m + assert_match /@admin_role\.save/, m + assert_match /@admin_role\.errors/, m + end + + assert_instance_method :update, content do |m| + assert_match /@admin_role = Admin::Role\.find\(params\[:id\]\)/, m + assert_match /@admin_role\.update_attributes\(params\[:admin_role\]\)/, m + assert_match /@admin_role\.errors/, m + end + + assert_instance_method :destroy, content do |m| + assert_match /@admin_role = Admin::Role\.find\(params\[:id\]\)/, m + assert_match /@admin_role\.destroy/, m + end + end + + assert_file "test/functional/admin/roles_controller_test.rb", + /class Admin::RolesControllerTest < ActionController::TestCase/ + + # Views + %w( + index + edit + new + show + _form + ).each { |view| assert_file "app/views/admin/roles/#{view}.html.erb" } + assert_no_file "app/views/layouts/admin/roles.html.erb" + + # Helpers + assert_file "app/helpers/admin/roles_helper.rb" + assert_file "test/unit/helpers/admin/roles_helper_test.rb" + + # Stylesheets + assert_file "public/stylesheets/scaffold.css" + end + + def test_scaffold_with_namespace_on_revoke + run_generator [ "admin/role", "name:string", "description:string" ] + run_generator [ "admin/role" ], :behavior => :revoke + + # Model + assert_file "app/models/admin.rb" # ( should not be remove ) + assert_no_file "app/models/admin/role.rb" + assert_no_file "test/unit/admin/role_test.rb" + assert_no_file "test/fixtures/admin/roles.yml" + assert_no_migration "db/migrate/create_admin_roles.rb" + + # Route + assert_file "config/routes.rb" do |route| + assert_no_match /namespace :admin do resources :roles end$/, route + end + + # Controller + assert_no_file "app/controllers/admin/roles_controller.rb" + assert_no_file "test/functional/admin/roles_controller_test.rb" + + # Views + assert_no_file "app/views/admin/roles" + assert_no_file "app/views/layouts/admin/roles.html.erb" + + # Helpers + assert_no_file "app/helpers/admin/roles_helper.rb" + assert_no_file "test/unit/helpers/admin/roles_helper_test.rb" + + # Stylesheets (should not be removed) + assert_file "public/stylesheets/scaffold.css" + end end -- cgit v1.2.3 From e8c064bbe0fb5e07c7ceaa45d0cafa3c4ef01ab0 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Wed, 23 Jun 2010 14:41:28 +0200 Subject: Regression with how base errors messages are added to a model. Works correctly for both string error messages and symbol translated messages. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_model/deprecated_error_methods.rb | 2 +- activemodel/lib/active_model/errors.rb | 2 +- activemodel/test/cases/validations_test.rb | 21 ++++++++++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/activemodel/lib/active_model/deprecated_error_methods.rb b/activemodel/lib/active_model/deprecated_error_methods.rb index dd8050c549..adc50773d9 100644 --- a/activemodel/lib/active_model/deprecated_error_methods.rb +++ b/activemodel/lib/active_model/deprecated_error_methods.rb @@ -16,7 +16,7 @@ module ActiveModel end def add_to_base(msg) - ActiveSupport::Deprecation.warn "Errors#add_to_base(msg) has been deprecated, use Errors#[:base] << msg instead" + ActiveSupport::Deprecation.warn "Errors#add_to_base(msg) has been deprecated, use Errors#add(:base, msg) instead" self[:base] << msg end diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index d943e044a7..ae7822d8d5 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -308,7 +308,7 @@ module ActiveModel defaults.flatten! key = defaults.shift - value = @base.send(:read_attribute_for_validation, attribute) + value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil) options = { :default => defaults, diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index af195af080..228c1c074f 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -83,10 +83,9 @@ class ValidationsTest < ActiveModel::TestCase r = Reply.new r.content = "Mismatch" r.valid? - r.errors[:base] << "Reply is not dignifying" + r.errors.add(:base, "Reply is not dignifying") - errors = [] - r.errors.to_a.each { |error| errors << error } + errors = r.errors.to_a.inject([]) { |result, error| result + [error] } assert_equal ["Reply is not dignifying"], r.errors[:base] @@ -95,6 +94,22 @@ class ValidationsTest < ActiveModel::TestCase assert_equal 2, r.errors.count end + def test_errors_on_base_with_symbol_message + r = Reply.new + r.content = "Mismatch" + r.valid? + r.errors.add(:base, :invalid) + + errors = r.errors.to_a.inject([]) { |result, error| result + [error] } + + assert_equal ["is invalid"], r.errors[:base] + + assert errors.include?("Title is Empty") + assert errors.include?("is invalid") + + assert_equal 2, r.errors.count + end + def test_errors_empty_after_errors_on_check t = Topic.new assert t.errors[:id].empty? -- cgit v1.2.3 From 0e0df4b0c5df7fdd1daa5653c255c4737f5526fc Mon Sep 17 00:00:00 2001 From: David Chelimsky Date: Wed, 23 Jun 2010 07:59:56 -0500 Subject: In ActionView::TestCase::Behavior, assign variables right before rendering the view. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Previously, _assigns were locked down the first time _view was referenced. [#4931 state:resolved] Signed-off-by: José Valim --- actionpack/lib/action_view/base.rb | 6 +++++- actionpack/lib/action_view/test_case.rb | 3 ++- actionpack/test/template/test_case_test.rb | 9 +++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index a7ba9f374a..50df1c97b2 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -204,8 +204,12 @@ module ActionView #:nodoc: value.dup : ActionView::PathSet.new(Array.wrap(value)) end + def assign(new_assigns) # :nodoc: + self.assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) } + end + def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc: - self.assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } + assign(assigns_for_first_render) self.helpers = self.class.helpers || Module.new if @_controller = controller diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 00c7386d23..5abc59507f 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -99,6 +99,7 @@ module ActionView end def render(options = {}, local_assigns = {}, &block) + _view.assign(_assigns) @rendered << output = _view.render(options, local_assigns, &block) output end @@ -147,7 +148,7 @@ module ActionView def _view @_view ||= begin - view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller) + view = ActionView::Base.new(ActionController::Base.view_paths, {}, @controller) view.singleton_class.send :include, _helpers view.singleton_class.send :include, @controller._router.url_helpers view.singleton_class.send :delegate, :alert, :notice, :to => "request.flash" diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index d86dc7b185..4773eae039 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -200,6 +200,15 @@ module ActionView assert_match /Hello: EloyHello: Manfred/, render(:file => 'test/list') end + test "is able to render partials from templates and also use instance variables after _view has been referenced" do + @controller.controller_path = "test" + + _view + + @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] + assert_match /Hello: EloyHello: Manfred/, render(:file => 'test/list') + end + end class AssertionsTest < ActionView::TestCase -- cgit v1.2.3 From 32b8be95331928990a61a43023a343a0583b53c7 Mon Sep 17 00:00:00 2001 From: David Chelimsky Date: Wed, 23 Jun 2010 10:19:13 -0500 Subject: Expose view via the view() method in AV::TestCase::Behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - was exposed as _view, which suggested it was private - left _view as an alias of view as not to break any extensions that are relying on _view [#4932 state:resolved] Signed-off-by: José Valim --- actionpack/lib/action_view/test_case.rb | 13 ++++++++----- actionpack/test/template/test_case_test.rb | 28 ++++++++++++++++------------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 5abc59507f..757e4cf77c 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -99,8 +99,8 @@ module ActionView end def render(options = {}, local_assigns = {}, &block) - _view.assign(_assigns) - @rendered << output = _view.render(options, local_assigns, &block) + view.assign(_assigns) + @rendered << output = view.render(options, local_assigns, &block) output end @@ -146,8 +146,9 @@ module ActionView end end - def _view - @_view ||= begin + # The instance of ActionView::Base that is used by +render+. + def view + @view ||= begin view = ActionView::Base.new(ActionController::Base.view_paths, {}, @controller) view.singleton_class.send :include, _helpers view.singleton_class.send :include, @controller._router.url_helpers @@ -159,10 +160,11 @@ module ActionView end end + alias_method :_view, :view + EXCLUDE_IVARS = %w{ @_assertion_wrapped @_result - @_view @controller @layouts @locals @@ -174,6 +176,7 @@ module ActionView @routes @templates @test_passed + @view @view_context_class } diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index 4773eae039..a747f46527 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -37,8 +37,12 @@ module ActionView include SharedTests test_case = self - test "memoizes the _view" do - assert_same _view, _view + test "memoizes the view" do + assert_same view, view + end + + test "exposes view as _view for backwards compatibility" do + assert_same _view, view end test "works without testing a helper module" do @@ -61,13 +65,13 @@ module ActionView end test "delegates notice to request.flash" do - _view.request.flash.expects(:notice).with("this message") - _view.notice("this message") + view.request.flash.expects(:notice).with("this message") + view.notice("this message") end test "delegates alert to request.flash" do - _view.request.flash.expects(:alert).with("this message") - _view.alert("this message") + view.request.flash.expects(:alert).with("this message") + view.alert("this message") end end @@ -136,7 +140,7 @@ module ActionView helper HelperThatInvokesProtectAgainstForgery test "protect_from_forgery? in any helpers returns false" do - assert !_view.help_me + assert !view.help_me end end @@ -200,10 +204,10 @@ module ActionView assert_match /Hello: EloyHello: Manfred/, render(:file => 'test/list') end - test "is able to render partials from templates and also use instance variables after _view has been referenced" do + test "is able to render partials from templates and also use instance variables after view has been referenced" do @controller.controller_path = "test" - _view + view @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] assert_match /Hello: EloyHello: Manfred/, render(:file => 'test/list') @@ -229,19 +233,19 @@ module ActionView end class RenderTemplateTest < ActionView::TestCase - test "render template supports specifying partials" do + test "supports specifying partials" do controller.controller_path = "test" render(:template => "test/calling_partial_with_layout") assert_template :partial => "_partial_for_use_in_layout" end - test "render template supports specifying locals (passing)" do + test "supports specifying locals (passing)" do controller.controller_path = "test" render(:template => "test/calling_partial_with_layout") assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "David" } end - test "render template supports specifying locals (failing)" do + test "supports specifying locals (failing)" do controller.controller_path = "test" render(:template => "test/calling_partial_with_layout") assert_raise Test::Unit::AssertionFailedError, /Somebody else.*David/m do -- cgit v1.2.3 From a3ce6fcc567836c99bb93cbe880b2c333a511593 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 23 Jun 2010 07:38:51 -0400 Subject: adding column named 'group' to ensure that nothing breaks given that 'group' is sql reserved word MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4945 state:resolved] Signed-off-by: José Valim --- activerecord/test/cases/base_test.rb | 2 +- activerecord/test/cases/reflection_test.rb | 10 +++++----- activerecord/test/schema/schema.rb | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index c24a494c85..f8b90d8877 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -2243,7 +2243,7 @@ class BasicsTest < ActiveRecord::TestCase def test_inspect_instance topic = topics(:first) - assert_equal %(#), topic.inspect + assert_equal %(#), topic.inspect end def test_inspect_new_instance diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 67818622d7..03c4fc4e80 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -24,25 +24,25 @@ class ReflectionTest < ActiveRecord::TestCase def test_read_attribute_names assert_equal( - %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id parent_title type ).sort, + %w( id title author_name author_email_address bonus_time written_on last_read content group approved replies_count parent_id parent_title type ).sort, @first.attribute_names ) end def test_columns - assert_equal 13, Topic.columns.length + assert_equal 14, Topic.columns.length end def test_columns_are_returned_in_the_order_they_were_declared column_names = Topic.columns.map { |column| column.name } - assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type), column_names + assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type group), column_names end def test_content_columns content_columns = Topic.content_columns content_column_names = content_columns.map {|column| column.name} - assert_equal 9, content_columns.length - assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved parent_title).sort, content_column_names.sort + assert_equal 10, content_columns.length + assert_equal %w(title author_name author_email_address written_on bonus_time last_read content group approved parent_title).sort, content_column_names.sort end def test_column_string_type_and_limit diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index f5fba2f87d..b212e7cff2 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -485,6 +485,7 @@ ActiveRecord::Schema.define do t.integer :parent_id t.string :parent_title t.string :type + t.string :group end create_table :taggings, :force => true do |t| -- cgit v1.2.3 From eb04408a20628a49296e0859425940b39a83ec63 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 23 Jun 2010 10:05:30 -0400 Subject: ActiveRecord's relation object should respond to to_json and to_yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4547 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/relation.rb | 2 +- activerecord/test/cases/relations_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 66970a5ea1..a342b1fe6f 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -10,7 +10,7 @@ module ActiveRecord include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches - delegate :length, :collect, :map, :each, :all?, :include?, :to => :to_a + delegate :to_json, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a delegate :insert, :to => :arel attr_reader :table, :klass diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 165b2e1e6c..f2216a1923 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -29,6 +29,16 @@ class RelationTest < ActiveRecord::TestCase assert_equal 4, topics.size end + def test_to_json + assert_nothing_raised { Bird.scoped.to_json } + assert_nothing_raised { Bird.scoped.all.to_json } + end + + def test_to_yaml + assert_nothing_raised { Bird.scoped.to_yaml } + assert_nothing_raised { Bird.scoped.all.to_yaml } + end + def test_scoped_all topics = Topic.scoped.all assert_kind_of Array, topics -- cgit v1.2.3 From 0bf3baa6b3d216c6340f8d3b5d0a3ebc093e969a Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 23 Jun 2010 11:54:38 -0400 Subject: adding fix for to_xml for ActiveRecord relation object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation.rb | 2 +- activerecord/test/cases/relations_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a342b1fe6f..a51b317dc3 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -10,7 +10,7 @@ module ActiveRecord include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches - delegate :to_json, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a + delegate :to_xml, :to_json, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a delegate :insert, :to => :arel attr_reader :table, :klass diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index f2216a1923..4316aa98d4 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -39,6 +39,11 @@ class RelationTest < ActiveRecord::TestCase assert_nothing_raised { Bird.scoped.all.to_yaml } end + def test_to_xml + assert_nothing_raised { Bird.scoped.to_xml } + assert_nothing_raised { Bird.scoped.all.to_xml } + end + def test_scoped_all topics = Topic.scoped.all assert_kind_of Array, topics -- cgit v1.2.3 From 49f52c3d910c8f183afc3a54ea2ae9667f23085e Mon Sep 17 00:00:00 2001 From: Michael Lovitt Date: Tue, 22 Jun 2010 09:55:50 -0400 Subject: Sessions should not be created until written to and session data should be destroyed on reset. [#4938] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/test_case.rb | 2 + actionpack/lib/action_dispatch/http/request.rb | 2 +- .../middleware/session/abstract_store.rb | 96 +++++++++++++++++++--- .../middleware/session/cookie_store.rb | 39 +++++---- .../middleware/session/mem_cache_store.rb | 9 ++ .../test/activerecord/active_record_store_test.rb | 35 +++++++- .../test/dispatch/session/cookie_store_test.rb | 11 ++- .../test/dispatch/session/mem_cache_store_test.rb | 31 ++++++- activerecord/lib/active_record/session_store.rb | 8 ++ 9 files changed, 198 insertions(+), 35 deletions(-) diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 26a385011f..650eb16ac0 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -193,6 +193,8 @@ module ActionController replace(session.stringify_keys) @loaded = true end + + def exists?; true; end end # Superclass for ActionController functional tests. Functional tests allow you to diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 98f4f5ae18..6b611823d0 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -195,7 +195,7 @@ module ActionDispatch end def reset_session - self.session_options.delete(:id) + session.destroy if session self.session = {} end diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 3e8d64b0c6..7623a94234 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -12,6 +12,37 @@ module ActionDispatch ENV_SESSION_KEY = 'rack.session'.freeze ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze + # thin wrapper around Hash that allows us to lazily + # load session id into session_options + class OptionsHash < Hash + def initialize(by, env, default_options) + @by = by + @env = env + merge!(default_options) + @session_id_loaded = false + end + + alias_method :get_without_session_load, :[] + + def [](key) + if key == :id + load_session_id! unless has_session_id? + end + super(key) + end + + private + + def has_session_id? + get_without_session_load(:id).present? || @session_id_loaded + end + + def load_session_id! + self[:id] = @by.send(:extract_session_id, @env) + @session_id_loaded = true + end + end + class SessionHash < Hash def initialize(by, env) super() @@ -21,45 +52,71 @@ module ActionDispatch end def [](key) - load! unless @loaded + load_for_read! + super(key.to_s) + end + + def has_key?(key) + load_for_read! super(key.to_s) end def []=(key, value) - load! unless @loaded + load_for_write! super(key.to_s, value) end def to_hash + load_for_read! h = {}.replace(self) h.delete_if { |k,v| v.nil? } h end def update(hash) - load! unless @loaded + load_for_write! super(hash.stringify_keys) end def delete(key) - load! unless @loaded + load_for_write! super(key.to_s) end def inspect - load! unless @loaded + load_for_read! super end + def exists? + @by.send(:exists?, @env) + end + def loaded? @loaded end + def destroy + clear + @by.send(:destroy, @env) if @by + @env[ENV_SESSION_OPTIONS_KEY].delete(:id) if @env && @env[ENV_SESSION_OPTIONS_KEY] + @loaded = false + end + private + + def load_for_read! + load! if !loaded? && exists? + end + + def load_for_write! + load! unless loaded? + end + def load! stale_session_check! do id, session = @by.send(:load_session, @env) - (@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id + @env[ENV_SESSION_OPTIONS_KEY][:id] = id replace(session.stringify_keys) @loaded = true end @@ -75,7 +132,6 @@ module ActionDispatch rescue LoadError, NameError => const_error raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n" end - retry else raise @@ -133,7 +189,7 @@ module ActionDispatch def prepare!(env) env[ENV_SESSION_KEY] = SessionHash.new(self, env) - env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup + env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options.dup) end def generate_sid @@ -145,13 +201,22 @@ module ActionDispatch end def load_session(env) - request = Rack::Request.new(env) - sid = request.cookies[@key] - sid ||= request.params[@key] unless @cookie_only + sid = current_session_id(env) sid, session = get_session(env, sid) [sid, session] end + def extract_session_id(env) + request = Rack::Request.new(env) + sid = request.cookies[@key] + sid ||= request.params[@key] unless @cookie_only + sid + end + + def current_session_id(env) + env[ENV_SESSION_OPTIONS_KEY][:id] + end + def ensure_session_key! if @key.blank? raise ArgumentError, 'A key is required to write a ' + @@ -161,6 +226,10 @@ module ActionDispatch end end + def exists?(env) + current_session_id(env).present? + end + def get_session(env, sid) raise '#get_session needs to be implemented.' end @@ -169,6 +238,11 @@ module ActionDispatch raise '#set_session needs to be implemented and should return ' << 'the value to be stored in the cookie (usually the sid)' end + + def destroy(env) + raise '#destroy needs to be implemented.' + end + end end end diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 92a86ee229..7c5626735b 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -39,16 +39,6 @@ module ActionDispatch # # Note that changing digest or secret invalidates all existing sessions! class CookieStore < AbstractStore - class OptionsHash < Hash - def initialize(by, env, default_options) - @session_data = env[AbstractStore::ENV_SESSION_KEY] - merge!(default_options) - end - - def [](key) - key == :id ? @session_data[:session_id] : super(key) - end - end def initialize(app, options = {}) super(app, options.merge!(:cookie_only => true)) @@ -57,19 +47,28 @@ module ActionDispatch private - def prepare!(env) - env[ENV_SESSION_KEY] = SessionHash.new(self, env) - env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options) - end - def load_session(env) - request = ActionDispatch::Request.new(env) - data = request.cookie_jar.signed[@key] + data = unpacked_cookie_data(env) data = persistent_session_id!(data) - data.stringify_keys! [data["session_id"], data] end + def extract_session_id(env) + if data = unpacked_cookie_data(env) + data["session_id"] + else + nil + end + end + + def unpacked_cookie_data(env) + request = ActionDispatch::Request.new(env) + if data = request.cookie_jar.signed[@key] + data.stringify_keys! + end + data + end + def set_cookie(request, options) request.cookie_jar.signed[@key] = options end @@ -78,6 +77,10 @@ module ActionDispatch persistent_session_id!(session_data, sid) end + def destroy(env) + # session data is stored on client; nothing to do here + end + def persistent_session_id!(data, sid=nil) data ||= {} data["session_id"] ||= sid || generate_sid diff --git a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb index 8df8f977e8..28e3dbd732 100644 --- a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb @@ -42,6 +42,15 @@ module ActionDispatch rescue MemCache::MemCacheError, Errno::ECONNREFUSED false end + + def destroy(env) + if sid = current_session_id(env) + @pool.delete(sid) + end + rescue MemCache::MemCacheError, Errno::ECONNREFUSED + false + end + end end end diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index 6d4b8e1e40..736829dbf7 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -17,7 +17,6 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end def get_session_id - session[:foo] render :text => "#{request.session_options[:id]}" end @@ -58,6 +57,10 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest get '/get_session_value' assert_response :success assert_equal 'foo: "baz"', response.body + + get '/call_reset_session' + assert_response :success + assert_not_equal [], headers['Set-Cookie'] end end end @@ -92,6 +95,34 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end end + def test_getting_session_value_after_session_reset + with_test_route_set do + get '/set_session_value' + assert_response :success + assert cookies['_session_id'] + session_cookie = cookies.send(:hash_for)['_session_id'] + + get '/call_reset_session' + assert_response :success + assert_not_equal [], headers['Set-Cookie'] + + cookies << session_cookie # replace our new session_id with our old, pre-reset session_id + + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from the database" + end + end + + def test_getting_from_nonexistent_session + with_test_route_set do + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body + assert_nil cookies['_session_id'], "should only create session on write, not read" + end + end + def test_getting_session_id with_test_route_set do get '/set_session_value' @@ -101,7 +132,7 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest get '/get_session_id' assert_response :success - assert_equal session_id, response.body + assert_equal session_id, response.body, "should be able to read session id without accessing the session hash" end end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index b4380f7818..787524ab7b 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -83,7 +83,7 @@ class CookieStoreTest < ActionController::IntegrationTest get '/get_session_id' assert_response :success - assert_equal "id: #{session_id}", response.body + assert_equal "id: #{session_id}", response.body, "should be able to read session id without accessing the session hash" end end @@ -141,6 +141,15 @@ class CookieStoreTest < ActionController::IntegrationTest end end + def test_getting_from_nonexistent_session + with_test_route_set do + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body + assert_nil headers['Set-Cookie'], "should only create session on write, not read" + end + end + def test_persistent_session_id with_test_route_set do cookies[SessionKey] = SignedBar diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index 8858a398e0..08f8069888 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -17,7 +17,6 @@ class MemCacheStoreTest < ActionController::IntegrationTest end def get_session_id - session[:foo] render :text => "#{request.session_options[:id]}" end @@ -56,6 +55,34 @@ class MemCacheStoreTest < ActionController::IntegrationTest end end + def test_getting_session_value_after_session_reset + with_test_route_set do + get '/set_session_value' + assert_response :success + assert cookies['_session_id'] + session_cookie = cookies.send(:hash_for)['_session_id'] + + get '/call_reset_session' + assert_response :success + assert_not_equal [], headers['Set-Cookie'] + + cookies << session_cookie # replace our new session_id with our old, pre-reset session_id + + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from memcached" + end + end + + def test_getting_from_nonexistent_session + with_test_route_set do + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body + assert_nil cookies['_session_id'], "should only create session on write, not read" + end + end + def test_setting_session_value_after_session_reset with_test_route_set do get '/set_session_value' @@ -86,7 +113,7 @@ class MemCacheStoreTest < ActionController::IntegrationTest get '/get_session_id' assert_response :success - assert_equal session_id, response.body + assert_equal session_id, response.body, "should be able to read session id without accessing the session hash" end end diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index f712a2c94f..b88d550086 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -318,6 +318,14 @@ module ActiveRecord sid end + def destroy(env) + if sid = current_session_id(env) + Base.silence do + get_session_model(env, sid).destroy + end + end + end + def get_session_model(env, sid) if env[ENV_SESSION_OPTIONS_KEY][:id].nil? env[SESSION_RECORD_KEY] = find_session(sid) -- cgit v1.2.3 From 4a0a640d33e1c729d38c6091bb1394fbda059b5c Mon Sep 17 00:00:00 2001 From: Paul Gillard Date: Wed, 23 Jun 2010 21:36:02 +0100 Subject: Remove incorrect comment regarding #initialize_copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comment added in #3164 is incorrect after reading http://dev.rubyonrails.org/ticket/7191. Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3a7db97a6a..7a262ad465 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1413,14 +1413,6 @@ module ActiveRecord #:nodoc: # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is # application specific and is therefore left to the application to implement according to its need. def initialize_copy(other) - # Think the assertion which fails if the after_initialize callback goes at the end of the method is wrong. The - # deleted clone method called new which therefore called the after_initialize callback. It then went on to copy - # over the attributes. But if it's copying the attributes afterwards then it hasn't finished initializing right? - # For example in the test suite the topic model's after_initialize method sets the author_email_address to - # test@test.com. I would have thought this would mean that all cloned models would have an author email address - # of test@test.com. However the test_clone test method seems to test that this is not the case. As a result the - # after_initialize callback has to be run *before* the copying of the attributes rather than afterwards in order - # for all tests to pass. This makes no sense to me. callback(:after_initialize) if respond_to_without_attributes?(:after_initialize) cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) -- cgit v1.2.3 From 6f83a5036d8a9c3f8ed74755ff6d42bc3f6e9982 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 22 Jun 2010 23:17:20 +0200 Subject: renames load_(once_)paths to autoload_(once_)paths in dependencies and config --- activesupport/CHANGELOG | 2 ++ activesupport/lib/active_support/dependencies.rb | 24 +++++++-------- activesupport/test/dependencies_test.rb | 36 +++++++++++----------- railties/CHANGELOG | 3 ++ railties/lib/rails/application.rb | 4 +-- railties/lib/rails/application/finisher.rb | 10 +++--- railties/lib/rails/engine.rb | 16 +++++----- railties/lib/rails/engine/configuration.rb | 10 +++--- .../rails/app/templates/config/application.rb | 2 +- railties/test/generators/actions_test.rb | 6 ++-- 10 files changed, 59 insertions(+), 54 deletions(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index abd0664118..59ad57ede8 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* +* Renamed ActiveSupport::Dependecies.load_(once_)paths to autoload_(once_)paths. [fxn] + * Added ActiveSupport::FileUpdateChecker to execute a block only if a set of files changed, used by Router and I18n locale files. [José Valim] * Added ActiveSupport::DescendantsTracker to track descendants with support to constants reloading. [José Valim] diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index e8210dfe37..f7b92cf896 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -33,14 +33,14 @@ module ActiveSupport #:nodoc: # The set of directories from which we may automatically load files. Files # under these directories will be reloaded on each request in development mode, - # unless the directory also appears in load_once_paths. - mattr_accessor :load_paths - self.load_paths = [] + # unless the directory also appears in autoload_once_paths. + mattr_accessor :autoload_paths + self.autoload_paths = [] # The set of directories from which automatically loaded constants are loaded - # only once. All directories in this set must also be present in +load_paths+. - mattr_accessor :load_once_paths - self.load_once_paths = [] + # only once. All directories in this set must also be present in +autoload_paths+. + mattr_accessor :autoload_once_paths + self.autoload_once_paths = [] # An array of qualified constant names that have been loaded. Adding a name to # this array will cause it to be unloaded the next time Dependencies are cleared. @@ -352,7 +352,7 @@ module ActiveSupport #:nodoc: # Given +path+, a filesystem path to a ruby file, return an array of constant # paths which would cause Dependencies to attempt to load this file. - def loadable_constants_for_path(path, bases = load_paths) + def loadable_constants_for_path(path, bases = autoload_paths) expanded_path = Pathname.new(path[/\A(.*?)(\.rb)?\Z/, 1]).expand_path bases.inject([]) do |paths, root| @@ -363,11 +363,11 @@ module ActiveSupport #:nodoc: end.uniq end - # Search for a file in load_paths matching the provided suffix. + # Search for a file in autoload_paths matching the provided suffix. def search_for_file(path_suffix) path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb") - load_paths.each do |root| + autoload_paths.each do |root| path = File.join(root, path_suffix) return path if File.file? path end @@ -377,14 +377,14 @@ module ActiveSupport #:nodoc: # Does the provided path_suffix correspond to an autoloadable module? # Instead of returning a boolean, the autoload base for this module is returned. def autoloadable_module?(path_suffix) - load_paths.each do |load_path| + autoload_paths.each do |load_path| return load_path if File.directory? File.join(load_path, path_suffix) end nil end def load_once_path?(path) - load_once_paths.any? { |base| path.starts_with? base } + autoload_once_paths.any? { |base| path.starts_with? base } end # Attempt to autoload the provided module name by searching for a directory @@ -396,7 +396,7 @@ module ActiveSupport #:nodoc: return nil unless base_path = autoloadable_module?(path_suffix) mod = Module.new into.const_set const_name, mod - autoloaded_constants << qualified_name unless load_once_paths.include?(base_path) + autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path) return mod end diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 5422c75f5f..c7088638c7 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -25,11 +25,11 @@ class DependenciesTest < Test::Unit::TestCase this_dir = File.dirname(__FILE__) parent_dir = File.dirname(this_dir) $LOAD_PATH.unshift(parent_dir) unless $LOAD_PATH.include?(parent_dir) - prior_load_paths = ActiveSupport::Dependencies.load_paths - ActiveSupport::Dependencies.load_paths = from.collect { |f| "#{this_dir}/#{f}" } + prior_autoload_paths = ActiveSupport::Dependencies.autoload_paths + ActiveSupport::Dependencies.autoload_paths = from.collect { |f| "#{this_dir}/#{f}" } yield ensure - ActiveSupport::Dependencies.load_paths = prior_load_paths + ActiveSupport::Dependencies.autoload_paths = prior_autoload_paths ActiveSupport::Dependencies.mechanism = old_mechanism ActiveSupport::Dependencies.explicitly_unloadable_constants = [] end @@ -264,7 +264,7 @@ class DependenciesTest < Test::Unit::TestCase def test_loadable_constants_for_path_should_provide_all_results fake_root = '/usr/apps/backpack' with_loading fake_root, fake_root + '/lib' do - root = ActiveSupport::Dependencies.load_paths.first + root = ActiveSupport::Dependencies.autoload_paths.first assert_equal ["Lib::A::B", "A::B"], ActiveSupport::Dependencies.loadable_constants_for_path(root + '/lib/a/b') end end @@ -272,7 +272,7 @@ class DependenciesTest < Test::Unit::TestCase def test_loadable_constants_for_path_should_uniq_results fake_root = '/usr/apps/backpack/lib' with_loading fake_root, fake_root + '/' do - root = ActiveSupport::Dependencies.load_paths.first + root = ActiveSupport::Dependencies.autoload_paths.first assert_equal ["A::B"], ActiveSupport::Dependencies.loadable_constants_for_path(root + '/a/b') end end @@ -344,7 +344,7 @@ class DependenciesTest < Test::Unit::TestCase def test_file_search with_loading 'dependencies' do - root = ActiveSupport::Dependencies.load_paths.first + root = ActiveSupport::Dependencies.autoload_paths.first assert_equal nil, ActiveSupport::Dependencies.search_for_file('service_three') assert_equal nil, ActiveSupport::Dependencies.search_for_file('service_three.rb') assert_equal root + '/service_one.rb', ActiveSupport::Dependencies.search_for_file('service_one') @@ -354,14 +354,14 @@ class DependenciesTest < Test::Unit::TestCase def test_file_search_uses_first_in_load_path with_loading 'dependencies', 'autoloading_fixtures' do - deps, autoload = ActiveSupport::Dependencies.load_paths + deps, autoload = ActiveSupport::Dependencies.autoload_paths assert_match %r/dependencies/, deps assert_match %r/autoloading_fixtures/, autoload assert_equal deps + '/conflict.rb', ActiveSupport::Dependencies.search_for_file('conflict') end with_loading 'autoloading_fixtures', 'dependencies' do - autoload, deps = ActiveSupport::Dependencies.load_paths + autoload, deps = ActiveSupport::Dependencies.autoload_paths assert_match %r/dependencies/, deps assert_match %r/autoloading_fixtures/, autoload @@ -418,7 +418,7 @@ class DependenciesTest < Test::Unit::TestCase def test_removal_from_tree_should_be_detected with_loading 'dependencies' do - root = ActiveSupport::Dependencies.load_paths.first + root = ActiveSupport::Dependencies.autoload_paths.first c = ServiceOne ActiveSupport::Dependencies.clear assert ! defined?(ServiceOne) @@ -433,7 +433,7 @@ class DependenciesTest < Test::Unit::TestCase def test_references_should_work with_loading 'dependencies' do - root = ActiveSupport::Dependencies.load_paths.first + root = ActiveSupport::Dependencies.autoload_paths.first c = ActiveSupport::Dependencies.ref("ServiceOne") service_one_first = ServiceOne assert_equal service_one_first, c.get @@ -460,9 +460,9 @@ class DependenciesTest < Test::Unit::TestCase end end - def test_load_once_paths_do_not_add_to_autoloaded_constants + def test_autoload_once_paths_do_not_add_to_autoloaded_constants with_autoloading_fixtures do - ActiveSupport::Dependencies.load_once_paths = ActiveSupport::Dependencies.load_paths.dup + ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths.dup assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder") assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") @@ -473,7 +473,7 @@ class DependenciesTest < Test::Unit::TestCase end ensure Object.class_eval { remove_const :ModuleFolder } - ActiveSupport::Dependencies.load_once_paths = [] + ActiveSupport::Dependencies.autoload_once_paths = [] end def test_application_should_special_case_application_controller @@ -760,20 +760,20 @@ class DependenciesTest < Test::Unit::TestCase def test_load_once_constants_should_not_be_unloaded with_autoloading_fixtures do - ActiveSupport::Dependencies.load_once_paths = ActiveSupport::Dependencies.load_paths + ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths ::A.to_s assert defined?(A) ActiveSupport::Dependencies.clear assert defined?(A) end ensure - ActiveSupport::Dependencies.load_once_paths = [] + ActiveSupport::Dependencies.autoload_once_paths = [] Object.class_eval { remove_const :A if const_defined?(:A) } end - def test_load_once_paths_should_behave_when_recursively_loading + def test_autoload_once_paths_should_behave_when_recursively_loading with_loading 'dependencies', 'autoloading_fixtures' do - ActiveSupport::Dependencies.load_once_paths = [ActiveSupport::Dependencies.load_paths.last] + ActiveSupport::Dependencies.autoload_once_paths = [ActiveSupport::Dependencies.autoload_paths.last] assert !defined?(CrossSiteDependency) assert_nothing_raised { CrossSiteDepender.nil? } assert defined?(CrossSiteDependency) @@ -784,7 +784,7 @@ class DependenciesTest < Test::Unit::TestCase "CrossSiteDependency shouldn't have been unloaded!" end ensure - ActiveSupport::Dependencies.load_once_paths = [] + ActiveSupport::Dependencies.autoload_once_paths = [] end def test_hook_called_multiple_times diff --git a/railties/CHANGELOG b/railties/CHANGELOG index c33a4d11dd..61d84e104a 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,6 +1,9 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* +* config.load_(once_)paths in config/application.rb got renamed to config.autoload_(once_)paths. [fxn] + * Abort generation/booting on Ruby 1.9.1. [fxn] + * Made the rails command work even when you're in a subdirectory [Chad Fowler] diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index eca6802297..aabe86715c 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -67,7 +67,7 @@ module Rails raise "You cannot have more than one Rails::Application" if Rails.application super Rails.application = base.instance - Rails.application.add_lib_to_load_paths! + Rails.application.add_lib_to_load_path! ActiveSupport.run_load_hooks(:before_configuration, base.instance) end @@ -97,7 +97,7 @@ module Rails # are changing config.root inside your application definition or having a custom # Rails application, you will need to add lib to $LOAD_PATH on your own in case # you need to load files in lib/ during the application configuration as well. - def add_lib_to_load_paths! #:nodoc: + def add_lib_to_load_path! #:nodoc: path = config.root.join('lib').to_s $LOAD_PATH.unshift(path) if File.exists?(path) end diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index 11a3329de6..855467227b 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -7,14 +7,14 @@ module Rails config.generators.templates.unshift(*paths.lib.templates.to_a) end - initializer :ensure_load_once_paths_as_subset do - extra = ActiveSupport::Dependencies.load_once_paths - - ActiveSupport::Dependencies.load_paths + initializer :ensure_autoload_once_paths_as_subset do + extra = ActiveSupport::Dependencies.autoload_once_paths - + ActiveSupport::Dependencies.autoload_paths unless extra.empty? abort <<-end_error - load_once_paths must be a subset of the load_paths. - Extra items in load_once_paths: #{extra * ','} + autoload_once_paths must be a subset of the autoload_paths. + Extra items in autoload_once_paths: #{extra * ','} end_error end end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index cdb00a4eff..0a3f21fb1b 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -32,14 +32,14 @@ module Rails # == Configuration # # Besides the Railtie configuration which is shared across the application, in a - # Rails::Engine you can access load_paths, eager_load_paths and load_once_paths, + # Rails::Engine you can access autoload_paths, eager_load_paths and autoload_once_paths, # which differently from a Railtie, are scoped to the current Engine. # # Example: # # class MyEngine < Rails::Engine # # Add a load path for this specific Engine - # config.load_paths << File.expand_path("../lib/some/path", __FILE__) + # config.autoload_paths << File.expand_path("../lib/some/path", __FILE__) # # initializer "my_engine.add_middleware" do |app| # app.middleware.use MyEngine::Middleware @@ -142,7 +142,7 @@ module Rails # Add configured load paths to ruby load paths and remove duplicates. initializer :set_load_path, :before => :bootstrap_hook do - config.load_paths.reverse_each do |path| + config.autoload_paths.reverse_each do |path| $LOAD_PATH.unshift(path) if File.directory?(path) end $LOAD_PATH.uniq! @@ -154,17 +154,17 @@ module Rails # This needs to be an initializer, since it needs to run once # per engine and get the engine as a block parameter initializer :set_autoload_paths, :before => :bootstrap_hook do |app| - ActiveSupport::Dependencies.load_paths.unshift(*config.load_paths) + ActiveSupport::Dependencies.autoload_paths.unshift(*config.autoload_paths) if reloadable?(app) - ActiveSupport::Dependencies.load_once_paths.unshift(*config.load_once_paths) + ActiveSupport::Dependencies.autoload_once_paths.unshift(*config.autoload_once_paths) else - ActiveSupport::Dependencies.load_once_paths.unshift(*config.load_paths) + ActiveSupport::Dependencies.autoload_once_paths.unshift(*config.autoload_paths) end # Freeze so future modifications will fail rather than do nothing mysteriously - config.load_paths.freeze - config.load_once_paths.freeze + config.autoload_paths.freeze + config.autoload_once_paths.freeze end initializer :add_routing_paths do |app| diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index 446fe0bda9..4e27180f1e 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -4,7 +4,7 @@ module Rails class Engine class Configuration < ::Rails::Railtie::Configuration attr_reader :root - attr_writer :eager_load_paths, :load_once_paths, :load_paths + attr_writer :eager_load_paths, :autoload_once_paths, :autoload_paths def initialize(root=nil) super() @@ -41,12 +41,12 @@ module Rails @eager_load_paths ||= paths.eager_load end - def load_once_paths - @load_once_paths ||= paths.load_once + def autoload_once_paths + @autoload_once_paths ||= paths.load_once end - def load_paths - @load_paths ||= paths.load_paths + def autoload_paths + @autoload_paths ||= paths.load_paths end end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 0066e2b0c2..031466cb86 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -22,7 +22,7 @@ module <%= app_const_base %> # -- all .rb files in that directory are automatically loaded. # Add additional load paths for your own custom dirs - # config.load_paths += %W( #{config.root}/extras ) + # config.autoload_paths += %W( #{config.root}/extras ) # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 65fbf61902..0472ca73a8 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -130,9 +130,9 @@ class ActionsTest < Rails::Generators::TestCase def test_environment_should_include_data_in_environment_initializer_block run_generator - load_paths = 'config.load_paths += %w["#{Rails.root}/app/extras"]' - action :environment, load_paths - assert_file 'config/application.rb', /#{Regexp.escape(load_paths)}/ + autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]' + action :environment, autoload_paths + assert_file 'config/application.rb', /#{Regexp.escape(autoload_paths)}/ end def test_environment_with_block_should_include_block_contents_in_environment_initializer_block -- cgit v1.2.3 From 69abbe893413c99808c8cebd2f1d468c7921c573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 24 Jun 2010 01:04:41 +0200 Subject: Avoid using Pathname on Resolver and AS::Dependencies. --- actionpack/lib/action_view/template/resolver.rb | 2 +- activesupport/lib/active_support/dependencies.rb | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 2cf7e955ab..c9e20ca14e 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -99,7 +99,7 @@ module ActionView def initialize(path) raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) super() - @path = Pathname.new(path).expand_path + @path = File.expand_path(path) end def eql?(resolver) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index f7b92cf896..7d5143ba37 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -353,14 +353,23 @@ module ActiveSupport #:nodoc: # Given +path+, a filesystem path to a ruby file, return an array of constant # paths which would cause Dependencies to attempt to load this file. def loadable_constants_for_path(path, bases = autoload_paths) - expanded_path = Pathname.new(path[/\A(.*?)(\.rb)?\Z/, 1]).expand_path + path = $1 if path =~ /\A(.*)\.rb\Z/ + expanded_path = File.expand_path(path) + paths = [] + + bases.each do |root| + expanded_root = File.expand_path(root) + next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path + + nesting = expanded_path[(expanded_root.size)..-1] + nesting = nesting[1..-1] if nesting && nesting[0] == ?/ + next if nesting.blank? - bases.inject([]) do |paths, root| - expanded_root = Pathname.new(root).expand_path - nesting = expanded_path.relative_path_from(expanded_root).to_s - next paths if nesting =~ /\.\./ paths << nesting.camelize - end.uniq + end + + paths.uniq! + paths end # Search for a file in autoload_paths matching the provided suffix. -- cgit v1.2.3 From 7b730a2f1b9be71695c1a01efc1fd05e98757736 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 23 Jun 2010 16:11:07 -0700 Subject: Show GC time and # of runs too --- tools/profile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/profile b/tools/profile index f02f1b5057..c86cedcbf4 100755 --- a/tools/profile +++ b/tools/profile @@ -39,6 +39,7 @@ end GC.start before = GC.allocated_size +before_gctime, before_gcruns = GC.time, GC.collections before_rss = `ps -o rss= -p #{Process.pid}`.to_i before_live_objects = ObjectSpace.live_objects @@ -54,6 +55,7 @@ end elapsed = Benchmark.realtime { require path } results = RubyProf.stop if mode +after_gctime, after_gcruns = GC.time, GC.collections GC.start after_live_objects = ObjectSpace.live_objects after_rss = `ps -o rss= -p #{Process.pid}`.to_i @@ -79,4 +81,4 @@ RequireProfiler.stats.each do |file, depth, sec, bytes, objects| puts "#{' ' * (42 + depth)}#{file}" end end -puts "%10.2f KB %10d obj %8.1f ms %d KB RSS" % [usage, after_live_objects - before_live_objects, elapsed * 1000, after_rss - before_rss] +puts "%10.2f KB %10d obj %8.1f ms %d KB RSS %8.1f ms GC time %d GC runs" % [usage, after_live_objects - before_live_objects, elapsed * 1000, after_rss - before_rss, (after_gctime - before_gctime) / 1000.0, after_gcruns - before_gcruns] -- cgit v1.2.3 From 4883082ff1d3f73666c284e8c4bce4a2a12b36c1 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 24 Jun 2010 01:08:12 -0700 Subject: Support an extra profile printer arg --- tools/profile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/profile b/tools/profile index c86cedcbf4..a6e3b41900 100755 --- a/tools/profile +++ b/tools/profile @@ -63,7 +63,9 @@ after = GC.allocated_size usage = (after - before) / 1024.0 if mode - if RubyProf.const_defined?(:CallStackPrinter) + if printer = ARGV.shift + RubyProf.const_get("#{printer.to_s.classify}Printer").new(results).print($stdout) + elsif RubyProf.const_defined?(:CallStackPrinter) File.open("#{File.basename(path, '.rb')}.#{mode}.html", 'w') do |out| RubyProf::CallStackPrinter.new(results).print(out) end -- cgit v1.2.3 From 0b3dd5718ca4de5773d9e54e6b3b51f7ba83823c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 24 Jun 2010 01:55:58 -0700 Subject: Array#sample now exists, so test for #forty_two instead --- railties/test/application/initializers/frameworks_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 35ea2729d3..604c50ec2d 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -57,7 +57,7 @@ module ApplicationTests Dir.chdir("#{app_path}/app") do require "#{app_path}/config/environment" - assert_raises(NoMethodError) { [1,2,3].sample } + assert_raises(NoMethodError) { [1,2,3].forty_two } end end -- cgit v1.2.3 From 60ab9255f0cb09ee5d2891878f715f301ad7a213 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 24 Jun 2010 01:56:16 -0700 Subject: Don't halt railties tests on failures --- railties/Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/Rakefile b/railties/Rakefile index bf19961b59..ddc872e18b 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -18,11 +18,11 @@ task :test => 'test:isolated' namespace :test do task :isolated do dir = ENV["TEST_DIR"] || "**" - Dir["test/#{dir}/*_test.rb"].all? do |file| + Dir["test/#{dir}/*_test.rb"].each do |file| next true if file.include?("fixtures") ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) system(ruby, '-Itest', "-I#{File.dirname(__FILE__)}/../activesupport/lib", file) - end or raise "Failures" + end end end -- cgit v1.2.3 From cdb8609c64ed7db7ea91cdcd4bcb4b400f7dfe07 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 24 Jun 2010 01:37:58 -0700 Subject: Speed up boot by tsorting as infrequently as possible --- railties/lib/rails/application.rb | 2 +- railties/lib/rails/initializable.rb | 9 ++------- railties/test/railties/engine_test.rb | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index aabe86715c..5813c5757d 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -178,7 +178,7 @@ module Rails railties.all { |r| initializers += r.initializers } initializers += super initializers += Finisher.initializers_for(self) - initializers + Collection.new(initializers) end protected diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 9a82e051e7..78f9eaf393 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -39,11 +39,6 @@ module Rails select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block) end - def initialize(initializers = []) - super(initializers) - replace(tsort) - end - def +(other) Collection.new(to_a + other.to_a) end @@ -51,7 +46,7 @@ module Rails def run_initializers(*args) return if instance_variable_defined?(:@ran) - initializers.each do |initializer| + initializers.tsort.each do |initializer| initializer.run(*args) end @ran = true @@ -94,4 +89,4 @@ module Rails end end end -end \ No newline at end of file +end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index b3f65fd00d..3fe01e543c 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -41,7 +41,7 @@ module RailtiesTest boot_rails - initializers = Rails.application.initializers + initializers = Rails.application.initializers.tsort index = initializers.index { |i| i.name == "dummy_initializer" } selection = initializers[(index-3)..(index)].map(&:name).map(&:to_s) -- cgit v1.2.3 From 9f7874ac419f626a6a5157c757669a8b16770ae4 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 24 Jun 2010 02:13:08 -0700 Subject: Move Collection responsibility from application to initializable --- railties/lib/rails/application.rb | 2 +- railties/lib/rails/initializable.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 5813c5757d..aabe86715c 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -178,7 +178,7 @@ module Rails railties.all { |r| initializers += r.initializers } initializers += super initializers += Finisher.initializers_for(self) - Collection.new(initializers) + initializers end protected diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 78f9eaf393..46b3a02b22 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -58,7 +58,7 @@ module Rails module ClassMethods def initializers - @initializers ||= [] + @initializers ||= Collection.new end def initializers_chain -- cgit v1.2.3 From 5eb3b4d9a78268753d009404810619cf93cf6581 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 24 Jun 2010 02:16:59 -0700 Subject: Fix initializable tests --- railties/lib/rails/initializable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 46b3a02b22..fba3ad2c85 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -82,7 +82,7 @@ module Rails def run_initializers(*args) return if @ran - initializers_chain.each do |initializer| + initializers_chain.tsort.each do |initializer| instance_exec(*args, &initializer.block) end @ran = true -- cgit v1.2.3 From e061a12a156791c35bba092263ad216b1b938502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 24 Jun 2010 11:39:37 +0200 Subject: Remove run_initializers from class methods. --- railties/lib/rails/initializable.rb | 8 -------- railties/test/initializable_test.rb | 39 +++++++++++++------------------------ 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index fba3ad2c85..686a2dc0cb 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -79,14 +79,6 @@ module Rails opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] } initializers << Initializer.new(name, nil, opts, &blk) end - - def run_initializers(*args) - return if @ran - initializers_chain.tsort.each do |initializer| - instance_exec(*args, &initializer.block) - end - @ran = true - end end end end diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb index 74301a5dc5..72c35879c5 100644 --- a/railties/test/initializable_test.rb +++ b/railties/test/initializable_test.rb @@ -5,10 +5,7 @@ module InitializableTests class Foo include Rails::Initializable - - class << self - attr_accessor :foo, :bar - end + attr_accessor :foo, :bar initializer :start do @foo ||= 0 @@ -158,30 +155,22 @@ module InitializableTests include ActiveSupport::Testing::Isolation test "initializers run" do - Foo.run_initializers - assert_equal 1, Foo.foo + foo = Foo.new + foo.run_initializers + assert_equal 1, foo.foo end test "initializers are inherited" do - Bar.run_initializers - assert_equal [1, 1], [Bar.foo, Bar.bar] + bar = Bar.new + bar.run_initializers + assert_equal [1, 1], [bar.foo, bar.bar] end test "initializers only get run once" do - Foo.run_initializers - Foo.run_initializers - assert_equal 1, Foo.foo - end - - test "running initializers on children does not effect the parent" do - Bar.run_initializers - assert_nil Foo.foo - assert_nil Foo.bar - end - - test "initializing with modules" do - Word.run_initializers - assert_equal "bird", $word + foo = Foo.new + foo.run_initializers + foo.run_initializers + assert_equal 1, foo.foo end test "creating initializer without a block raises an error" do @@ -198,19 +187,19 @@ module InitializableTests class BeforeAfter < ActiveSupport::TestCase test "running on parent" do $arr = [] - Parent.run_initializers + Parent.new.run_initializers assert_equal [5, 1, 2], $arr end test "running on child" do $arr = [] - Child.run_initializers + Child.new.run_initializers assert_equal [5, 3, 1, 4, 2], $arr end test "handles dependencies introduced before all initializers are loaded" do $arr = [] - Interdependent::Application.run_initializers + Interdependent::Application.new.run_initializers assert_equal [1, 2, 3, 4], $arr end end -- cgit v1.2.3 From 5441e082f9478ddb3c676c681b09786f1391483c Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 23 Jun 2010 13:10:53 -0300 Subject: Add tests for relation count. Fix other tests that were conceptually wrong. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4882 state:resolved] Signed-off-by: José Valim --- activerecord/test/cases/calculations_test.rb | 2 +- activerecord/test/cases/relations_test.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 329dd7d761..2c9d23c80f 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -272,7 +272,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_count_with_column_and_options_parameter - assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50") + assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50 AND firm_id IS NOT NULL") end def test_count_with_no_parameters_isnt_deprecated diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 4316aa98d4..abf43cea98 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -483,7 +483,7 @@ class RelationTest < ActiveRecord::TestCase posts = Post.scoped assert_equal [0], posts.select('comments_count').where('id is not null').group('id').order('id').count.values.uniq - assert_equal 0, posts.where('id is not null').select('comments_count').count + assert_equal 7, posts.where('id is not null').select('comments_count').count assert_equal 7, posts.select('comments_count').count('id') assert_equal 0, posts.select('comments_count').count @@ -619,4 +619,8 @@ class RelationTest < ActiveRecord::TestCase def test_order_by_relation_attribute assert_equal Post.order(Post.arel_table[:title]).all, Post.order("title").all end + + def test_relations_limit_with_conditions_or_limit + assert_equal Post.limit(2).size, Post.limit(2).all.size + end end -- cgit v1.2.3 From 6788db824ab732b13493a9d702dd8fb89fa153c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 24 Jun 2010 13:23:43 +0200 Subject: Move Rails::LogSubscriber to ActiveSupport::LogSubscriber, allowing frameworks like ActiveRecord and ActiveResource to log outsude Rails::Application [#4816 state:resolved] --- actionmailer/lib/action_mailer/base.rb | 1 + actionmailer/lib/action_mailer/log_subscriber.rb | 22 ++++ actionmailer/lib/action_mailer/railtie.rb | 3 - .../lib/action_mailer/railties/log_subscriber.rb | 22 ---- actionmailer/test/log_subscriber_test.rb | 8 +- actionpack/lib/action_controller/base.rb | 2 + actionpack/lib/action_controller/log_subscriber.rb | 56 ++++++++++ actionpack/lib/action_controller/railtie.rb | 3 - .../action_controller/railties/log_subscriber.rb | 56 ---------- actionpack/lib/action_view/base.rb | 1 + actionpack/lib/action_view/log_subscriber.rb | 27 +++++ actionpack/lib/action_view/railtie.rb | 3 - .../lib/action_view/railties/log_subscriber.rb | 27 ----- .../test/activerecord/controller_runtime_test.rb | 10 +- actionpack/test/controller/log_subscriber_test.rb | 10 +- actionpack/test/template/log_subscriber_test.rb | 10 +- activerecord/lib/active_record/base.rb | 1 + activerecord/lib/active_record/log_subscriber.rb | 32 ++++++ activerecord/lib/active_record/railtie.rb | 3 - .../lib/active_record/railties/log_subscriber.rb | 32 ------ activerecord/test/cases/log_subscriber_test.rb | 9 +- activeresource/lib/active_resource/base.rb | 1 + .../lib/active_resource/log_subscriber.rb | 15 +++ activeresource/lib/active_resource/railtie.rb | 3 - .../lib/active_resource/railties/log_subscriber.rb | 15 --- activeresource/test/cases/log_subscriber_test.rb | 9 +- activesupport/lib/active_support.rb | 3 +- activesupport/lib/active_support/log_subscriber.rb | 114 +++++++++++++++++++ .../active_support/log_subscriber/test_helper.rb | 90 +++++++++++++++ activesupport/test/log_subscriber_test.rb | 123 +++++++++++++++++++++ railties/lib/rails.rb | 1 - railties/lib/rails/application/configuration.rb | 2 +- railties/lib/rails/log_subscriber.rb | 115 ------------------- railties/lib/rails/log_subscriber/test_helper.rb | 97 ---------------- railties/lib/rails/rack/logger.rb | 24 ++-- railties/lib/rails/railtie.rb | 34 +----- railties/test/log_subscriber_test.rb | 123 --------------------- 37 files changed, 528 insertions(+), 579 deletions(-) create mode 100644 actionmailer/lib/action_mailer/log_subscriber.rb delete mode 100644 actionmailer/lib/action_mailer/railties/log_subscriber.rb create mode 100644 actionpack/lib/action_controller/log_subscriber.rb delete mode 100644 actionpack/lib/action_controller/railties/log_subscriber.rb create mode 100644 actionpack/lib/action_view/log_subscriber.rb delete mode 100644 actionpack/lib/action_view/railties/log_subscriber.rb create mode 100644 activerecord/lib/active_record/log_subscriber.rb delete mode 100644 activerecord/lib/active_record/railties/log_subscriber.rb create mode 100644 activeresource/lib/active_resource/log_subscriber.rb delete mode 100644 activeresource/lib/active_resource/railties/log_subscriber.rb create mode 100644 activesupport/lib/active_support/log_subscriber.rb create mode 100644 activesupport/lib/active_support/log_subscriber/test_helper.rb create mode 100644 activesupport/test/log_subscriber_test.rb delete mode 100644 railties/lib/rails/log_subscriber.rb delete mode 100644 railties/lib/rails/log_subscriber/test_helper.rb delete mode 100644 railties/test/log_subscriber_test.rb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 44c7382f72..3b6e2c6387 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -4,6 +4,7 @@ require 'action_mailer/collector' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/proc' +require 'action_mailer/log_subscriber' module ActionMailer #:nodoc: # Action Mailer allows you to send email from your application using a mailer model and views. diff --git a/actionmailer/lib/action_mailer/log_subscriber.rb b/actionmailer/lib/action_mailer/log_subscriber.rb new file mode 100644 index 0000000000..7ba57b19e0 --- /dev/null +++ b/actionmailer/lib/action_mailer/log_subscriber.rb @@ -0,0 +1,22 @@ +require 'active_support/core_ext/array/wrap' + +module ActionMailer + class LogSubscriber < ActiveSupport::LogSubscriber + def deliver(event) + recipients = Array.wrap(event.payload[:to]).join(', ') + info("\nSent mail to #{recipients} (%1.fms)" % event.duration) + debug(event.payload[:mail]) + end + + def receive(event) + info("\nReceived mail (%.1fms)" % event.duration) + debug(event.payload[:mail]) + end + + def logger + ActionMailer::Base.logger + end + end +end + +ActionMailer::LogSubscriber.attach_to :action_mailer \ No newline at end of file diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb index 43a4936013..d7b09b2dc6 100644 --- a/actionmailer/lib/action_mailer/railtie.rb +++ b/actionmailer/lib/action_mailer/railtie.rb @@ -5,9 +5,6 @@ module ActionMailer class Railtie < Rails::Railtie config.action_mailer = ActiveSupport::OrderedOptions.new - require "action_mailer/railties/log_subscriber" - log_subscriber :action_mailer, ActionMailer::Railties::LogSubscriber.new - initializer "action_mailer.logger" do ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger } end diff --git a/actionmailer/lib/action_mailer/railties/log_subscriber.rb b/actionmailer/lib/action_mailer/railties/log_subscriber.rb deleted file mode 100644 index af76d807d0..0000000000 --- a/actionmailer/lib/action_mailer/railties/log_subscriber.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'active_support/core_ext/array/wrap' - -module ActionMailer - module Railties - class LogSubscriber < Rails::LogSubscriber - def deliver(event) - recipients = Array.wrap(event.payload[:to]).join(', ') - info("\nSent mail to #{recipients} (%1.fms)" % event.duration) - debug(event.payload[:mail]) - end - - def receive(event) - info("\nReceived mail (%.1fms)" % event.duration) - debug(event.payload[:mail]) - end - - def logger - ActionMailer::Base.logger - end - end - end -end diff --git a/actionmailer/test/log_subscriber_test.rb b/actionmailer/test/log_subscriber_test.rb index c08c34b4a2..ba9b4d6500 100644 --- a/actionmailer/test/log_subscriber_test.rb +++ b/actionmailer/test/log_subscriber_test.rb @@ -1,13 +1,13 @@ require "abstract_unit" -require "rails/log_subscriber/test_helper" -require "action_mailer/railties/log_subscriber" +require "active_support/log_subscriber/test_helper" +require "action_mailer/log_subscriber" class AMLogSubscriberTest < ActionMailer::TestCase - include Rails::LogSubscriber::TestHelper + include ActiveSupport::LogSubscriber::TestHelper def setup super - Rails::LogSubscriber.add(:action_mailer, ActionMailer::Railties::LogSubscriber.new) + ActionMailer::LogSubscriber.attach_to :action_mailer end class TestMailer < ActionMailer::Base diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 8611d0d3c3..a70ba0d2e3 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1,3 +1,5 @@ +require "action_controller/log_subscriber" + module ActionController class Base < Metal abstract! diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb new file mode 100644 index 0000000000..ece270b3ce --- /dev/null +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -0,0 +1,56 @@ +require 'active_support/core_ext/object/blank' + +module ActionController + class LogSubscriber < ActiveSupport::LogSubscriber + INTERNAL_PARAMS = %w(controller action format _method only_path) + + def start_processing(event) + payload = event.payload + params = payload[:params].except(*INTERNAL_PARAMS) + + info " Processing by #{payload[:controller]}##{payload[:action]} as #{payload[:formats].first.to_s.upcase}" + info " Parameters: #{params.inspect}" unless params.empty? + end + + def process_action(event) + payload = event.payload + additions = ActionController::Base.log_process_action(payload) + + message = "Completed #{payload[:status]} #{Rack::Utils::HTTP_STATUS_CODES[payload[:status]]} in %.0fms" % event.duration + message << " (#{additions.join(" | ")})" unless additions.blank? + + info(message) + end + + def send_file(event) + message = "Sent file %s" + message << " (%.1fms)" + info(message % [event.payload[:path], event.duration]) + end + + def redirect_to(event) + info "Redirected to #{event.payload[:location]}" + end + + def send_data(event) + info("Sent data %s (%.1fms)" % [event.payload[:filename], event.duration]) + end + + %w(write_fragment read_fragment exist_fragment? + expire_fragment expire_page write_page).each do |method| + class_eval <<-METHOD, __FILE__, __LINE__ + 1 + def #{method}(event) + key_or_path = event.payload[:key] || event.payload[:path] + human_name = #{method.to_s.humanize.inspect} + info("\#{human_name} \#{key_or_path} (%.1fms)" % event.duration) + end + METHOD + end + + def logger + ActionController::Base.logger + end + end +end + +ActionController::LogSubscriber.attach_to :action_controller \ No newline at end of file diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index 0e3cdffadc..86395c4d93 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -6,7 +6,6 @@ require "active_support/core_ext/class/subclasses" require "active_support/deprecation/proxy_wrappers" require "active_support/deprecation" -require "action_controller/railties/log_subscriber" require "action_controller/railties/url_helpers" module ActionController @@ -35,8 +34,6 @@ module ActionController end end - log_subscriber :action_controller, ActionController::Railties::LogSubscriber.new - initializer "action_controller.set_configs" do |app| paths = app.config.paths ac = app.config.action_controller diff --git a/actionpack/lib/action_controller/railties/log_subscriber.rb b/actionpack/lib/action_controller/railties/log_subscriber.rb deleted file mode 100644 index 00ac3bdf67..0000000000 --- a/actionpack/lib/action_controller/railties/log_subscriber.rb +++ /dev/null @@ -1,56 +0,0 @@ -require 'active_support/core_ext/object/blank' - -module ActionController - module Railties - class LogSubscriber < Rails::LogSubscriber - INTERNAL_PARAMS = %w(controller action format _method only_path) - - def start_processing(event) - payload = event.payload - params = payload[:params].except(*INTERNAL_PARAMS) - - info " Processing by #{payload[:controller]}##{payload[:action]} as #{payload[:formats].first.to_s.upcase}" - info " Parameters: #{params.inspect}" unless params.empty? - end - - def process_action(event) - payload = event.payload - additions = ActionController::Base.log_process_action(payload) - - message = "Completed #{payload[:status]} #{Rack::Utils::HTTP_STATUS_CODES[payload[:status]]} in %.0fms" % event.duration - message << " (#{additions.join(" | ")})" unless additions.blank? - - info(message) - end - - def send_file(event) - message = "Sent file %s" - message << " (%.1fms)" - info(message % [event.payload[:path], event.duration]) - end - - def redirect_to(event) - info "Redirected to #{event.payload[:location]}" - end - - def send_data(event) - info("Sent data %s (%.1fms)" % [event.payload[:filename], event.duration]) - end - - %w(write_fragment read_fragment exist_fragment? - expire_fragment expire_page write_page).each do |method| - class_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{method}(event) - key_or_path = event.payload[:key] || event.payload[:path] - human_name = #{method.to_s.humanize.inspect} - info("\#{human_name} \#{key_or_path} (%.1fms)" % event.duration) - end - METHOD - end - - def logger - ActionController::Base.logger - end - end - end -end \ No newline at end of file diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 50df1c97b2..956c88a553 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/array/wrap' require 'active_support/ordered_options' +require 'action_view/log_subscriber' module ActionView #:nodoc: class NonConcattingString < ActiveSupport::SafeBuffer diff --git a/actionpack/lib/action_view/log_subscriber.rb b/actionpack/lib/action_view/log_subscriber.rb new file mode 100644 index 0000000000..4a52937c58 --- /dev/null +++ b/actionpack/lib/action_view/log_subscriber.rb @@ -0,0 +1,27 @@ +module ActionView + # = Action View Log Subscriber + # + # Provides functionality so that Rails can output logs from Action View. + class LogSubscriber < ActiveSupport::LogSubscriber + def render_template(event) + message = "Rendered #{from_rails_root(event.payload[:identifier])}" + message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout] + message << (" (%.1fms)" % event.duration) + info(message) + end + alias :render_partial :render_template + alias :render_collection :render_template + + def logger + ActionController::Base.logger + end + + protected + + def from_rails_root(string) + string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "") + end + end +end + +ActionView::LogSubscriber.attach_to :action_view \ No newline at end of file diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb index 8c7b7d64c2..33dfcbb803 100644 --- a/actionpack/lib/action_view/railtie.rb +++ b/actionpack/lib/action_view/railtie.rb @@ -8,9 +8,6 @@ module ActionView config.action_view.stylesheet_expansions = {} config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] } - require "action_view/railties/log_subscriber" - log_subscriber :action_view, ActionView::Railties::LogSubscriber.new - initializer "action_view.cache_asset_timestamps" do |app| unless app.config.cache_classes ActiveSupport.on_load(:action_view) do diff --git a/actionpack/lib/action_view/railties/log_subscriber.rb b/actionpack/lib/action_view/railties/log_subscriber.rb deleted file mode 100644 index cb2ad0711e..0000000000 --- a/actionpack/lib/action_view/railties/log_subscriber.rb +++ /dev/null @@ -1,27 +0,0 @@ -module ActionView - # = Action View Log Subscriber - # - # Provides functionality so that Rails can output logs from Action View. - module Railties - class LogSubscriber < Rails::LogSubscriber - def render_template(event) - message = "Rendered #{from_rails_root(event.payload[:identifier])}" - message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout] - message << (" (%.1fms)" % event.duration) - info(message) - end - alias :render_partial :render_template - alias :render_collection :render_template - - def logger - ActionController::Base.logger - end - - protected - - def from_rails_root(string) - string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "") - end - end - end -end \ No newline at end of file diff --git a/actionpack/test/activerecord/controller_runtime_test.rb b/actionpack/test/activerecord/controller_runtime_test.rb index 331f861d8f..cfd86d704d 100644 --- a/actionpack/test/activerecord/controller_runtime_test.rb +++ b/actionpack/test/activerecord/controller_runtime_test.rb @@ -1,8 +1,8 @@ require 'active_record_unit' require 'active_record/railties/controller_runtime' require 'fixtures/project' -require 'rails/log_subscriber/test_helper' -require 'action_controller/railties/log_subscriber' +require 'active_support/log_subscriber/test_helper' +require 'action_controller/log_subscriber' ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime @@ -13,18 +13,18 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase end end - include Rails::LogSubscriber::TestHelper + include ActiveSupport::LogSubscriber::TestHelper tests LogSubscriberController def setup super @old_logger = ActionController::Base.logger - Rails::LogSubscriber.add(:action_controller, ActionController::Railties::LogSubscriber.new) + ActionController::LogSubscriber.attach_to :action_controller end def teardown super - Rails::LogSubscriber.log_subscribers.clear + ActiveSupport::LogSubscriber.log_subscribers.clear ActionController::Base.logger = @old_logger end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index b11eba2f89..0a18741f0c 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -1,6 +1,6 @@ require "abstract_unit" -require "rails/log_subscriber/test_helper" -require "action_controller/railties/log_subscriber" +require "active_support/log_subscriber/test_helper" +require "action_controller/log_subscriber" module Another class LogSubscribersController < ActionController::Base @@ -37,7 +37,7 @@ end class ACLogSubscriberTest < ActionController::TestCase tests Another::LogSubscribersController - include Rails::LogSubscriber::TestHelper + include ActiveSupport::LogSubscriber::TestHelper def setup super @@ -47,12 +47,12 @@ class ACLogSubscriberTest < ActionController::TestCase @cache_path = File.expand_path('../temp/test_cache', File.dirname(__FILE__)) ActionController::Base.page_cache_directory = @cache_path @controller.cache_store = :file_store, @cache_path - Rails::LogSubscriber.add(:action_controller, ActionController::Railties::LogSubscriber.new) + ActionController::LogSubscriber.attach_to :action_controller end def teardown super - Rails::LogSubscriber.log_subscribers.clear + ActiveSupport::LogSubscriber.log_subscribers.clear FileUtils.rm_rf(@cache_path) ActionController::Base.logger = @old_logger end diff --git a/actionpack/test/template/log_subscriber_test.rb b/actionpack/test/template/log_subscriber_test.rb index 5076dfa70f..eb1e548672 100644 --- a/actionpack/test/template/log_subscriber_test.rb +++ b/actionpack/test/template/log_subscriber_test.rb @@ -1,22 +1,22 @@ require "abstract_unit" -require "rails/log_subscriber/test_helper" -require "action_view/railties/log_subscriber" +require "active_support/log_subscriber/test_helper" +require "action_view/log_subscriber" require "controller/fake_models" class AVLogSubscriberTest < ActiveSupport::TestCase - include Rails::LogSubscriber::TestHelper + include ActiveSupport::LogSubscriber::TestHelper def setup super @old_logger = ActionController::Base.logger @view = ActionView::Base.new(ActionController::Base.view_paths, {}) Rails.stubs(:root).returns(File.expand_path(FIXTURE_LOAD_PATH)) - Rails::LogSubscriber.add(:action_view, ActionView::Railties::LogSubscriber.new) + ActionView::LogSubscriber.attach_to :action_view end def teardown super - Rails::LogSubscriber.log_subscribers.clear + ActiveSupport::LogSubscriber.log_subscribers.clear ActionController::Base.logger = @old_logger end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7a262ad465..16cf501bd5 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -20,6 +20,7 @@ require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/object/blank' require 'arel' require 'active_record/errors' +require 'active_record/log_subscriber' module ActiveRecord #:nodoc: # = Active Record diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb new file mode 100644 index 0000000000..71065f9908 --- /dev/null +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -0,0 +1,32 @@ +module ActiveRecord + class LogSubscriber < ActiveSupport::LogSubscriber + def initialize + super + @odd_or_even = false + end + + def sql(event) + name = '%s (%.1fms)' % [event.payload[:name], event.duration] + sql = event.payload[:sql].squeeze(' ') + + if odd? + name = color(name, :cyan, true) + sql = color(sql, nil, true) + else + name = color(name, :magenta, true) + end + + debug " #{name} #{sql}" + end + + def odd? + @odd_or_even = !@odd_or_even + end + + def logger + ActiveRecord::Base.logger + end + end +end + +ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 36df878e1b..2808e199fe 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -26,9 +26,6 @@ module ActiveRecord load "active_record/railties/databases.rake" end - require "active_record/railties/log_subscriber" - log_subscriber :active_record, ActiveRecord::Railties::LogSubscriber.new - initializer "active_record.initialize_timezone" do ActiveSupport.on_load(:active_record) do self.time_zone_aware_attributes = true diff --git a/activerecord/lib/active_record/railties/log_subscriber.rb b/activerecord/lib/active_record/railties/log_subscriber.rb deleted file mode 100644 index 31b98bb6ed..0000000000 --- a/activerecord/lib/active_record/railties/log_subscriber.rb +++ /dev/null @@ -1,32 +0,0 @@ -module ActiveRecord - module Railties - class LogSubscriber < Rails::LogSubscriber - def initialize - super - @odd_or_even = false - end - - def sql(event) - name = '%s (%.1fms)' % [event.payload[:name], event.duration] - sql = event.payload[:sql].squeeze(' ') - - if odd? - name = color(name, :cyan, true) - sql = color(sql, nil, true) - else - name = color(name, :magenta, true) - end - - debug " #{name} #{sql}" - end - - def odd? - @odd_or_even = !@odd_or_even - end - - def logger - ActiveRecord::Base.logger - end - end - end -end diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 1f544b4211..4aeae1fe45 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -1,20 +1,19 @@ require "cases/helper" require "models/developer" -require "rails/log_subscriber/test_helper" -require "active_record/railties/log_subscriber" +require "active_support/log_subscriber/test_helper" class LogSubscriberTest < ActiveSupport::TestCase - include Rails::LogSubscriber::TestHelper + include ActiveSupport::LogSubscriber::TestHelper def setup @old_logger = ActiveRecord::Base.logger super - - Rails::LogSubscriber.add(:active_record, ActiveRecord::Railties::LogSubscriber.new) + ActiveRecord::LogSubscriber.attach_to(:active_record) end def teardown super + ActiveRecord::LogSubscriber.log_subscribers.pop ActiveRecord::Base.logger = @old_logger end diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 521e2b6f5d..6c494a8bcc 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -17,6 +17,7 @@ require 'active_resource/exceptions' require 'active_resource/connection' require 'active_resource/formats' require 'active_resource/schema' +require 'active_resource/log_subscriber' module ActiveResource # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application. diff --git a/activeresource/lib/active_resource/log_subscriber.rb b/activeresource/lib/active_resource/log_subscriber.rb new file mode 100644 index 0000000000..9e52baf36d --- /dev/null +++ b/activeresource/lib/active_resource/log_subscriber.rb @@ -0,0 +1,15 @@ +module ActiveResource + class LogSubscriber < ActiveSupport::LogSubscriber + def request(event) + result = event.payload[:result] + info "#{event.payload[:method].to_s.upcase} #{event.payload[:request_uri]}" + info "--> %d %s %d (%.1fms)" % [result.code, result.message, result.body.to_s.length, event.duration] + end + + def logger + ActiveResource::Base.logger + end + end +end + +ActiveResource::LogSubscriber.attach_to :active_resource \ No newline at end of file diff --git a/activeresource/lib/active_resource/railtie.rb b/activeresource/lib/active_resource/railtie.rb index aa878c7212..60f6f88311 100644 --- a/activeresource/lib/active_resource/railtie.rb +++ b/activeresource/lib/active_resource/railtie.rb @@ -5,9 +5,6 @@ module ActiveResource class Railtie < Rails::Railtie config.active_resource = ActiveSupport::OrderedOptions.new - require "active_resource/railties/log_subscriber" - log_subscriber :active_resource, ActiveResource::Railties::LogSubscriber.new - initializer "active_resource.set_configs" do |app| app.config.active_resource.each do |k,v| ActiveResource::Base.send "#{k}=", v diff --git a/activeresource/lib/active_resource/railties/log_subscriber.rb b/activeresource/lib/active_resource/railties/log_subscriber.rb deleted file mode 100644 index 86806a93d0..0000000000 --- a/activeresource/lib/active_resource/railties/log_subscriber.rb +++ /dev/null @@ -1,15 +0,0 @@ -module ActiveResource - module Railties - class LogSubscriber < Rails::LogSubscriber - def request(event) - result = event.payload[:result] - info "#{event.payload[:method].to_s.upcase} #{event.payload[:request_uri]}" - info "--> %d %s %d (%.1fms)" % [result.code, result.message, result.body.to_s.length, event.duration] - end - - def logger - ActiveResource::Base.logger - end - end - end -end \ No newline at end of file diff --git a/activeresource/test/cases/log_subscriber_test.rb b/activeresource/test/cases/log_subscriber_test.rb index f0330e8f51..3cd96007db 100644 --- a/activeresource/test/cases/log_subscriber_test.rb +++ b/activeresource/test/cases/log_subscriber_test.rb @@ -1,12 +1,11 @@ require "abstract_unit" require "fixtures/person" -require "rails/log_subscriber/test_helper" -require "active_resource/railties/log_subscriber" +require "active_support/log_subscriber/test_helper" +require "active_resource/log_subscriber" require "active_support/core_ext/hash/conversions" -# TODO: This test should be part of Railties class LogSubscriberTest < ActiveSupport::TestCase - include Rails::LogSubscriber::TestHelper + include ActiveSupport::LogSubscriber::TestHelper def setup super @@ -16,7 +15,7 @@ class LogSubscriberTest < ActiveSupport::TestCase mock.get "/people/1.xml", {}, @matz end - Rails::LogSubscriber.add(:active_resource, ActiveResource::Railties::LogSubscriber.new) + ActiveResource::LogSubscriber.attach_to :active_resource end def set_logger(logger) diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 3ce5476bbd..ba91e8bba3 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -41,6 +41,8 @@ module ActiveSupport autoload :DescendantsTracker autoload :FileUpdateChecker + autoload :LogSubscriber + autoload :Notifications # TODO: Narrow this list down eager_autoload do @@ -64,7 +66,6 @@ module ActiveSupport autoload :OptionMerger autoload :OrderedHash autoload :OrderedOptions - autoload :Notifications autoload :Rescuable autoload :SecureRandom autoload :StringInquirer diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb new file mode 100644 index 0000000000..a1ffb8eece --- /dev/null +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -0,0 +1,114 @@ +require 'active_support/core_ext/module/attribute_accessors' +require 'active_support/core_ext/class/attribute' + +module ActiveSupport + # ActiveSupport::LogSubscriber is an object set to consume ActiveSupport::Notifications + # with solely purpose of logging. The log subscriber dispatches notifications to a + # regirested object based on its given namespace. + # + # An example would be Active Record log subscriber responsible for logging queries: + # + # module ActiveRecord + # class LogSubscriber < ActiveSupport::LogSubscriber + # def sql(event) + # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}" + # end + # end + # end + # + # And it's finally registed as: + # + # ActiveRecord::LogSubscriber.attach_to :active_record + # + # Since we need to know all instance methods before attaching the log subscriber, + # the line above shuold be called after your ActiveRecord::LogSubscriber definition. + # + # After configured, whenever a "sql.active_record" notification is published, + # it will properly dispatch the event (ActiveSupport::Notifications::Event) to + # the sql method. + # + # Log subscriber also has some helpers to deal with logging and automatically flushes + # all logs when the request finishes (via action_dispatch.callback notification) in + # a Rails environment. + class LogSubscriber + mattr_accessor :colorize_logging + self.colorize_logging = true + + class_attribute :logger + + def self.logger + @logger ||= Rails.logger if defined?(Rails) + end + + # Embed in a String to clear all previous ANSI sequences. + CLEAR = "\e[0m" + BOLD = "\e[1m" + + # Colors + BLACK = "\e[30m" + RED = "\e[31m" + GREEN = "\e[32m" + YELLOW = "\e[33m" + BLUE = "\e[34m" + MAGENTA = "\e[35m" + CYAN = "\e[36m" + WHITE = "\e[37m" + + def self.attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications) + log_subscribers << log_subscriber + @@flushable_loggers = nil + + log_subscriber.public_methods(false).each do |event| + notifier.subscribe("#{event}.#{namespace}") do |*args| + next if log_subscriber.logger.nil? + + begin + log_subscriber.send(event, ActiveSupport::Notifications::Event.new(*args)) + rescue Exception => e + log_subscriber.logger.error "Could not log #{args[0].inspect} event. #{e.class}: #{e.message}" + end + end + end + end + + def self.log_subscribers + @@log_subscribers ||= [] + end + + def self.flushable_loggers + @@flushable_loggers ||= begin + loggers = log_subscribers.map(&:logger) + loggers.uniq! + loggers.select { |l| l.respond_to?(:flush) } + end + end + + # Flush all log_subscribers' logger. + def self.flush_all! + flushable_loggers.each(&:flush) + end + + protected + + %w(info debug warn error fatal unknown).each do |level| + class_eval <<-METHOD, __FILE__, __LINE__ + 1 + def #{level}(*args, &block) + return unless logger + logger.#{level}(*args, &block) + end + METHOD + end + + # Set color by using a string or one of the defined constants. If a third + # option is set to true, it also adds bold to the string. This is based + # on Highline implementation and it automatically appends CLEAR to the end + # of the returned String. + # + def color(text, color, bold=false) + return text unless colorize_logging + color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) + bold = bold ? BOLD : "" + "#{bold}#{color}#{text}#{CLEAR}" + end + end +end diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb new file mode 100644 index 0000000000..96506a4b2b --- /dev/null +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -0,0 +1,90 @@ +require 'active_support/log_subscriber' + +module ActiveSupport + class LogSubscriber + # Provides some helpers to deal with testing log subscribers by setting up + # notifications. Take for instance Active Record subscriber tests: + # + # class SyncLogSubscriberTest < ActiveSupport::TestCase + # include ActiveSupport::LogSubscriber::TestHelper + # + # def setup + # ActiveRecord::LogSubscriber.attach_to(:active_record) + # end + # + # def test_basic_query_logging + # Developer.all + # wait + # assert_equal 1, @logger.logged(:debug).size + # assert_match /Developer Load/, @logger.logged(:debug).last + # assert_match /SELECT \* FROM "developers"/, @logger.logged(:debug).last + # end + # end + # + # All you need to do is to ensure that your log subscriber is added to Rails::Subscriber, + # as in the second line of the code above. The test helpers is reponsible for setting + # up the queue, subscriptions and turning colors in logs off. + # + # The messages are available in the @logger instance, which is a logger with limited + # powers (it actually do not send anything to your output), and you can collect them + # doing @logger.logged(level), where level is the level used in logging, like info, + # debug, warn and so on. + # + module TestHelper + def setup + @logger = MockLogger.new + @notifier = ActiveSupport::Notifications::Notifier.new(queue) + + ActiveSupport::LogSubscriber.colorize_logging = false + + set_logger(@logger) + ActiveSupport::Notifications.notifier = @notifier + end + + def teardown + set_logger(nil) + ActiveSupport::Notifications.notifier = nil + end + + class MockLogger + attr_reader :flush_count + + def initialize + @flush_count = 0 + @logged = Hash.new { |h,k| h[k] = [] } + end + + def method_missing(level, message) + @logged[level] << message + end + + def logged(level) + @logged[level].compact.map { |l| l.to_s.strip } + end + + def flush + @flush_count += 1 + end + end + + # Wait notifications to be published. + def wait + @notifier.wait + end + + # Overwrite if you use another logger in your log subscriber: + # + # def logger + # ActiveRecord::Base.logger = @logger + # end + # + def set_logger(logger) + ActiveSupport::LogSubscriber.logger = logger + end + + def queue + ActiveSupport::Notifications::Fanout.new + end + end + end +end \ No newline at end of file diff --git a/activesupport/test/log_subscriber_test.rb b/activesupport/test/log_subscriber_test.rb new file mode 100644 index 0000000000..0c1f3c51ed --- /dev/null +++ b/activesupport/test/log_subscriber_test.rb @@ -0,0 +1,123 @@ +require 'abstract_unit' +require 'active_support/log_subscriber/test_helper' + +class MyLogSubscriber < ActiveSupport::LogSubscriber + attr_reader :event + + def some_event(event) + @event = event + info event.name + end + + def foo(event) + debug "debug" + info "info" + warn "warn" + end + + def bar(event) + info "#{color("cool", :red)}, #{color("isn't it?", :blue, true)}" + end + + def puke(event) + raise "puke" + end +end + +class SyncLogSubscriberTest < ActiveSupport::TestCase + include ActiveSupport::LogSubscriber::TestHelper + + def setup + super + @log_subscriber = MyLogSubscriber.new + end + + def teardown + super + ActiveSupport::LogSubscriber.log_subscribers.clear + end + + def instrument(*args, &block) + ActiveSupport::Notifications.instrument(*args, &block) + end + + def test_proxies_method_to_rails_logger + @log_subscriber.foo(nil) + assert_equal %w(debug), @logger.logged(:debug) + assert_equal %w(info), @logger.logged(:info) + assert_equal %w(warn), @logger.logged(:warn) + end + + def test_set_color_for_messages + ActiveSupport::LogSubscriber.colorize_logging = true + @log_subscriber.bar(nil) + assert_equal "\e[31mcool\e[0m, \e[1m\e[34misn't it?\e[0m", @logger.logged(:info).last + end + + def test_does_not_set_color_if_colorize_logging_is_set_to_false + @log_subscriber.bar(nil) + assert_equal "cool, isn't it?", @logger.logged(:info).last + end + + def test_event_is_sent_to_the_registered_class + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + instrument "some_event.my_log_subscriber" + wait + assert_equal %w(some_event.my_log_subscriber), @logger.logged(:info) + end + + def test_event_is_an_active_support_notifications_event + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + instrument "some_event.my_log_subscriber" + wait + assert_kind_of ActiveSupport::Notifications::Event, @log_subscriber.event + end + + def test_does_not_send_the_event_if_it_doesnt_match_the_class + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + instrument "unknown_event.my_log_subscriber" + wait + # If we get here, it means that NoMethodError was not raised. + end + + def test_does_not_send_the_event_if_logger_is_nil + ActiveSupport::LogSubscriber.logger = nil + @log_subscriber.expects(:some_event).never + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + instrument "some_event.my_log_subscriber" + wait + end + + def test_does_not_fail_with_non_namespaced_events + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + instrument "whatever" + wait + end + + def test_flushes_loggers + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + ActiveSupport::LogSubscriber.flush_all! + assert_equal 1, @logger.flush_count + end + + def test_flushes_the_same_logger_just_once + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + ActiveSupport::LogSubscriber.attach_to :another, @log_subscriber + ActiveSupport::LogSubscriber.flush_all! + wait + assert_equal 1, @logger.flush_count + end + + def test_logging_does_not_die_on_failures + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + instrument "puke.my_log_subscriber" + instrument "some_event.my_log_subscriber" + wait + + assert_equal 1, @logger.logged(:info).size + assert_equal 'some_event.my_log_subscriber', @logger.logged(:info).last + + assert_equal 1, @logger.logged(:error).size + assert_equal 'Could not log "puke.my_log_subscriber" event. RuntimeError: puke', @logger.logged(:error).last + end +end \ No newline at end of file diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index ed66f493e6..bbf28a8c08 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -9,7 +9,6 @@ require 'active_support/core_ext/logger' require 'rails/application' require 'rails/version' require 'rails/deprecation' -require 'rails/log_subscriber' require 'active_support/railtie' require 'action_dispatch/railtie' diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 25e54e9dce..e3165b2d4c 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -110,7 +110,7 @@ module Rails def colorize_logging=(val) @colorize_logging = val - Rails::LogSubscriber.colorize_logging = val + ActiveSupport::LogSubscriber.colorize_logging = val self.generators.colorize_logging = val end diff --git a/railties/lib/rails/log_subscriber.rb b/railties/lib/rails/log_subscriber.rb deleted file mode 100644 index a30701d4d5..0000000000 --- a/railties/lib/rails/log_subscriber.rb +++ /dev/null @@ -1,115 +0,0 @@ -require 'active_support/core_ext/class/inheritable_attributes' -require 'active_support/notifications' - -module Rails - # Rails::LogSubscriber is an object set to consume ActiveSupport::Notifications - # on initialization with solely purpose of logging. The log subscriber dispatches - # notifications to a regirested object based on its given namespace. - # - # An example would be Active Record log subscriber responsible for logging queries: - # - # module ActiveRecord - # class Railtie - # class LogSubscriber < Rails::LogSubscriber - # def sql(event) - # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}" - # end - # end - # end - # end - # - # It's finally registed as: - # - # Rails::LogSubscriber.add :active_record, ActiveRecord::Railtie::LogSubscriber.new - # - # So whenever a "sql.active_record" notification arrive to Rails::LogSubscriber, - # it will properly dispatch the event (ActiveSupport::Notifications::Event) to - # the sql method. - # - # This is useful because it avoids spanning several log subscribers just for logging - # purposes(which slows down the main thread). Besides of providing a centralized - # facility on top of Rails.logger. - # - # Log subscriber also has some helpers to deal with logging and automatically flushes - # all logs when the request finishes (via action_dispatch.callback notification). - class LogSubscriber - mattr_accessor :colorize_logging - self.colorize_logging = true - - # Embed in a String to clear all previous ANSI sequences. - CLEAR = "\e[0m" - BOLD = "\e[1m" - - # Colors - BLACK = "\e[30m" - RED = "\e[31m" - GREEN = "\e[32m" - YELLOW = "\e[33m" - BLUE = "\e[34m" - MAGENTA = "\e[35m" - CYAN = "\e[36m" - WHITE = "\e[37m" - - def self.add(namespace, log_subscriber, notifier = ActiveSupport::Notifications) - log_subscribers << log_subscriber - @flushable_loggers = nil - - log_subscriber.public_methods(false).each do |event| - notifier.subscribe("#{event}.#{namespace}") do |*args| - next if log_subscriber.logger.nil? - - begin - log_subscriber.send(event, ActiveSupport::Notifications::Event.new(*args)) - rescue Exception => e - Rails.logger.error "Could not log #{args[0].inspect} event. #{e.class}: #{e.message}" - end - end - end - end - - def self.log_subscribers - @log_subscribers ||= [] - end - - def self.flushable_loggers - @flushable_loggers ||= begin - loggers = log_subscribers.map(&:logger) - loggers.uniq! - loggers.select { |l| l.respond_to?(:flush) } - end - end - - # Flush all log_subscribers' logger. - def self.flush_all! - flushable_loggers.each(&:flush) - end - - # By default, we use the Rails.logger for logging. - def logger - Rails.logger - end - - protected - - %w(info debug warn error fatal unknown).each do |level| - class_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{level}(*args, &block) - return unless logger - logger.#{level}(*args, &block) - end - METHOD - end - - # Set color by using a string or one of the defined constants. If a third - # option is set to true, it also adds bold to the string. This is based - # on Highline implementation and it automatically appends CLEAR to the end - # of the returned String. - # - def color(text, color, bold=false) - return text unless colorize_logging - color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) - bold = bold ? BOLD : "" - "#{bold}#{color}#{text}#{CLEAR}" - end - end -end diff --git a/railties/lib/rails/log_subscriber/test_helper.rb b/railties/lib/rails/log_subscriber/test_helper.rb deleted file mode 100644 index 9b7b0738cd..0000000000 --- a/railties/lib/rails/log_subscriber/test_helper.rb +++ /dev/null @@ -1,97 +0,0 @@ -require 'rails/log_subscriber' - -module Rails - class LogSubscriber - # Provides some helpers to deal with testing log subscribers by setting up - # notifications. Take for instance Active Record subscriber tests: - # - # class SyncLogSubscriberTest < ActiveSupport::TestCase - # include Rails::LogSubscriber::TestHelper - # Rails::LogSubscriber.add(:active_record, ActiveRecord::Railties::LogSubscriber.new) - # - # def test_basic_query_logging - # Developer.all - # wait - # assert_equal 1, @logger.logged(:debug).size - # assert_match /Developer Load/, @logger.logged(:debug).last - # assert_match /SELECT \* FROM "developers"/, @logger.logged(:debug).last - # end - # - # class SyncLogSubscriberTest < ActiveSupport::TestCase - # include Rails::LogSubscriber::SyncTestHelper - # include LogSubscriberTest - # end - # - # class AsyncLogSubscriberTest < ActiveSupport::TestCase - # include Rails::LogSubscriber::AsyncTestHelper - # include LogSubscriberTest - # end - # end - # - # All you need to do is to ensure that your log subscriber is added to Rails::Subscriber, - # as in the second line of the code above. The test helpers is reponsible for setting - # up the queue, subscriptions and turning colors in logs off. - # - # The messages are available in the @logger instance, which is a logger with limited - # powers (it actually do not send anything to your output), and you can collect them - # doing @logger.logged(level), where level is the level used in logging, like info, - # debug, warn and so on. - # - module TestHelper - def setup - @logger = MockLogger.new - @notifier = ActiveSupport::Notifications::Notifier.new(queue) - - Rails::LogSubscriber.colorize_logging = false - - set_logger(@logger) - ActiveSupport::Notifications.notifier = @notifier - end - - def teardown - set_logger(nil) - ActiveSupport::Notifications.notifier = nil - end - - class MockLogger - attr_reader :flush_count - - def initialize - @flush_count = 0 - @logged = Hash.new { |h,k| h[k] = [] } - end - - def method_missing(level, message) - @logged[level] << message - end - - def logged(level) - @logged[level].compact.map { |l| l.to_s.strip } - end - - def flush - @flush_count += 1 - end - end - - # Wait notifications to be published. - def wait - @notifier.wait - end - - # Overwrite if you use another logger in your log subscriber: - # - # def logger - # ActiveRecord::Base.logger = @logger - # end - # - def set_logger(logger) - Rails.logger = logger - end - - def queue - ActiveSupport::Notifications::Fanout.new - end - end - end -end \ No newline at end of file diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb index 73e9af3b41..b3dc1f894c 100644 --- a/railties/lib/rails/rack/logger.rb +++ b/railties/lib/rails/rack/logger.rb @@ -1,10 +1,9 @@ -require 'rails/log_subscriber' require 'active_support/core_ext/time/conversions' module Rails module Rack # Log the request started and flush all loggers after it. - class Logger < Rails::LogSubscriber + class Logger < ActiveSupport::LogSubscriber def initialize(app) @app = app end @@ -16,20 +15,19 @@ module Rails after_dispatch(env) end - protected + protected - def before_dispatch(env) - request = ActionDispatch::Request.new(env) - path = request.fullpath + def before_dispatch(env) + request = ActionDispatch::Request.new(env) + path = request.fullpath - info "\n\nStarted #{env["REQUEST_METHOD"]} \"#{path}\" " \ - "for #{request.ip} at #{Time.now.to_default_s}" - end - - def after_dispatch(env) - Rails::LogSubscriber.flush_all! - end + info "\n\nStarted #{env["REQUEST_METHOD"]} \"#{path}\" " \ + "for #{request.ip} at #{Time.now.to_default_s}" + end + def after_dispatch(env) + ActiveSupport::LogSubscriber.flush_all! + end end end end diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index ad776933f2..dbdbfea509 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -114,36 +114,6 @@ module Rails # end # end # - # == Adding your subscriber - # - # Since version 3.0, Rails ships with a notification system which is used for several - # purposes, including logging. If you are sending notifications in your Railtie, you may - # want to add a subscriber to consume such notifications for logging purposes. - # - # The subscriber is added under the railtie_name namespace and only consumes notifications - # under the given namespace. For example, let's suppose your railtie is publishing the - # following "something_expensive" instrumentation: - # - # ActiveSupport::Notifications.instrument "my_railtie.something_expensive" do - # # something expensive - # end - # - # You can log this instrumentation with your own Rails::Subscriber: - # - # class MyRailtie::Subscriber < Rails::Subscriber - # def something_expensive(event) - # info("Something expensive took %.1fms" % event.duration) - # end - # end - # - # By registering it: - # - # class MyRailtie < Railtie - # subscriber :my_gem, MyRailtie::Subscriber.new - # end - # - # Take a look in Rails::Subscriber docs for more information. - # # == Application, Plugin and Engine # # A Rails::Engine is nothing more than a Railtie with some initializers already set. @@ -176,8 +146,8 @@ module Rails ActiveSupport::Deprecation.warn "railtie_name is deprecated and has no effect", caller end - def log_subscriber(name, log_subscriber) - Rails::LogSubscriber.add(name, log_subscriber) + def log_subscriber(*) + ActiveSupport::Deprecation.warn "log_subscriber is deprecated and has no effect", caller end def rake_tasks(&blk) diff --git a/railties/test/log_subscriber_test.rb b/railties/test/log_subscriber_test.rb deleted file mode 100644 index a3a755ae62..0000000000 --- a/railties/test/log_subscriber_test.rb +++ /dev/null @@ -1,123 +0,0 @@ -require 'abstract_unit' -require 'rails/log_subscriber/test_helper' - -class MyLogSubscriber < Rails::LogSubscriber - attr_reader :event - - def some_event(event) - @event = event - info event.name - end - - def foo(event) - debug "debug" - info "info" - warn "warn" - end - - def bar(event) - info "#{color("cool", :red)}, #{color("isn't it?", :blue, true)}" - end - - def puke(event) - raise "puke" - end -end - -class SyncLogSubscriberTest < ActiveSupport::TestCase - include Rails::LogSubscriber::TestHelper - - def setup - super - @log_subscriber = MyLogSubscriber.new - end - - def teardown - super - Rails::LogSubscriber.log_subscribers.clear - end - - def instrument(*args, &block) - ActiveSupport::Notifications.instrument(*args, &block) - end - - def test_proxies_method_to_rails_logger - @log_subscriber.foo(nil) - assert_equal %w(debug), @logger.logged(:debug) - assert_equal %w(info), @logger.logged(:info) - assert_equal %w(warn), @logger.logged(:warn) - end - - def test_set_color_for_messages - Rails::LogSubscriber.colorize_logging = true - @log_subscriber.bar(nil) - assert_equal "\e[31mcool\e[0m, \e[1m\e[34misn't it?\e[0m", @logger.logged(:info).last - end - - def test_does_not_set_color_if_colorize_logging_is_set_to_false - @log_subscriber.bar(nil) - assert_equal "cool, isn't it?", @logger.logged(:info).last - end - - def test_event_is_sent_to_the_registered_class - Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "some_event.my_log_subscriber" - wait - assert_equal %w(some_event.my_log_subscriber), @logger.logged(:info) - end - - def test_event_is_an_active_support_notifications_event - Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "some_event.my_log_subscriber" - wait - assert_kind_of ActiveSupport::Notifications::Event, @log_subscriber.event - end - - def test_does_not_send_the_event_if_it_doesnt_match_the_class - Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "unknown_event.my_log_subscriber" - wait - # If we get here, it means that NoMethodError was not raised. - end - - def test_does_not_send_the_event_if_logger_is_nil - Rails.logger = nil - @log_subscriber.expects(:some_event).never - Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "some_event.my_log_subscriber" - wait - end - - def test_does_not_fail_with_non_namespaced_events - Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "whatever" - wait - end - - def test_flushes_loggers - Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - Rails::LogSubscriber.flush_all! - assert_equal 1, @logger.flush_count - end - - def test_flushes_the_same_logger_just_once - Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - Rails::LogSubscriber.add :another, @log_subscriber - Rails::LogSubscriber.flush_all! - wait - assert_equal 1, @logger.flush_count - end - - def test_logging_does_not_die_on_failures - Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "puke.my_log_subscriber" - instrument "some_event.my_log_subscriber" - wait - - assert_equal 1, @logger.logged(:info).size - assert_equal 'some_event.my_log_subscriber', @logger.logged(:info).last - - assert_equal 1, @logger.logged(:error).size - assert_equal 'Could not log "puke.my_log_subscriber" event. RuntimeError: puke', @logger.logged(:error).last - end -end \ No newline at end of file -- cgit v1.2.3 From 176c386409fd57bc03b9ebf1570a8955e21e0800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elomar=20Fran=C3=A7a?= Date: Tue, 22 Jun 2010 19:50:15 -0300 Subject: Fix bug where ActiveResource::HttpMock would overwrite Accept/Content-Type header to application/xml [#4939 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activeresource/lib/active_resource/http_mock.rb | 16 +++++- activeresource/test/cases/http_mock_test.rb | 71 +++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 activeresource/test/cases/http_mock_test.rb diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb index 1ed3804017..f192c53b4f 100644 --- a/activeresource/lib/active_resource/http_mock.rb +++ b/activeresource/lib/active_resource/http_mock.rb @@ -148,16 +148,28 @@ module ActiveResource attr_accessor :path, :method, :body, :headers def initialize(method, path, body = nil, headers = {}) - @method, @path, @body, @headers = method, path, body, headers.merge(ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[method] => 'application/xml') + @method, @path, @body, @headers = method, path, body, headers end def ==(req) - path == req.path && method == req.method && headers == req.headers + path == req.path && method == req.method && headers_match?(req) end def to_s "<#{method.to_s.upcase}: #{path} [#{headers}] (#{body})>" end + + private + + def headers_match?(req) + # Ignore format header on equality if it's not defined + format_header = ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[method] + if headers[format_header].present? || req.headers[format_header].blank? + headers == req.headers + else + headers.dup.merge(format_header => req.headers[format_header]) == req.headers + end + end end class Response diff --git a/activeresource/test/cases/http_mock_test.rb b/activeresource/test/cases/http_mock_test.rb new file mode 100644 index 0000000000..5e032d03f1 --- /dev/null +++ b/activeresource/test/cases/http_mock_test.rb @@ -0,0 +1,71 @@ +require 'abstract_unit' + +class HttpMockTest < ActiveSupport::TestCase + setup do + @http = ActiveResource::HttpMock.new("http://example.com") + end + + FORMAT_HEADER = ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES + + [:post, :put, :get, :delete, :head].each do |method| + test "responds to simple #{method} request" do + ActiveResource::HttpMock.respond_to do |mock| + mock.send(method, "/people/1", {FORMAT_HEADER[method] => "application/xml"}, "Response") + end + + assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/xml").body + end + + test "adds format header by default to #{method} request" do + ActiveResource::HttpMock.respond_to do |mock| + mock.send(method, "/people/1", {}, "Response") + end + + assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/xml").body + end + + test "respond only when headers match header by default to #{method} request" do + ActiveResource::HttpMock.respond_to do |mock| + mock.send(method, "/people/1", {"X-Header" => "X"}, "Response") + end + + assert_equal "Response", request(method, "/people/1", "X-Header" => "X").body + assert_raise(ActiveResource::InvalidRequestError) { request(method, "/people/1") } + end + + test "does not overwrite format header to #{method} request" do + ActiveResource::HttpMock.respond_to do |mock| + mock.send(method, "/people/1", {FORMAT_HEADER[method] => "application/json"}, "Response") + end + + assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/json").body + end + + test "ignores format header when there is only one response to same url in a #{method} request" do + ActiveResource::HttpMock.respond_to do |mock| + mock.send(method, "/people/1", {}, "Response") + end + + assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/json").body + assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/xml").body + end + + test "responds correctly when format header is given to #{method} request" do + ActiveResource::HttpMock.respond_to do |mock| + mock.send(method, "/people/1", {FORMAT_HEADER[method] => "application/xml"}, "XML") + mock.send(method, "/people/1", {FORMAT_HEADER[method] => "application/json"}, "Json") + end + + assert_equal "XML", request(method, "/people/1", FORMAT_HEADER[method] => "application/xml").body + assert_equal "Json", request(method, "/people/1", FORMAT_HEADER[method] => "application/json").body + end + end + + def request(method, path, headers = {}, body = nil) + if [:put, :post].include? method + @http.send(method, path, body, headers) + else + @http.send(method, path, headers) + end + end +end -- cgit v1.2.3 From df15ba7eadb180626474161741f901e675f517ce Mon Sep 17 00:00:00 2001 From: Andrew White Date: Thu, 24 Jun 2010 11:48:05 +0100 Subject: Fix namespaced redirects [#4389 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- actionpack/test/dispatch/routing_test.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index e001d10c0f..67a629036b 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -55,7 +55,7 @@ module ActionDispatch path = args.first end - if @scope[:module] && options[:to] + if @scope[:module] && options[:to] && !options[:to].is_a?(Proc) if options[:to].to_s.include?("#") options[:to] = "#{@scope[:module]}/#{options[:to]}" elsif @scope[:controller].nil? diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 58a1fa0518..8f43b5f934 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -300,6 +300,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match '/' => 'mes#index' end + namespace :private do + root :to => redirect('/private/index') + match "index", :to => 'private#index' + end + match "whatever/:controller(/:action(/:id))" resource :profile do @@ -421,6 +426,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_namespace_redirect + with_test_routes do + get '/private' + assert_equal 301, @response.status + assert_equal 'http://www.example.com/private/index', @response.headers['Location'] + assert_equal 'Moved Permanently', @response.body + end + end + def test_session_singleton_resource with_test_routes do get '/session' -- cgit v1.2.3 From 7710a8cb6b0f3b8f94c130aa5d6062d45e34219c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 24 Jun 2010 04:31:07 -0700 Subject: Ops, this should be beta4. --- version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.rb b/version.rb index c10876134a..34d96fd0f3 100644 --- a/version.rb +++ b/version.rb @@ -3,7 +3,7 @@ module Rails MAJOR = 3 MINOR = 0 TINY = 0 - BUILD = "beta3" + BUILD = "beta4" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end -- cgit v1.2.3 From fdb7f84eb10c5e59490764a1a259aa00a1fcfe5f Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 11:17:05 -0300 Subject: Remove deprecated block usage in composed_of. --- activerecord/lib/active_record/aggregations.rb | 6 ++---- activerecord/test/cases/aggregations_test.rb | 28 -------------------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 51ffc7542c..c45400d3d9 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -190,7 +190,7 @@ module ActiveRecord # :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) }, # :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) } # - def composed_of(part_id, options = {}, &block) + def composed_of(part_id, options = {}) options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter) name = part_id.id2name @@ -199,9 +199,7 @@ module ActiveRecord mapping = [ mapping ] unless mapping.first.is_a?(Array) allow_nil = options[:allow_nil] || false constructor = options[:constructor] || :new - converter = options[:converter] || block - - ActiveSupport::Deprecation.warn('The conversion block has been deprecated, use the :converter option instead.', caller) if block_given? + converter = options[:converter] reader_method(name, class_name, mapping, allow_nil, constructor) writer_method(name, class_name, mapping, allow_nil, converter) diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb index e5fc1a2046..74588b4f47 100644 --- a/activerecord/test/cases/aggregations_test.rb +++ b/activerecord/test/cases/aggregations_test.rb @@ -121,34 +121,6 @@ class AggregationsTest < ActiveRecord::TestCase end end -class DeprecatedAggregationsTest < ActiveRecord::TestCase - class Person < ActiveRecord::Base; end - - def test_conversion_block_is_deprecated - assert_deprecated 'conversion block has been deprecated' do - Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money } - end - end - - def test_conversion_block_used_when_converter_option_is_nil - assert_deprecated 'conversion block has been deprecated' do - Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money } - end - assert_raise(NoMethodError) { Person.new.balance = 5 } - end - - def test_converter_option_overrides_conversion_block - assert_deprecated 'conversion block has been deprecated' do - Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money } - end - - person = Person.new - assert_nothing_raised { person.balance = 5 } - assert_equal 5, person.balance.amount - assert_kind_of Money, person.balance - end -end - class OverridingAggregationsTest < ActiveRecord::TestCase class Name; end class DifferentName; end -- cgit v1.2.3 From d9f199e1238a723432a2005c405fc5ae22dea24b Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 11:40:23 -0300 Subject: Don't define block, just yield if block is given. --- .../active_record/associations/association_collection.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index f8d46bcb48..186b531ffb 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -26,10 +26,10 @@ module ActiveRecord delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped - def select(select = nil, &block) + def select(select = nil) if block_given? load_target - @target.select(&block) + @target.select.each { |e| yield e } else scoped.select(select) end @@ -123,7 +123,7 @@ module ActiveRecord end end - # Add +records+ to this association. Returns +self+ so method calls may be chained. + # Add +records+ to this association. Returns +self+ so method calls may be chained. # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically. def <<(*records) result = true @@ -168,7 +168,7 @@ module ActiveRecord reset_target! reset_named_scopes_cache! end - + # Calculate sum using SQL, not Enumerable def sum(*args) if block_given? @@ -241,7 +241,7 @@ module ActiveRecord if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy destroy_all - else + else delete_all end @@ -520,8 +520,8 @@ module ActiveRecord def callbacks_for(callback_name) full_callback_name = "#{callback_name}_for_#{@reflection.name}" @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || [] - end - + end + def ensure_owner_is_not_new if @owner.new_record? raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" -- cgit v1.2.3 From 4086ecea24446904bac4c69812f219ce7cbfbbba Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 12:02:00 -0300 Subject: Remove block definition from method, is not needed since yield is used inside. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 2 +- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 4567539566..be8d1bd76b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -107,7 +107,7 @@ module ActiveRecord # REFERENTIAL INTEGRITY ==================================== # Override to turn off referential integrity while executing &block. - def disable_referential_integrity(&block) + def disable_referential_integrity yield end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 7c7bc5e292..aa3626a37e 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -219,7 +219,7 @@ module ActiveRecord # REFERENTIAL INTEGRITY ==================================== - def disable_referential_integrity(&block) #:nodoc: + def disable_referential_integrity #:nodoc: old = select_value("SELECT @@FOREIGN_KEY_CHECKS") begin diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e84242601b..6fa4c50d6a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -372,7 +372,7 @@ module ActiveRecord return false end - def disable_referential_integrity(&block) #:nodoc: + def disable_referential_integrity #:nodoc: if supports_disable_referential_integrity?() then execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";")) end -- cgit v1.2.3 From 2c203a94136d5b8681e2b2b55783ef6dde54405f Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 13:07:39 -0300 Subject: Remove block definitions in finder methods. --- activerecord/lib/active_record/relation/finder_methods.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 7a0c9dc612..f39951e16c 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -87,8 +87,8 @@ module ActiveRecord # person.visits += 1 # person.save! # end - def find(*args, &block) - return to_a.find(&block) if block_given? + def find(*args) + return to_a.find { |*block_args| yield(*block_args) } if block_given? options = args.extract_options! @@ -259,8 +259,8 @@ module ActiveRecord record end - def find_with_ids(*ids, &block) - return to_a.find(&block) if block_given? + def find_with_ids(*ids) + return to_a.find { |*block_args| yield(*block_args) } if block_given? expects_array = ids.first.kind_of?(Array) return ids.first if expects_array && ids.first.empty? -- cgit v1.2.3 From b549d93d2f34a18971e691ff93e4c5b7b092eb14 Mon Sep 17 00:00:00 2001 From: Nick Sieger Date: Tue, 15 Jun 2010 15:28:19 -0500 Subject: AS::Isolation functional on Windows/JRuby. Doesn't make up for the fact that it's slooooooooow, though. Signed-off-by: wycats --- activesupport/lib/active_support/testing/isolation.rb | 19 +++++++++++-------- railties/test/abstract_unit.rb | 2 -- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index 69df399cde..d629f6f2b7 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -45,12 +45,16 @@ module ActiveSupport end end + def _run_class_setup # class setup method should only happen in parent + unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST'] + self.class.setup if self.class.respond_to?(:setup) + @@ran_class_setup = true + end + end + module TestUnit def run(result) - unless defined?(@@ran_class_setup) - self.class.setup if self.class.respond_to?(:setup) - @@ran_class_setup = true - end + _run_class_setup yield(Test::Unit::TestCase::STARTED, name) @@ -74,10 +78,7 @@ module ActiveSupport module MiniTest def run(runner) - unless defined?(@@ran_class_setup) - self.class.setup if self.class.respond_to?(:setup) - @@ran_class_setup = true - end + _run_class_setup serialized = run_in_isolation do |isolated_runner| super(isolated_runner) @@ -109,6 +110,8 @@ module ActiveSupport end module Subprocess + ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV) + # Crazy H4X to get this working in windows / jruby with # no forking. def run_in_isolation(&blk) diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index d04a2aa1f3..a05bae5dcc 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -1,5 +1,3 @@ -ORIG_ARGV = ARGV.dup - require File.expand_path("../../../load_paths", __FILE__) require 'stringio' -- cgit v1.2.3 From 4b5f417e63e4cdd7fd6837c10ebaa9dd4860f55e Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 24 Jun 2010 13:17:24 -0300 Subject: Only yield block if given. --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 43032ba9d8..e8d0f215f6 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -22,9 +22,9 @@ module ActiveRecord end class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def select(*args, &block) + def select(*args) if block_given? - to_a.select(&block) + to_a.select { |*block_args| yield(*block_args) } else new_relation = clone value = Array.wrap(args.flatten).reject {|x| x.blank? } -- cgit v1.2.3 From d69ebb849a78c07a4efc869789c4bc90e8741482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 24 Jun 2010 20:02:23 +0200 Subject: Avoid deserializing cookies too early, which causes session objects to not be available yet. Unfortunately, could not reproduce this in a test case. --- .../middleware/session/abstract_store.rb | 33 +++++++++++----------- .../middleware/session/cookie_store.rb | 7 ++++- .../test/dispatch/session/cookie_store_test.rb | 6 ++-- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 7623a94234..2dff139b49 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -18,29 +18,28 @@ module ActionDispatch def initialize(by, env, default_options) @by = by @env = env - merge!(default_options) @session_id_loaded = false + merge!(default_options) end - alias_method :get_without_session_load, :[] - def [](key) if key == :id - load_session_id! unless has_session_id? + load_session_id! unless key?(:id) || has_session_id? end super(key) end - private + private - def has_session_id? - get_without_session_load(:id).present? || @session_id_loaded - end + def has_session_id? + @session_id_loaded + end - def load_session_id! - self[:id] = @by.send(:extract_session_id, @env) - @session_id_loaded = true - end + def load_session_id! + self[:id] = @by.send(:extract_session_id, @env) + ensure + @session_id_loaded = true + end end class SessionHash < Hash @@ -99,7 +98,7 @@ module ActionDispatch def destroy clear @by.send(:destroy, @env) if @by - @env[ENV_SESSION_OPTIONS_KEY].delete(:id) if @env && @env[ENV_SESSION_OPTIONS_KEY] + @env[ENV_SESSION_OPTIONS_KEY][:id] = nil if @env && @env[ENV_SESSION_OPTIONS_KEY] @loaded = false end @@ -164,8 +163,8 @@ module ActionDispatch session_data = env[ENV_SESSION_KEY] options = env[ENV_SESSION_OPTIONS_KEY] - if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after] - session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?) + if !session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after] + session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded? sid = options[:id] || generate_sid session_data = session_data.to_hash @@ -189,7 +188,7 @@ module ActionDispatch def prepare!(env) env[ENV_SESSION_KEY] = SessionHash.new(self, env) - env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options.dup) + env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options) end def generate_sid @@ -207,7 +206,7 @@ module ActionDispatch end def extract_session_id(env) - request = Rack::Request.new(env) + request = ActionDispatch::Request.new(env) sid = request.cookies[@key] sid ||= request.params[@key] unless @cookie_only sid diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 7c5626735b..6c1567f470 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -65,8 +65,9 @@ module ActionDispatch request = ActionDispatch::Request.new(env) if data = request.cookie_jar.signed[@key] data.stringify_keys! + else + {} end - data end def set_cookie(request, options) @@ -77,6 +78,10 @@ module ActionDispatch persistent_session_id!(session_data, sid) end + def exists?(env) + ActionDispatch::Request.new(env).cookie_jar.key?(@key) + end + def destroy(env) # session data is stored on client; nothing to do here end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 787524ab7b..6aca22b456 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -205,21 +205,21 @@ class CookieStoreTest < ActionController::IntegrationTest def test_session_store_without_domain with_test_route_set do get '/set_session_value' - assert_no_match /domain\=/, headers['Set-Cookie'] + assert_no_match(/domain\=/, headers['Set-Cookie']) end end def test_session_store_with_nil_domain with_test_route_set(:domain => nil) do get '/set_session_value' - assert_no_match /domain\=/, headers['Set-Cookie'] + assert_no_match(/domain\=/, headers['Set-Cookie']) end end def test_session_store_with_all_domains with_test_route_set(:domain => :all) do get '/set_session_value' - assert_match /domain=\.example\.com/, headers['Set-Cookie'] + assert_match(/domain=\.example\.com/, headers['Set-Cookie']) end end -- cgit v1.2.3 From 974196b0910924d08640703cc558c48ca049246a Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Thu, 24 Jun 2010 23:21:32 +0700 Subject: Remove obsolete test case, since we have move Rails::LogSubscriber to ActiveSupport::LogSubscriber in [6788db824ab732b13493a9d702dd8fb89fa153c8] [#4816 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/test/railties/railtie_test.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb index 2accaca855..c74cc01dc1 100644 --- a/railties/test/railties/railtie_test.rb +++ b/railties/test/railties/railtie_test.rb @@ -48,15 +48,6 @@ module RailtiesTest assert_equal "hello", AppTemplate::Application.config.foo.greetings end - test "railtie can add log subscribers" do - begin - class Foo < Rails::Railtie ; log_subscriber(:foo, Rails::LogSubscriber.new) ; end - assert_kind_of Rails::LogSubscriber, Rails::LogSubscriber.log_subscribers[0] - ensure - Rails::LogSubscriber.log_subscribers.clear - end - end - test "railtie can add to_prepare callbacks" do $to_prepare = false class Foo < Rails::Railtie ; config.to_prepare { $to_prepare = true } ; end -- cgit v1.2.3 From 81c5684267b0341a1537c2db83a820bcff8dc808 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 24 Jun 2010 12:42:09 -0400 Subject: adding middleware test for RAILS_CACHE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/test/application/middleware_test.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 999f666a64..e66e81ea2c 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -83,6 +83,17 @@ module ApplicationTests assert_equal "Rack::Config", middleware.second end + test "RAILS_CACHE does not respond to middleware" do + add_to_config "config.cache_store = :memory_store" + boot! + assert_equal "Rack::Runtime", middleware.third + end + + test "RAILS_CACHE does respond to middleware" do + boot! + assert_equal "Rack::Runtime", middleware.fourth + end + test "insert middleware before" do add_to_config "config.middleware.insert_before ActionDispatch::Static, Rack::Config" boot! -- cgit v1.2.3 From 497a0c3b00cc225215a8744bbf2a7e04c7297b7c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 23 Jun 2010 21:12:51 -0300 Subject: quotes are not necessary here MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 16cf501bd5..e7b52287a5 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -917,8 +917,8 @@ module ActiveRecord #:nodoc: def instantiate(record) object = find_sti_class(record[inheritance_column]).allocate - object.instance_variable_set(:'@attributes', record) - object.instance_variable_set(:'@attributes_cache', {}) + object.instance_variable_set(:@attributes, record) + object.instance_variable_set(:@attributes_cache, {}) object.instance_variable_set(:@new_record, false) object.instance_variable_set(:@readonly, false) object.instance_variable_set(:@destroyed, false) -- cgit v1.2.3 From 7a7c608a26f03abb1245ff83d4e25040ad09cb44 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 23 Jun 2010 21:44:27 -0300 Subject: Your original TIME ZONE value on PostgreSQL is correctly restored now, after going through options :utc and then going back to :local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4950 state:committed] Signed-off-by: José Valim --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e84242601b..851e6d3718 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -216,7 +216,10 @@ module ActiveRecord super(connection, logger) @connection_parameters, @config = connection_parameters, config + # @local_tz is initialized as nil to avoid warnings when connect tries to use it + @local_tz = nil connect + @local_tz = execute('SHOW TIME ZONE').first["TimeZone"] end # Is this connection alive and ready for queries? @@ -929,9 +932,8 @@ module ActiveRecord # TIMESTAMP WITH ZONE types in UTC. if ActiveRecord::Base.default_timezone == :utc execute("SET time zone 'UTC'") - else - offset = Time.local(2000).utc_offset / 3600 - execute("SET time zone '#{offset}'") + elsif @local_tz + execute("SET time zone '#{@local_tz}'") end end -- cgit v1.2.3 From 67ee6c38b9b112eabe37d5869c23210b9ebf453c Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Thu, 24 Jun 2010 22:05:27 +0700 Subject: Remove the --singeleton option from scaffold generator. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turned out to be that scaffold for singeleton resource will always depend on another model, and it's not possible at the moment to make the application tests pass after generate the singeleton scafold. So, it would be better to remove it for now and probably provide another generator, such as singeleton_scaffold, in which also require the depended model name. [#4863 state:resolved] Signed-off-by: José Valim --- railties/lib/rails/generators.rb | 3 +-- .../rails/generators/erb/scaffold/scaffold_generator.rb | 7 +------ .../rails/generators/rails/resource/resource_generator.rb | 15 +-------------- .../scaffold_controller/scaffold_controller_generator.rb | 2 -- .../rails/scaffold_controller/templates/controller.rb | 2 -- .../generators/test_unit/scaffold/scaffold_generator.rb | 1 - .../test_unit/scaffold/templates/functional_test.rb | 2 -- railties/test/generators/resource_generator_test.rb | 8 -------- .../test/generators/scaffold_controller_generator_test.rb | 14 -------------- 9 files changed, 3 insertions(+), 51 deletions(-) diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 41aecea355..8794392a7d 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -50,7 +50,6 @@ module Rails :performance_tool => nil, :resource_controller => :controller, :scaffold_controller => :scaffold_controller, - :singleton => false, :stylesheets => true, :test_framework => nil, :template_engine => :erb @@ -334,4 +333,4 @@ module Rails paths end end -end \ No newline at end of file +end diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb index 2db7f7bbf3..b2c8d7051b 100644 --- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb @@ -8,17 +8,12 @@ module Erb argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" - class_option :singleton, :type => :boolean, :desc => "Supply to skip index view" - def create_root_folder empty_directory File.join("app/views", controller_file_path) end def copy_view_files - views = available_views - views.delete("index") if options[:singleton] - - views.each do |view| + available_views.each do |view| filename = filename_with_extensions(view) template filename, File.join("app/views", controller_file_path, filename) end diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb index ee302b8aad..fc070026d6 100644 --- a/railties/lib/rails/generators/rails/resource/resource_generator.rb +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -14,26 +14,13 @@ module Rails class_option :actions, :type => :array, :banner => "ACTION ACTION", :default => [], :desc => "Actions for the resource controller" - class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" - def add_resource_route return if options[:actions].present? route_config = class_path.collect{|namespace| "namespace :#{namespace} do " }.join(" ") - route_config << "resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}" + route_config << "resources :#{file_name.pluralize}" route_config << " end" * class_path.size route route_config end - - protected - - def pluralize?(name) - if options[:singleton] - name - else - name.pluralize - end - end - end end end diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb index 49af2974cd..2271c6f9c1 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb @@ -10,8 +10,6 @@ module Rails class_option :orm, :banner => "NAME", :type => :string, :required => true, :desc => "ORM to generate the controller for" - class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" - def create_controller_files template 'controller.rb', File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb") end diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb index 84cf58d7c4..b21340f755 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb @@ -1,5 +1,4 @@ class <%= controller_class_name %>Controller < ApplicationController -<% unless options[:singleton] -%> # GET <%= route_url %> # GET <%= route_url %>.xml def index @@ -10,7 +9,6 @@ class <%= controller_class_name %>Controller < ApplicationController format.xml { render :xml => @<%= plural_table_name %> } end end -<% end -%> # GET <%= route_url %>/1 # GET <%= route_url %>/1.xml diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb index c0315c7fe6..f7e907a017 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb @@ -6,7 +6,6 @@ module TestUnit class ScaffoldGenerator < Base include Rails::Generators::ResourceHelpers - class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" check_class_collision :suffix => "ControllerTest" def create_test_files diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb index 957ebaa522..f23e495450 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb @@ -5,13 +5,11 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase @<%= singular_table_name %> = <%= table_name %>(:one) end -<% unless options[:singleton] -%> test "should get index" do get :index assert_response :success assert_not_nil assigns(:<%= table_name %>) end -<% end -%> test "should get new" do get :new diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index 96fd7a0a72..55d5bd6f83 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -59,14 +59,6 @@ class ResourceGeneratorTest < Rails::Generators::TestCase end end - def test_singleton_resource - run_generator ["account", "--singleton"] - - assert_file "config/routes.rb" do |route| - assert_match /resource :account$/, route - end - end - def test_plural_names_are_singularized content = run_generator ["accounts".freeze] assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/ diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 8040b22fe6..d55ed22975 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -78,20 +78,6 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase end end - def test_generates_singleton_controller - run_generator ["User", "name:string", "age:integer", "--singleton"] - - assert_file "app/controllers/users_controller.rb" do |content| - assert_no_match /def index/, content - end - - assert_file "test/functional/users_controller_test.rb" do |content| - assert_no_match /test "should get index"/, content - end - - assert_no_file "app/views/users/index.html.erb" - end - def test_skip_helper_if_required run_generator ["User", "name:string", "age:integer", "--no-helper"] assert_no_file "app/helpers/users_helper.rb" -- cgit v1.2.3 From 0d2cebe3386e57332dc63501a17246e647cefb7a Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 24 Jun 2010 14:27:11 -0400 Subject: fixing test by replacing assert with assert_equal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/test/application/initializers/frameworks_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 604c50ec2d..7c9f67da69 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -40,7 +40,7 @@ module ApplicationTests RUBY require "#{app_path}/config/environment" - assert "test.rails", ActionMailer::Base.default_url_options[:host] + assert_equal "test.rails", ActionMailer::Base.default_url_options[:host] end # AS -- cgit v1.2.3 From 4e8ca7bc9a5adac45391177d5b47a33d1da93036 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 24 Jun 2010 14:25:36 -0400 Subject: adding missing assertion and fixing the test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/test/application/initializers/frameworks_test.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 7c9f67da69..7269a7c5a8 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -28,8 +28,10 @@ module ApplicationTests RUBY require "#{app_path}/config/environment" - ActionController::Base.view_paths.include?(File.expand_path("app/views", app_path)) - ActionMailer::Base.view_paths.include?(File.expand_path("app/views", app_path)) + + expanded_path = File.expand_path("app/views", app_path) + assert_equal ActionController::Base.view_paths[0].to_s, expanded_path + assert_equal ActionMailer::Base.view_paths[0].to_s, expanded_path end test "allows me to configure default url options for ActionMailer" do -- cgit v1.2.3 From 6682cce0386811ffe3e6d31fc025ede0936d86c3 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 24 Jun 2010 17:38:46 -0500 Subject: Dont reload the environment, just not needed bro --- railties/lib/rails/console/app.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb index 4a7701081b..9d9763699d 100644 --- a/railties/lib/rails/console/app.rb +++ b/railties/lib/rails/console/app.rb @@ -30,5 +30,3 @@ def reload!(print=true) ActionDispatch::Callbacks.new(Proc.new {}, false).call({}) true end - -reload!(false) -- cgit v1.2.3 From a12b76b09e98493c1e4aee147416ae5999402298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Jun 2010 09:36:26 +0200 Subject: Just reading flash messages should not create a session if one does not exist yet. --- actionpack/lib/action_dispatch/http/request.rb | 3 +++ actionpack/lib/action_dispatch/middleware/flash.rb | 11 +++++++++-- actionpack/test/controller/flash_test.rb | 20 +++++++++++++++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 6b611823d0..fd23b1df79 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -194,9 +194,12 @@ module ActionDispatch @env['rack.input'] end + # TODO This should be broken apart into AD::Request::Session and probably + # be included by the session middleware. def reset_session session.destroy if session self.session = {} + @env['action_dispatch.request.flash_hash'] = nil end def session=(session) #:nodoc: diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 18771fe782..bfa30cf5af 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -4,7 +4,7 @@ module ActionDispatch # read a notice you put there or flash["notice"] = "hello" # to put a new one. def flash - session['flash'] ||= Flash::FlashHash.new + @env['action_dispatch.request.flash_hash'] ||= (session["flash"] || Flash::FlashHash.new) end end @@ -176,7 +176,14 @@ module ActionDispatch @app.call(env) ensure - if (session = env['rack.session']) && session.key?('flash') && session['flash'].empty? + session = env['rack.session'] || {} + flash_hash = env['action_dispatch.request.flash_hash'] + + if flash_hash && (!flash_hash.empty? || session.key?('flash')) + session["flash"] = flash_hash + end + + if session.key?('flash') && session['flash'].empty? session.delete('flash') end end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 5c636cbab8..4be09f8c83 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -236,6 +236,15 @@ class FlashIntegrationTest < ActionController::IntegrationTest end end + def test_just_using_flash_does_not_stream_a_cookie_back + with_test_route_set do + get '/use_flash' + assert_response :success + assert_nil @response.headers["Set-Cookie"] + assert_equal "flash: ", @response.body + end + end + private # Overwrite get to send SessionSecret in env hash @@ -247,10 +256,15 @@ class FlashIntegrationTest < ActionController::IntegrationTest def with_test_route_set with_routing do |set| set.draw do |map| - match ':action', :to => ActionDispatch::Session::CookieStore.new( - FlashIntegrationTest::TestController, :key => FlashIntegrationTest::SessionKey, :secret => FlashIntegrationTest::SessionSecret - ) + match ':action', :to => FlashIntegrationTest::TestController + end + + @app = self.class.build_app(set) do |middleware| + middleware.use ActionDispatch::Session::CookieStore, :key => SessionKey + middleware.use ActionDispatch::Flash + middleware.delete "ActionDispatch::ShowExceptions" end + yield end end -- cgit v1.2.3 From 617e94658ddc3f71c42fc4f8c2346f87ff106bbe Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Fri, 25 Jun 2010 02:42:08 +0700 Subject: Make sure that Rails doesn't resent session_id cookie over and over again if it's already there [#2485 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This apply to only Active Record store and Memcached store, as they both store only the session_id, which will be unchanged, in the cookie. Signed-off-by: José Valim --- .../lib/action_dispatch/middleware/session/abstract_store.rb | 4 +++- actionpack/test/activerecord/active_record_store_test.rb | 12 ++++++++++++ actionpack/test/dispatch/session/mem_cache_store_test.rb | 12 ++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 2dff139b49..fcc2287279 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -196,7 +196,9 @@ module ActionDispatch end def set_cookie(request, options) - request.cookie_jar[@key] = options + if request.cookie_jar[@key] != options[:value] || !options[:expires].nil? + request.cookie_jar[@key] = options + end end def load_session(env) diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index 736829dbf7..bdd1a0a15c 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -136,6 +136,18 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end end + def test_doesnt_write_session_cookie_if_session_id_is_already_exists + with_test_route_set do + get '/set_session_value' + assert_response :success + assert cookies['_session_id'] + + get '/get_session_value' + assert_response :success + assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists" + end + end + def test_prevents_session_fixation with_test_route_set do get '/set_session_value' diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index 08f8069888..d388992b98 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -117,6 +117,18 @@ class MemCacheStoreTest < ActionController::IntegrationTest end end + def test_doesnt_write_session_cookie_if_session_id_is_already_exists + with_test_route_set do + get '/set_session_value' + assert_response :success + assert cookies['_session_id'] + + get '/get_session_value' + assert_response :success + assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists" + end + end + def test_prevents_session_fixation with_test_route_set do get '/get_session_value' -- cgit v1.2.3 From b01df28413be0add0876de6406be9c02d4b96f4c Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Thu, 24 Jun 2010 17:36:54 -0300 Subject: Do not wrap hidden fields with error proc [#4962 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_view/helpers/active_model_helper.rb | 18 ++++++++++++++++-- actionpack/lib/action_view/helpers/form_helper.rb | 1 - actionpack/test/template/active_model_helper_test.rb | 7 +++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb index 0f9b04cb5f..6bb0875bc3 100644 --- a/actionpack/lib/action_view/helpers/active_model_helper.rb +++ b/actionpack/lib/action_view/helpers/active_model_helper.rb @@ -36,12 +36,16 @@ module ActionView end end - %w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| + %w(content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| module_eval "def #{meth}(*) error_wrapping(super) end", __FILE__, __LINE__ end + def tag(type, options, *) + tag_generate_errors?(options) ? error_wrapping(super) : super + end + def error_wrapping(html_tag) - if object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && object.errors[@method_name].any? + if object_has_errors? Base.field_error_proc.call(html_tag, self) else html_tag @@ -51,6 +55,16 @@ module ActionView def error_message object.errors[@method_name] end + + private + + def object_has_errors? + object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && error_message.any? + end + + def tag_generate_errors?(options) + options['type'] != 'hidden' + end end class FormBuilder diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 2bbe11fef2..d1b10a9281 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -124,7 +124,6 @@ module ActionView # model: # # <%= form_for :person do |f| %> - # <%= f.error_messages %> # First name: <%= f.text_field :first_name %>
    # Last name : <%= f.text_field :last_name %>
    # Biography : <%= f.text_area :biography %>
    diff --git a/actionpack/test/template/active_model_helper_test.rb b/actionpack/test/template/active_model_helper_test.rb index b1705072c2..6ab244d178 100644 --- a/actionpack/test/template/active_model_helper_test.rb +++ b/actionpack/test/template/active_model_helper_test.rb @@ -39,6 +39,13 @@ class ActiveModelHelperTest < ActionView::TestCase ) end + def test_hidden_field_does_not_render_errors + assert_dom_equal( + %(), + hidden_field("post", "author_name") + ) + end + def test_field_error_proc old_proc = ActionView::Base.field_error_proc ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| -- cgit v1.2.3 From 518b16d9aedf4534b11aab8043d4efd0361fa1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Fri, 25 Jun 2010 10:51:43 +0200 Subject: Line break in migration template and nicer code indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/migration/templates/migration.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb index d6ab3257a0..edc6a18d5d 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb @@ -1,15 +1,17 @@ class <%= migration_class_name %> < ActiveRecord::Migration - def self.up<% attributes.each do |attribute| %> - <%- if migration_action -%> + def self.up +<% attributes.each do |attribute| %> + <%- if migration_action -%> <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %> - <%- end -%> <%- end -%> +<%- end -%> end - def self.down<% attributes.reverse.each do |attribute| %> - <%- if migration_action -%> + def self.down +<% attributes.reverse.each do |attribute| %> + <%- if migration_action -%> <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %> - <%- end -%> <%- end -%> +<%- end -%> end end -- cgit v1.2.3 From 21c99e93883c1cf32474ad65a507e69e373a380c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Jun 2010 12:14:59 +0200 Subject: Calling exists? in the session store, without checking for stale sessions, was causing the cookie store to panic because we need to unpack the whole session to get its key. This commit fixes this issue and also caches exists calls for performance improvements. --- .../action_dispatch/middleware/session/abstract_store.rb | 5 ++++- .../action_dispatch/middleware/session/cookie_store.rb | 15 ++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index fcc2287279..e9e0c26c73 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -88,7 +88,10 @@ module ActionDispatch end def exists? - @by.send(:exists?, @env) + return @exists if instance_variable_defined?(:@exists) + stale_session_check! do + @exists = @by.send(:exists?, @env) + end end def loaded? diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 6c1567f470..7ebd532f4a 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -62,11 +62,12 @@ module ActionDispatch end def unpacked_cookie_data(env) - request = ActionDispatch::Request.new(env) - if data = request.cookie_jar.signed[@key] - data.stringify_keys! - else - {} + env["action_dispatch.request.unsigned_session_cookie"] ||= begin + request = ActionDispatch::Request.new(env) + if data = request.cookie_jar.signed[@key] + data.stringify_keys! + end + data || {} end end @@ -78,10 +79,6 @@ module ActionDispatch persistent_session_id!(session_data, sid) end - def exists?(env) - ActionDispatch::Request.new(env).cookie_jar.key?(@key) - end - def destroy(env) # session data is stored on client; nothing to do here end -- cgit v1.2.3 From 01472b5217ab3a8c03619ca458ec05552b4c7a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Jun 2010 19:03:00 +0200 Subject: Do not mark the session as loaded if an error happened while doing it. --- actionpack/lib/action_dispatch/middleware/session/abstract_store.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index e9e0c26c73..bc1d6fab83 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -24,7 +24,7 @@ module ActionDispatch def [](key) if key == :id - load_session_id! unless key?(:id) || has_session_id? + load_session_id! unless super(:id) || has_session_id? end super(key) end @@ -37,7 +37,6 @@ module ActionDispatch def load_session_id! self[:id] = @by.send(:extract_session_id, @env) - ensure @session_id_loaded = true end end -- cgit v1.2.3 From 0883b2b0c3dbe85a473a83cce6d1f733e0b74cb6 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 25 Jun 2010 10:11:46 -0700 Subject: Depend on unreleased rack-mount 0.6.6.pre --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index fe6ef9aa8a..d0b11b04e4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ source 'http://rubygems.org' gem "arel", :git => "git://github.com/rails/arel.git" +gem "rack-mount", :git => "git://github.com/rails/rack-mount.git" gem "rails", :path => File.dirname(__FILE__) gem "rake", ">= 0.8.7" -- cgit v1.2.3 From 3d8200318aab7458e019e2e434901cccc2532979 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Fri, 25 Jun 2010 12:16:43 +0100 Subject: Add failing test case for parameters with periods [#2536 state:resolved] Signed-off-by: Jeremy Kemper --- actionpack/test/dispatch/routing_test.rb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 8f43b5f934..915c51a020 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -213,6 +213,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get "profile" => "customers#profile", :as => :profile, :on => :member post "preview" => "customers#preview", :as => :preview, :on => :new end + scope(':version', :version => /.+/) do + resources :users, :id => /.+?/, :format => /json|xml/ + end end match 'sprockets.js' => ::TestRoutingMapper::SprocketsApp @@ -1421,6 +1424,30 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_non_greedy_regexp + with_test_routes do + get '/api/1.0/users' + assert_equal 'api/users#index', @response.body + assert_equal '/api/1.0/users', api_users_path(:version => '1.0') + + get '/api/1.0/users.json' + assert_equal 'api/users#index', @response.body + assert_equal true, @request.format.json? + assert_equal '/api/1.0/users.json', api_users_path(:version => '1.0', :format => :json) + + get '/api/1.0/users/first.last' + assert_equal 'api/users#show', @response.body + assert_equal 'first.last', @request.params[:id] + assert_equal '/api/1.0/users/first.last', api_user_path(:version => '1.0', :id => 'first.last') + + get '/api/1.0/users/first.last.xml' + assert_equal 'api/users#show', @response.body + assert_equal 'first.last', @request.params[:id] + assert_equal true, @request.format.xml? + assert_equal '/api/1.0/users/first.last.xml', api_user_path(:version => '1.0', :id => 'first.last', :format => :xml) + end + end + private def with_test_routes yield -- cgit v1.2.3 From 5f64bce10f2d4b7819e97586cbe3ab225e9e43e8 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Fri, 25 Jun 2010 14:38:17 +0100 Subject: Failing test for whether glob parameters accept regexps [#4970 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/test/dispatch/routing_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 915c51a020..68e3dd6de3 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -317,6 +317,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest post :preview end end + + match '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/ end end @@ -1448,6 +1450,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_glob_parameter_accepts_regexp + with_test_routes do + get '/en/path/to/existing/file.html' + assert_equal 200, @response.status + end + end + private def with_test_routes yield -- cgit v1.2.3 From 3344c011da17cb59f4452101c8e65067d7d30cce Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 13:07:17 -0300 Subject: Avoid a blank line before the add/remove columns --- .../rails/generators/active_record/migration/templates/migration.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb index edc6a18d5d..8ac21c1410 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb @@ -1,6 +1,6 @@ class <%= migration_class_name %> < ActiveRecord::Migration def self.up -<% attributes.each do |attribute| %> +<% attributes.each do |attribute| -%> <%- if migration_action -%> <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %> <%- end -%> @@ -8,7 +8,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration end def self.down -<% attributes.reverse.each do |attribute| %> +<% attributes.reverse.each do |attribute| -%> <%- if migration_action -%> <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %> <%- end -%> -- cgit v1.2.3 From 82dc16b7d2b6313117a42f7bf69a097b5ab79d1f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 24 Jun 2010 20:34:01 -0300 Subject: Metaprogramming not needed here --- .../lib/active_record/relation/query_methods.rb | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e8d0f215f6..adc56fbef0 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -21,18 +21,16 @@ module ActiveRecord CEVAL end - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def select(*args) - if block_given? - to_a.select { |*block_args| yield(*block_args) } - else - new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.select_values += value if value.present? - new_relation - end + def select(*args) + if block_given? + to_a.select { |*block_args| yield(*block_args) } + else + new_relation = clone + value = Array.wrap(args.flatten).reject {|x| x.blank? } + new_relation.select_values += value if value.present? + new_relation end - CEVAL + end [:where, :having].each do |query_method| class_eval <<-CEVAL, __FILE__, __LINE__ + 1 -- cgit v1.2.3 From 3d8ccb924084ecd341b2d9644e6e0b66903d8432 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 24 Jun 2010 20:36:01 -0300 Subject: Makes a build_select for the select part of build_arel --- .../lib/active_record/relation/query_methods.rb | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index adc56fbef0..1e570a569b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -137,16 +137,7 @@ module ActiveRecord arel = arel.order(*@order_values.uniq.select{|o| o.present?}) if @order_values.present? - selects = @select_values.uniq - - if selects.present? - selects.each do |s| - @implicit_readonly = false - arel = arel.project(s) if s.present? - end - else - arel = arel.project(@klass.quoted_table_name + '.*') - end + arel = build_select(arel, @select_values.uniq) arel = arel.from(@from_value) if @from_value.present? @@ -219,6 +210,18 @@ module ActiveRecord relation.join(custom_joins) end + def build_select(arel, selects) + if selects.present? + @implicit_readonly = false + selects.each do |s| + arel = arel.project(s) if s.present? + end + else + arel = arel.project(@klass.quoted_table_name + '.*') + end + arel + end + def apply_modules(modules) values = Array.wrap(modules) @extensions += values if values.present? -- cgit v1.2.3 From 0ebb5bf6590b8ac62c53538ade7095676baec3d4 Mon Sep 17 00:00:00 2001 From: Neeraj Singh and Santiago Pastorino Date: Thu, 24 Jun 2010 22:52:15 -0300 Subject: Support for multiple selects added [#4841 state:committed] --- activerecord/lib/active_record/relation/query_methods.rb | 11 +++++++---- activerecord/test/cases/method_scoping_test.rb | 8 +++++--- activerecord/test/cases/relations_test.rb | 6 ++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1e570a569b..4dbb30c777 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -213,13 +213,16 @@ module ActiveRecord def build_select(arel, selects) if selects.present? @implicit_readonly = false - selects.each do |s| - arel = arel.project(s) if s.present? + # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array. + # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array + if selects.all? { |s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) + arel.project(*selects) + else + arel.project(selects.last) end else - arel = arel.project(@klass.quoted_table_name + '.*') + arel.project(@klass.quoted_table_name + '.*') end - arel end def apply_modules(modules) diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index e93f22bede..6cd42ff936 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -77,11 +77,13 @@ class MethodScopingTest < ActiveRecord::TestCase end end - def test_options_select_replaces_scope_select - Developer.send(:with_scope, :find => { :select => "id, name" }) do + def test_scope_select_concatenates + Developer.send(:with_scope, :find => { :select => "name" }) do developer = Developer.find(:first, :select => 'id, salary', :conditions => "name = 'David'") assert_equal 80000, developer.salary - assert !developer.has_attribute?(:name) + assert developer.has_attribute?(:id) + assert developer.has_attribute?(:name) + assert developer.has_attribute?(:salary) end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index abf43cea98..5b1c6b8f22 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -491,6 +491,12 @@ class RelationTest < ActiveRecord::TestCase assert_equal 0, posts.count('comments_count') end + def test_multiple_selects + post = Post.scoped.select('comments_count').select('title').order("id ASC").first + assert_equal "Welcome to the weblog", post.title + assert_equal 2, post.comments_count + end + def test_size posts = Post.scoped -- cgit v1.2.3 From ccc8eba4dcdee9ae07788574f7cfffe1e15c2d0a Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 25 Jun 2010 12:19:03 -0700 Subject: Change the generated Gemfile to resolve, via documentation, the issue of rspec generators being unavailable in development mode --- railties/lib/rails/generators/rails/app/templates/Gemfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index 0b922a89c0..a108968b97 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -32,8 +32,9 @@ gem '<%= gem_for_database %>'<% if require_for_database %>, :require => '<%= req # gem 'sqlite3-ruby', :require => 'sqlite3' # gem 'aws-s3', :require => 'aws/s3' -# Bundle gems for certain environments: -# gem 'rspec', :group => :test -# group :test do +# Bundle gems for the local environment. Make sure to +# put test-only gems in this group so their generators +# and rake tasks are available in development mode: +# group :development, :test do # gem 'webrat' # end -- cgit v1.2.3 From efbd0eb9f7508187259208caf6b51eec206cbac9 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 16:31:42 -0300 Subject: Order is not guaranteed here, ordering by id of people table makes it work --- activerecord/test/cases/associations/cascaded_eager_loading_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 050b730dda..9c5dcc2ad9 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -41,9 +41,9 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase def test_eager_association_loading_grafts_stashed_associations_to_correct_parent assert_nothing_raised do - Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('primary_contacts_people_2.id').all + Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').all end - assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('primary_contacts_people_2.id').first + assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').first end def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations -- cgit v1.2.3 From 5e2b473b478d93d6a1aa627d688b2b2ce05fa9ef Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 25 Jun 2010 13:29:17 -0700 Subject: reorganizing adapter specific tests. [#4974 state:resolved] Signed-off-by: Jeremy Kemper --- activerecord/Rakefile | 5 +- .../test/cases/active_schema_test_mysql.rb | 124 ----------- .../test/cases/active_schema_test_postgresql.rb | 28 --- .../cases/adapters/firebird/connection_test.rb | 8 + .../test/cases/adapters/firebird/default_test.rb | 16 ++ .../test/cases/adapters/firebird/migration_test.rb | 124 +++++++++++ .../cases/adapters/mysql/active_schema_test.rb | 124 +++++++++++ .../test/cases/adapters/mysql/connection_test.rb | 64 ++++++ .../cases/adapters/mysql/reserved_word_test.rb | 176 +++++++++++++++ .../test/cases/adapters/oracle/synonym_test.rb | 17 ++ .../adapters/postgresql/active_schema_test.rb | 28 +++ .../cases/adapters/postgresql/datatype_test.rb | 243 +++++++++++++++++++++ .../postgresql/schema_authorization_test.rb | 84 +++++++ .../test/cases/adapters/postgresql/schema_test.rb | 193 ++++++++++++++++ .../test/cases/adapters/sqlite/copy_table_test.rb | 80 +++++++ .../test/cases/connection_test_firebird.rb | 8 - activerecord/test/cases/connection_test_mysql.rb | 64 ------ activerecord/test/cases/copy_table_test_sqlite.rb | 80 ------- .../test/cases/datatype_test_postgresql.rb | 243 --------------------- activerecord/test/cases/default_test_firebird.rb | 16 -- activerecord/test/cases/migration_test_firebird.rb | 124 ----------- .../test/cases/reserved_word_test_mysql.rb | 176 --------------- .../cases/schema_authorization_test_postgresql.rb | 84 ------- activerecord/test/cases/schema_test_postgresql.rb | 193 ---------------- activerecord/test/cases/synonym_test_oracle.rb | 17 -- 25 files changed, 1161 insertions(+), 1158 deletions(-) delete mode 100644 activerecord/test/cases/active_schema_test_mysql.rb delete mode 100644 activerecord/test/cases/active_schema_test_postgresql.rb create mode 100644 activerecord/test/cases/adapters/firebird/connection_test.rb create mode 100644 activerecord/test/cases/adapters/firebird/default_test.rb create mode 100644 activerecord/test/cases/adapters/firebird/migration_test.rb create mode 100644 activerecord/test/cases/adapters/mysql/active_schema_test.rb create mode 100644 activerecord/test/cases/adapters/mysql/connection_test.rb create mode 100644 activerecord/test/cases/adapters/mysql/reserved_word_test.rb create mode 100644 activerecord/test/cases/adapters/oracle/synonym_test.rb create mode 100644 activerecord/test/cases/adapters/postgresql/active_schema_test.rb create mode 100644 activerecord/test/cases/adapters/postgresql/datatype_test.rb create mode 100644 activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb create mode 100644 activerecord/test/cases/adapters/postgresql/schema_test.rb create mode 100644 activerecord/test/cases/adapters/sqlite/copy_table_test.rb delete mode 100644 activerecord/test/cases/connection_test_firebird.rb delete mode 100644 activerecord/test/cases/connection_test_mysql.rb delete mode 100644 activerecord/test/cases/copy_table_test_sqlite.rb delete mode 100644 activerecord/test/cases/datatype_test_postgresql.rb delete mode 100644 activerecord/test/cases/default_test_firebird.rb delete mode 100644 activerecord/test/cases/migration_test_firebird.rb delete mode 100644 activerecord/test/cases/reserved_word_test_mysql.rb delete mode 100644 activerecord/test/cases/schema_authorization_test_postgresql.rb delete mode 100644 activerecord/test/cases/schema_test_postgresql.rb delete mode 100644 activerecord/test/cases/synonym_test_oracle.rb diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 12e094b406..22a17a62af 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -49,7 +49,10 @@ end connection_path = "test/connections/#{adapter =~ /jdbc/ ? 'jdbc' : 'native'}_#{adapter}" adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/] t.libs << "test" << connection_path - t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort + t.test_files = (Dir.glob( "test/cases/**/*_test.rb" ).reject { + |x| x =~ /\/adapters\// + } + Dir.glob("test/cases/adapters/#{adapter_short}/**/*_test.rb")).sort + t.verbose = true t.warning = true } diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb deleted file mode 100644 index d7431e5158..0000000000 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ /dev/null @@ -1,124 +0,0 @@ -require "cases/helper" - -class ActiveSchemaTest < ActiveRecord::TestCase - def setup - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - alias_method :execute_without_stub, :execute - remove_method :execute - def execute(sql, name = nil) return sql end - end - end - - def teardown - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - remove_method :execute - alias_method :execute, :execute_without_stub - end - end - - def test_add_index - # add_index calls index_exists? which can't work since execute is stubbed - ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_exists?) do |*| - false - end - expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" - assert_equal expected, add_index(:people, :last_name, :length => nil) - - expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))" - assert_equal expected, add_index(:people, :last_name, :length => 10) - - expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))" - assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15) - - expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)" - assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15}) - - expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" - assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10}) - ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_exists?) - end - - def test_drop_table - assert_equal "DROP TABLE `people`", drop_table(:people) - end - - if current_adapter?(:MysqlAdapter) - def test_create_mysql_database_with_encoding - assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) - assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) - end - - def test_recreate_mysql_database_with_encoding - create_database(:luca, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) - end - end - - def test_add_column - assert_equal "ALTER TABLE `people` ADD `last_name` varchar(255)", add_column(:people, :last_name, :string) - end - - def test_add_column_with_limit - assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32) - end - - def test_drop_table_with_specific_database - assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people') - end - - def test_add_timestamps - with_real_execute do - begin - ActiveRecord::Base.connection.create_table :delete_me do |t| - end - ActiveRecord::Base.connection.add_timestamps :delete_me - assert column_present?('delete_me', 'updated_at', 'datetime') - assert column_present?('delete_me', 'created_at', 'datetime') - ensure - ActiveRecord::Base.connection.drop_table :delete_me rescue nil - end - end - end - - def test_remove_timestamps - with_real_execute do - begin - ActiveRecord::Base.connection.create_table :delete_me do |t| - t.timestamps - end - ActiveRecord::Base.connection.remove_timestamps :delete_me - assert !column_present?('delete_me', 'updated_at', 'datetime') - assert !column_present?('delete_me', 'created_at', 'datetime') - ensure - ActiveRecord::Base.connection.drop_table :delete_me rescue nil - end - end - end - - private - def with_real_execute - #we need to actually modify some data, so we make execute point to the original method - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - alias_method :execute_with_stub, :execute - alias_method :execute, :execute_without_stub - end - yield - ensure - #before finishing, we restore the alias to the mock-up method - ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do - remove_method :execute - alias_method :execute, :execute_with_stub - end - end - - - def method_missing(method_symbol, *arguments) - ActiveRecord::Base.connection.send(method_symbol, *arguments) - end - - def column_present?(table_name, column_name, type) - results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'") - results.first && results.first['Type'] == type - end -end diff --git a/activerecord/test/cases/active_schema_test_postgresql.rb b/activerecord/test/cases/active_schema_test_postgresql.rb deleted file mode 100644 index f106e14319..0000000000 --- a/activerecord/test/cases/active_schema_test_postgresql.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'cases/helper' - -class PostgresqlActiveSchemaTest < Test::Unit::TestCase - def setup - ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do - alias_method :real_execute, :execute - remove_method :execute - def execute(sql, name = nil) sql end - end - end - - def teardown - ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do - remove_method :execute - alias_method :execute, :real_execute - end - end - - def test_create_database_with_encoding - assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt) - assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1) - end - - private - def method_missing(method_symbol, *arguments) - ActiveRecord::Base.connection.send(method_symbol, *arguments) - end -end diff --git a/activerecord/test/cases/adapters/firebird/connection_test.rb b/activerecord/test/cases/adapters/firebird/connection_test.rb new file mode 100644 index 0000000000..f57ea686a5 --- /dev/null +++ b/activerecord/test/cases/adapters/firebird/connection_test.rb @@ -0,0 +1,8 @@ +require "cases/helper" + +class FirebirdConnectionTest < ActiveRecord::TestCase + def test_charset_properly_set + fb_conn = ActiveRecord::Base.connection.instance_variable_get(:@connection) + assert_equal 'UTF8', fb_conn.database.character_set + end +end diff --git a/activerecord/test/cases/adapters/firebird/default_test.rb b/activerecord/test/cases/adapters/firebird/default_test.rb new file mode 100644 index 0000000000..713c7e11bf --- /dev/null +++ b/activerecord/test/cases/adapters/firebird/default_test.rb @@ -0,0 +1,16 @@ +require "cases/helper" +require 'models/default' + +class DefaultTest < ActiveRecord::TestCase + def test_default_timestamp + default = Default.new + assert_instance_of(Time, default.default_timestamp) + assert_equal(:datetime, default.column_for_attribute(:default_timestamp).type) + + # Variance should be small; increase if required -- e.g., if test db is on + # remote host and clocks aren't synchronized. + t1 = Time.new + accepted_variance = 1.0 + assert_in_delta(t1.to_f, default.default_timestamp.to_f, accepted_variance) + end +end diff --git a/activerecord/test/cases/adapters/firebird/migration_test.rb b/activerecord/test/cases/adapters/firebird/migration_test.rb new file mode 100644 index 0000000000..710661b9bd --- /dev/null +++ b/activerecord/test/cases/adapters/firebird/migration_test.rb @@ -0,0 +1,124 @@ +require "cases/helper" +require 'models/course' + +class FirebirdMigrationTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + # using Course connection for tests -- need a db that doesn't already have a BOOLEAN domain + @connection = Course.connection + @fireruby_connection = @connection.instance_variable_get(:@connection) + end + + def teardown + @connection.drop_table :foo rescue nil + @connection.execute("DROP DOMAIN D_BOOLEAN") rescue nil + end + + def test_create_table_with_custom_sequence_name + assert_nothing_raised do + @connection.create_table(:foo, :sequence => 'foo_custom_seq') do |f| + f.column :bar, :string + end + end + assert !sequence_exists?('foo_seq') + assert sequence_exists?('foo_custom_seq') + + assert_nothing_raised { @connection.drop_table(:foo, :sequence => 'foo_custom_seq') } + assert !sequence_exists?('foo_custom_seq') + ensure + FireRuby::Generator.new('foo_custom_seq', @fireruby_connection).drop rescue nil + end + + def test_create_table_without_sequence + assert_nothing_raised do + @connection.create_table(:foo, :sequence => false) do |f| + f.column :bar, :string + end + end + assert !sequence_exists?('foo_seq') + assert_nothing_raised { @connection.drop_table :foo } + + assert_nothing_raised do + @connection.create_table(:foo, :id => false) do |f| + f.column :bar, :string + end + end + assert !sequence_exists?('foo_seq') + assert_nothing_raised { @connection.drop_table :foo } + end + + def test_create_table_with_boolean_column + assert !boolean_domain_exists? + assert_nothing_raised do + @connection.create_table :foo do |f| + f.column :bar, :string + f.column :baz, :boolean + end + end + assert boolean_domain_exists? + end + + def test_add_boolean_column + assert !boolean_domain_exists? + @connection.create_table :foo do |f| + f.column :bar, :string + end + + assert_nothing_raised { @connection.add_column :foo, :baz, :boolean } + assert boolean_domain_exists? + assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "baz" }.type + end + + def test_change_column_to_boolean + assert !boolean_domain_exists? + # Manually create table with a SMALLINT column, which can be changed to a BOOLEAN + @connection.execute "CREATE TABLE foo (bar SMALLINT)" + assert_equal :integer, @connection.columns(:foo).find { |c| c.name == "bar" }.type + + assert_nothing_raised { @connection.change_column :foo, :bar, :boolean } + assert boolean_domain_exists? + assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "bar" }.type + end + + def test_rename_table_with_data_and_index + @connection.create_table :foo do |f| + f.column :baz, :string, :limit => 50 + end + 100.times { |i| @connection.execute "INSERT INTO foo VALUES (GEN_ID(foo_seq, 1), 'record #{i+1}')" } + @connection.add_index :foo, :baz + + assert_nothing_raised { @connection.rename_table :foo, :bar } + assert !@connection.tables.include?("foo") + assert @connection.tables.include?("bar") + assert_equal "index_bar_on_baz", @connection.indexes("bar").first.name + assert_equal 100, FireRuby::Generator.new("bar_seq", @fireruby_connection).last + assert_equal 100, @connection.select_one("SELECT COUNT(*) FROM bar")["count"] + ensure + @connection.drop_table :bar rescue nil + end + + def test_renaming_table_with_fk_constraint_raises_error + @connection.create_table :parent do |p| + p.column :name, :string + end + @connection.create_table :child do |c| + c.column :parent_id, :integer + end + @connection.execute "ALTER TABLE child ADD CONSTRAINT fk_child_parent FOREIGN KEY(parent_id) REFERENCES parent(id)" + assert_raise(ActiveRecord::ActiveRecordError) { @connection.rename_table :child, :descendant } + ensure + @connection.drop_table :child rescue nil + @connection.drop_table :descendant rescue nil + @connection.drop_table :parent rescue nil + end + + private + def boolean_domain_exists? + !@connection.select_one("SELECT 1 FROM rdb$fields WHERE rdb$field_name = 'D_BOOLEAN'").nil? + end + + def sequence_exists?(sequence_name) + FireRuby::Generator.exists?(sequence_name, @fireruby_connection) + end +end diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb new file mode 100644 index 0000000000..d7431e5158 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -0,0 +1,124 @@ +require "cases/helper" + +class ActiveSchemaTest < ActiveRecord::TestCase + def setup + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + alias_method :execute_without_stub, :execute + remove_method :execute + def execute(sql, name = nil) return sql end + end + end + + def teardown + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + remove_method :execute + alias_method :execute, :execute_without_stub + end + end + + def test_add_index + # add_index calls index_exists? which can't work since execute is stubbed + ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_exists?) do |*| + false + end + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" + assert_equal expected, add_index(:people, :last_name, :length => nil) + + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))" + assert_equal expected, add_index(:people, :last_name, :length => 10) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15}) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10}) + ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_exists?) + end + + def test_drop_table + assert_equal "DROP TABLE `people`", drop_table(:people) + end + + if current_adapter?(:MysqlAdapter) + def test_create_mysql_database_with_encoding + assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) + assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) + end + + def test_recreate_mysql_database_with_encoding + create_database(:luca, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) + end + end + + def test_add_column + assert_equal "ALTER TABLE `people` ADD `last_name` varchar(255)", add_column(:people, :last_name, :string) + end + + def test_add_column_with_limit + assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32) + end + + def test_drop_table_with_specific_database + assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people') + end + + def test_add_timestamps + with_real_execute do + begin + ActiveRecord::Base.connection.create_table :delete_me do |t| + end + ActiveRecord::Base.connection.add_timestamps :delete_me + assert column_present?('delete_me', 'updated_at', 'datetime') + assert column_present?('delete_me', 'created_at', 'datetime') + ensure + ActiveRecord::Base.connection.drop_table :delete_me rescue nil + end + end + end + + def test_remove_timestamps + with_real_execute do + begin + ActiveRecord::Base.connection.create_table :delete_me do |t| + t.timestamps + end + ActiveRecord::Base.connection.remove_timestamps :delete_me + assert !column_present?('delete_me', 'updated_at', 'datetime') + assert !column_present?('delete_me', 'created_at', 'datetime') + ensure + ActiveRecord::Base.connection.drop_table :delete_me rescue nil + end + end + end + + private + def with_real_execute + #we need to actually modify some data, so we make execute point to the original method + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + alias_method :execute_with_stub, :execute + alias_method :execute, :execute_without_stub + end + yield + ensure + #before finishing, we restore the alias to the mock-up method + ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do + remove_method :execute + alias_method :execute, :execute_with_stub + end + end + + + def method_missing(method_symbol, *arguments) + ActiveRecord::Base.connection.send(method_symbol, *arguments) + end + + def column_present?(table_name, column_name, type) + results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'") + results.first && results.first['Type'] == type + end +end diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb new file mode 100644 index 0000000000..8e4842a1b6 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -0,0 +1,64 @@ +require "cases/helper" + +class MysqlConnectionTest < ActiveRecord::TestCase + def setup + super + @connection = ActiveRecord::Base.connection + end + + def test_mysql_reconnect_attribute_after_connection_with_reconnect_true + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true})) + assert ActiveRecord::Base.connection.raw_connection.reconnect + end + end + + def test_mysql_reconnect_attribute_after_connection_with_reconnect_false + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false})) + assert !ActiveRecord::Base.connection.raw_connection.reconnect + end + end + + def test_no_automatic_reconnection_after_timeout + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + assert !@connection.active? + end + + def test_successful_reconnection_after_timeout_with_manual_reconnect + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + @connection.reconnect! + assert @connection.active? + end + + def test_successful_reconnection_after_timeout_with_verify + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + @connection.verify! + assert @connection.active? + end + + # Test that MySQL allows multiple results for stored procedures + if Mysql.const_defined?(:CLIENT_MULTI_RESULTS) + def test_multi_results + rows = ActiveRecord::Base.connection.select_rows('CALL ten();') + assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" + end + end + + private + + def run_without_connection + original_connection = ActiveRecord::Base.remove_connection + begin + yield original_connection + ensure + ActiveRecord::Base.establish_connection(original_connection) + end + end +end diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb new file mode 100644 index 0000000000..90d8b0d923 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb @@ -0,0 +1,176 @@ +require "cases/helper" + +class Group < ActiveRecord::Base + Group.table_name = 'group' + belongs_to :select, :class_name => 'Select' + has_one :values +end + +class Select < ActiveRecord::Base + Select.table_name = 'select' + has_many :groups +end + +class Values < ActiveRecord::Base + Values.table_name = 'values' +end + +class Distinct < ActiveRecord::Base + Distinct.table_name = 'distinct' + has_and_belongs_to_many :selects + has_many :values, :through => :groups +end + +# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with +# reserved word names (ie: group, order, values, etc...) +class MysqlReservedWordTest < ActiveRecord::TestCase + def setup + @connection = ActiveRecord::Base.connection + + # we call execute directly here (and do similar below) because ActiveRecord::Base#create_table() + # will fail with these table names if these test cases fail + + create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int', + 'select'=>'id int auto_increment primary key', + 'values'=>'id int auto_increment primary key, group_id int', + 'distinct'=>'id int auto_increment primary key', + 'distincts_selects'=>'distinct_id int, select_id int' + end + + def teardown + drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order'] + end + + # create tables with reserved-word names and columns + def test_create_tables + assert_nothing_raised { + @connection.create_table :order do |t| + t.column :group, :string + end + } + end + + # rename tables with reserved-word names + def test_rename_tables + assert_nothing_raised { @connection.rename_table(:group, :order) } + end + + # alter column with a reserved-word name in a table with a reserved-word name + def test_change_columns + assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') } + #the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter + assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) } + assert_nothing_raised { @connection.rename_column(:group, :order, :values) } + end + + # dump structure of table with reserved word name + def test_structure_dump + assert_nothing_raised { @connection.structure_dump } + end + + # introspect table with reserved word name + def test_introspect + assert_nothing_raised { @connection.columns(:group) } + assert_nothing_raised { @connection.indexes(:group) } + end + + #fixtures + self.use_instantiated_fixtures = true + self.use_transactional_fixtures = false + + #fixtures :group + + def test_fixtures + f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + + assert_nothing_raised { + f.each do |x| + x.delete_existing_fixtures + end + } + + assert_nothing_raised { + f.each do |x| + x.insert_fixtures + end + } + end + + #activerecord model class with reserved-word table name + def test_activerecord_model + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + x = nil + assert_nothing_raised { x = Group.new } + x.order = 'x' + assert_nothing_raised { x.save } + x.order = 'y' + assert_nothing_raised { x.save } + assert_nothing_raised { y = Group.find_by_order('y') } + assert_nothing_raised { y = Group.find(1) } + x = Group.find(1) + end + + # has_one association with reserved-word table name + def test_has_one_associations + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + v = nil + assert_nothing_raised { v = Group.find(1).values } + assert_equal 2, v.id + end + + # belongs_to association with reserved-word table name + def test_belongs_to_associations + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + gs = nil + assert_nothing_raised { gs = Select.find(2).groups } + assert_equal gs.length, 2 + assert(gs.collect{|x| x.id}.sort == [2, 3]) + end + + # has_and_belongs_to_many with reserved-word table name + def test_has_and_belongs_to_many + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + s = nil + assert_nothing_raised { s = Distinct.find(1).selects } + assert_equal s.length, 2 + assert(s.collect{|x|x.id}.sort == [1, 2]) + end + + # activerecord model introspection with reserved-word table and column names + def test_activerecord_introspection + assert_nothing_raised { Group.table_exists? } + assert_nothing_raised { Group.columns } + end + + # Calculations + def test_calculations_work_with_reserved_words + assert_nothing_raised { Group.count } + end + + def test_associations_work_with_reserved_words + assert_nothing_raised { Select.find(:all, :include => [:groups]) } + end + + #the following functions were added to DRY test cases + + private + # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path + def create_test_fixtures(*fixture_names) + Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) + end + + # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name + def drop_tables_directly(table_names, connection = @connection) + table_names.each do |name| + connection.execute("DROP TABLE IF EXISTS `#{name}`") + end + end + + # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns + def create_tables_directly (tables, connection = @connection) + tables.each do |table_name, column_properties| + connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )") + end + end + +end diff --git a/activerecord/test/cases/adapters/oracle/synonym_test.rb b/activerecord/test/cases/adapters/oracle/synonym_test.rb new file mode 100644 index 0000000000..b9a422a6ca --- /dev/null +++ b/activerecord/test/cases/adapters/oracle/synonym_test.rb @@ -0,0 +1,17 @@ +require "cases/helper" +require 'models/topic' +require 'models/subject' + +# confirm that synonyms work just like tables; in this case +# the "subjects" table in Oracle (defined in oci.sql) is just +# a synonym to the "topics" table + +class TestOracleSynonym < ActiveRecord::TestCase + + def test_oracle_synonym + topic = Topic.new + subject = Subject.new + assert_equal(topic.attributes, subject.attributes) + end + +end diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb new file mode 100644 index 0000000000..f106e14319 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb @@ -0,0 +1,28 @@ +require 'cases/helper' + +class PostgresqlActiveSchemaTest < Test::Unit::TestCase + def setup + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do + alias_method :real_execute, :execute + remove_method :execute + def execute(sql, name = nil) sql end + end + end + + def teardown + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do + remove_method :execute + alias_method :execute, :real_execute + end + end + + def test_create_database_with_encoding + assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt) + assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1) + end + + private + def method_missing(method_symbol, *arguments) + ActiveRecord::Base.connection.send(method_symbol, *arguments) + end +end diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb new file mode 100644 index 0000000000..5bb8fa2f93 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -0,0 +1,243 @@ +require "cases/helper" + +class PostgresqlArray < ActiveRecord::Base +end + +class PostgresqlMoney < ActiveRecord::Base +end + +class PostgresqlNumber < ActiveRecord::Base +end + +class PostgresqlTime < ActiveRecord::Base +end + +class PostgresqlNetworkAddress < ActiveRecord::Base +end + +class PostgresqlBitString < ActiveRecord::Base +end + +class PostgresqlOid < ActiveRecord::Base +end + +class PostgresqlTimestampWithZone < ActiveRecord::Base +end + +class PostgresqlDataTypeTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @connection = ActiveRecord::Base.connection + @connection.execute("set lc_monetary = 'C'") + + @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )") + @first_array = PostgresqlArray.find(1) + + @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('567.89'::money)") + @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-567.89'::money)") + @first_money = PostgresqlMoney.find(1) + @second_money = PostgresqlMoney.find(2) + + @connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)") + @first_number = PostgresqlNumber.find(1) + + @connection.execute("INSERT INTO postgresql_times (time_interval) VALUES ('1 year 2 days ago')") + @first_time = PostgresqlTime.find(1) + + @connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')") + @first_network_address = PostgresqlNetworkAddress.find(1) + + @connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')") + @first_bit_string = PostgresqlBitString.find(1) + + @connection.execute("INSERT INTO postgresql_oids (obj_id) VALUES (1234)") + @first_oid = PostgresqlOid.find(1) + + @connection.execute("INSERT INTO postgresql_timestamp_with_zones (time) VALUES ('2010-01-01 10:00:00-1')") + end + + def test_data_type_of_array_types + assert_equal :string, @first_array.column_for_attribute(:commission_by_quarter).type + assert_equal :string, @first_array.column_for_attribute(:nicknames).type + end + + def test_data_type_of_money_types + assert_equal :decimal, @first_money.column_for_attribute(:wealth).type + 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 + end + + def test_data_type_of_time_types + assert_equal :string, @first_time.column_for_attribute(:time_interval).type + end + + def test_data_type_of_network_address_types + assert_equal :string, @first_network_address.column_for_attribute(:cidr_address).type + assert_equal :string, @first_network_address.column_for_attribute(:inet_address).type + assert_equal :string, @first_network_address.column_for_attribute(:mac_address).type + end + + def test_data_type_of_bit_string_types + assert_equal :string, @first_bit_string.column_for_attribute(:bit_string).type + assert_equal :string, @first_bit_string.column_for_attribute(:bit_string_varying).type + end + + def test_data_type_of_oid_types + assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type + end + + def test_array_values + assert_equal '{35000,21000,18000,17000}', @first_array.commission_by_quarter + assert_equal '{foo,bar,baz}', @first_array.nicknames + end + + def test_money_values + assert_equal 567.89, @first_money.wealth + assert_equal(-567.89, @second_money.wealth) + end + + def test_number_values + assert_equal 123.456, @first_number.single + assert_equal 123456.789, @first_number.double + end + + def test_time_values + assert_equal '-1 years -2 days', @first_time.time_interval + end + + def test_network_address_values + assert_equal '192.168.0.0/24', @first_network_address.cidr_address + assert_equal '172.16.1.254', @first_network_address.inet_address + assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address + end + + def test_bit_string_values + assert_equal '00010101', @first_bit_string.bit_string + assert_equal '00010101', @first_bit_string.bit_string_varying + end + + def test_oid_values + assert_equal 1234, @first_oid.obj_id + end + + def test_update_integer_array + new_value = '{32800,95000,29350,17000}' + assert @first_array.commission_by_quarter = new_value + assert @first_array.save + assert @first_array.reload + assert_equal @first_array.commission_by_quarter, new_value + assert @first_array.commission_by_quarter = new_value + assert @first_array.save + assert @first_array.reload + assert_equal @first_array.commission_by_quarter, new_value + end + + def test_update_text_array + new_value = '{robby,robert,rob,robbie}' + assert @first_array.nicknames = new_value + assert @first_array.save + assert @first_array.reload + assert_equal @first_array.nicknames, new_value + assert @first_array.nicknames = new_value + assert @first_array.save + assert @first_array.reload + assert_equal @first_array.nicknames, new_value + end + + def test_update_money + new_value = BigDecimal.new('123.45') + assert @first_money.wealth = new_value + assert @first_money.save + assert @first_money.reload + assert_equal new_value, @first_money.wealth + end + + def test_update_number + new_single = 789.012 + new_double = 789012.345 + assert @first_number.single = new_single + assert @first_number.double = new_double + assert @first_number.save + assert @first_number.reload + assert_equal @first_number.single, new_single + assert_equal @first_number.double, new_double + end + + def test_update_time + assert @first_time.time_interval = '2 years 3 minutes' + assert @first_time.save + assert @first_time.reload + assert_equal @first_time.time_interval, '2 years 00:03:00' + end + + def test_update_network_address + new_cidr_address = '10.1.2.3/32' + new_inet_address = '10.0.0.0/8' + new_mac_address = 'bc:de:f0:12:34:56' + assert @first_network_address.cidr_address = new_cidr_address + assert @first_network_address.inet_address = new_inet_address + assert @first_network_address.mac_address = new_mac_address + assert @first_network_address.save + assert @first_network_address.reload + assert_equal @first_network_address.cidr_address, new_cidr_address + assert_equal @first_network_address.inet_address, new_inet_address + assert_equal @first_network_address.mac_address, new_mac_address + end + + def test_update_bit_string + new_bit_string = '11111111' + new_bit_string_varying = 'FF' + assert @first_bit_string.bit_string = new_bit_string + assert @first_bit_string.bit_string_varying = new_bit_string_varying + assert @first_bit_string.save + assert @first_bit_string.reload + assert_equal @first_bit_string.bit_string, new_bit_string + assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying + end + + def test_update_oid + new_value = 567890 + assert @first_oid.obj_id = new_value + assert @first_oid.save + assert @first_oid.reload + assert_equal @first_oid.obj_id, new_value + end + + def test_timestamp_with_zone_values_with_rails_time_zone_support + old_tz = ActiveRecord::Base.time_zone_aware_attributes + old_default_tz = ActiveRecord::Base.default_timezone + + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + + @connection.reconnect! + + @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) + assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time + ensure + ActiveRecord::Base.default_timezone = old_default_tz + ActiveRecord::Base.time_zone_aware_attributes = old_tz + @connection.reconnect! + end + + def test_timestamp_with_zone_values_without_rails_time_zone_support + old_tz = ActiveRecord::Base.time_zone_aware_attributes + old_default_tz = ActiveRecord::Base.default_timezone + + ActiveRecord::Base.time_zone_aware_attributes = false + ActiveRecord::Base.default_timezone = :local + + @connection.reconnect! + + @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) + assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time + ensure + ActiveRecord::Base.default_timezone = old_default_tz + ActiveRecord::Base.time_zone_aware_attributes = old_tz + @connection.reconnect! + end +end diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb new file mode 100644 index 0000000000..6f372edc38 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb @@ -0,0 +1,84 @@ +require "cases/helper" + +class SchemaThing < ActiveRecord::Base +end + +class SchemaAuthorizationTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + TABLE_NAME = 'schema_things' + COLUMNS = [ + 'id serial primary key', + 'name character varying(50)' + ] + USERS = ['rails_pg_schema_user1', 'rails_pg_schema_user2'] + + def setup + @connection = ActiveRecord::Base.connection + @connection.execute "SET search_path TO '$user',public" + set_session_auth + USERS.each do |u| + @connection.execute "CREATE USER #{u}" rescue nil + @connection.execute "CREATE SCHEMA AUTHORIZATION #{u}" rescue nil + set_session_auth u + @connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" + @connection.execute "INSERT INTO #{TABLE_NAME} (name) VALUES ('#{u}')" + set_session_auth + end + end + + def teardown + set_session_auth + @connection.execute "RESET search_path" + USERS.each do |u| + @connection.execute "DROP SCHEMA #{u} CASCADE" + @connection.execute "DROP USER #{u}" + end + end + + def test_schema_invisible + assert_raise(ActiveRecord::StatementInvalid) do + set_session_auth + @connection.execute "SELECT * FROM #{TABLE_NAME}" + end + end + + def test_schema_uniqueness + assert_nothing_raised do + set_session_auth + USERS.each do |u| + set_session_auth u + assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = 1") + set_session_auth + end + end + end + + def test_sequence_schema_caching + assert_nothing_raised do + USERS.each do |u| + set_session_auth u + st = SchemaThing.new :name => 'TEST1' + st.save! + st = SchemaThing.new :id => 5, :name => 'TEST2' + st.save! + set_session_auth + end + end + end + + def test_tables_in_current_schemas + assert !@connection.tables.include?(TABLE_NAME) + USERS.each do |u| + set_session_auth u + assert @connection.tables.include?(TABLE_NAME) + set_session_auth + end + end + + private + def set_session_auth auth = nil + @connection.execute "SET SESSION AUTHORIZATION #{auth || 'default'}" + end + +end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb new file mode 100644 index 0000000000..3ed9b1974d --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -0,0 +1,193 @@ +require "cases/helper" + +class SchemaTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + SCHEMA_NAME = 'test_schema' + SCHEMA2_NAME = 'test_schema2' + TABLE_NAME = 'things' + CAPITALIZED_TABLE_NAME = 'Things' + INDEX_A_NAME = 'a_index_things_on_name' + INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema' + INDEX_A_COLUMN = 'name' + INDEX_B_COLUMN_S1 = 'email' + INDEX_B_COLUMN_S2 = 'moment' + COLUMNS = [ + 'id integer', + 'name character varying(50)', + 'email character varying(50)', + 'moment timestamp without time zone default now()' + ] + + class Thing1 < ActiveRecord::Base + set_table_name "test_schema.things" + end + + class Thing2 < ActiveRecord::Base + set_table_name "test_schema2.things" + end + + class Thing3 < ActiveRecord::Base + set_table_name 'test_schema."things.table"' + end + + class Thing4 < ActiveRecord::Base + set_table_name 'test_schema."Things"' + end + + def setup + @connection = ActiveRecord::Base.connection + @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" + @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})" + @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})" + @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" + @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" + @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" + @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S1});" + @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S2});" + end + + def teardown + @connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE" + @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE" + end + + def test_table_exists? + [Thing1, Thing2, Thing3, Thing4].each do |klass| + name = klass.table_name + assert @connection.table_exists?(name), "'#{name}' table should exist" + end + end + + def test_table_exists_wrong_schema + assert(!@connection.table_exists?("foo.things"), "table should not exist") + end + + def test_table_exists_quoted_table + assert(@connection.table_exists?('"things.table"'), "table should exist") + end + + def test_with_schema_prefixed_table_name + assert_nothing_raised do + assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}") + end + end + + def test_with_schema_prefixed_capitalized_table_name + assert_nothing_raised do + assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}") + end + end + + def test_with_schema_search_path + assert_nothing_raised do + with_schema_search_path(SCHEMA_NAME) do + assert_equal COLUMNS, columns(TABLE_NAME) + end + end + end + + + def test_proper_encoding_of_table_name + assert_equal '"table_name"', @connection.quote_table_name('table_name') + assert_equal '"table.name"', @connection.quote_table_name('"table.name"') + assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name') + assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"') + assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name') + assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"') + end + + def test_classes_with_qualified_schema_name + assert_equal 0, Thing1.count + assert_equal 0, Thing2.count + assert_equal 0, Thing3.count + assert_equal 0, Thing4.count + + Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 0, Thing2.count + assert_equal 0, Thing3.count + assert_equal 0, Thing4.count + + Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 0, Thing3.count + assert_equal 0, Thing4.count + + Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 1, Thing3.count + assert_equal 0, Thing4.count + + Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 1, Thing3.count + assert_equal 1, Thing4.count + end + + def test_raise_on_unquoted_schema_name + assert_raise(ActiveRecord::StatementInvalid) do + with_schema_search_path '$user,public' + end + end + + def test_without_schema_search_path + assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) } + end + + def test_ignore_nil_schema_search_path + assert_nothing_raised { with_schema_search_path nil } + end + + def test_dump_indexes_for_schema_one + do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1) + end + + def test_dump_indexes_for_schema_two + do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2) + end + + def test_with_uppercase_index_name + ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" + assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"} + + ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" + ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME + assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "things_Index"} + ActiveRecord::Base.connection.schema_search_path = "public" + end + + private + def columns(table_name) + @connection.send(:column_definitions, table_name).map do |name, type, default| + "#{name} #{type}" + (default ? " default #{default}" : '') + end + end + + def with_schema_search_path(schema_search_path) + @connection.schema_search_path = schema_search_path + yield if block_given? + ensure + @connection.schema_search_path = "'$user', public" + end + + def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name) + with_schema_search_path(this_schema_name) do + indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name} + assert_equal 2,indexes.size + + do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name) + do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name) + end + end + + def do_dump_index_assertions_for_one_index(this_index, this_index_name, this_index_column) + assert_equal TABLE_NAME, this_index.table + assert_equal 1, this_index.columns.size + assert_equal this_index_column, this_index.columns[0] + assert_equal this_index_name, this_index.name + end +end diff --git a/activerecord/test/cases/adapters/sqlite/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite/copy_table_test.rb new file mode 100644 index 0000000000..575b4806c1 --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite/copy_table_test.rb @@ -0,0 +1,80 @@ +require "cases/helper" + +class CopyTableTest < ActiveRecord::TestCase + fixtures :customers, :companies, :comments + + def setup + @connection = ActiveRecord::Base.connection + class << @connection + public :copy_table, :table_structure, :indexes + end + end + + def test_copy_table(from = 'customers', to = 'customers2', options = {}) + assert_nothing_raised {copy_table(from, to, options)} + assert_equal row_count(from), row_count(to) + + if block_given? + yield from, to, options + else + assert_equal column_names(from), column_names(to) + end + + @connection.drop_table(to) rescue nil + end + + def test_copy_table_renaming_column + test_copy_table('customers', 'customers2', + :rename => {'name' => 'person_name'}) do |from, to, options| + expected = column_values(from, 'name') + assert_equal expected, column_values(to, 'person_name') + assert expected.any?, "No values in table: #{expected.inspect}" + end + end + + def test_copy_table_with_index + test_copy_table('comments', 'comments_with_index') do + @connection.add_index('comments_with_index', ['post_id', 'type']) + test_copy_table('comments_with_index', 'comments_with_index2') do + assert_equal table_indexes_without_name('comments_with_index'), + table_indexes_without_name('comments_with_index2') + end + end + end + + def test_copy_table_without_primary_key + test_copy_table('developers_projects', 'programmers_projects') + end + + def test_copy_table_with_id_col_that_is_not_primary_key + test_copy_table('goofy_string_id', 'goofy_string_id2') do |from, to, options| + original_id = @connection.columns('goofy_string_id').detect{|col| col.name == 'id' } + copied_id = @connection.columns('goofy_string_id2').detect{|col| col.name == 'id' } + assert_equal original_id.type, copied_id.type + assert_equal original_id.sql_type, copied_id.sql_type + assert_equal original_id.limit, copied_id.limit + assert_equal original_id.primary, copied_id.primary + end + end + +protected + def copy_table(from, to, options = {}) + @connection.copy_table(from, to, {:temporary => true}.merge(options)) + end + + def column_names(table) + @connection.table_structure(table).map {|column| column['name']} + end + + def column_values(table, column) + @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]} + end + + def table_indexes_without_name(table) + @connection.indexes('comments_with_index').delete(:name) + end + + def row_count(table) + @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count'] + end +end diff --git a/activerecord/test/cases/connection_test_firebird.rb b/activerecord/test/cases/connection_test_firebird.rb deleted file mode 100644 index f57ea686a5..0000000000 --- a/activerecord/test/cases/connection_test_firebird.rb +++ /dev/null @@ -1,8 +0,0 @@ -require "cases/helper" - -class FirebirdConnectionTest < ActiveRecord::TestCase - def test_charset_properly_set - fb_conn = ActiveRecord::Base.connection.instance_variable_get(:@connection) - assert_equal 'UTF8', fb_conn.database.character_set - end -end diff --git a/activerecord/test/cases/connection_test_mysql.rb b/activerecord/test/cases/connection_test_mysql.rb deleted file mode 100644 index 8e4842a1b6..0000000000 --- a/activerecord/test/cases/connection_test_mysql.rb +++ /dev/null @@ -1,64 +0,0 @@ -require "cases/helper" - -class MysqlConnectionTest < ActiveRecord::TestCase - def setup - super - @connection = ActiveRecord::Base.connection - end - - def test_mysql_reconnect_attribute_after_connection_with_reconnect_true - run_without_connection do |orig_connection| - ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true})) - assert ActiveRecord::Base.connection.raw_connection.reconnect - end - end - - def test_mysql_reconnect_attribute_after_connection_with_reconnect_false - run_without_connection do |orig_connection| - ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false})) - assert !ActiveRecord::Base.connection.raw_connection.reconnect - end - end - - def test_no_automatic_reconnection_after_timeout - assert @connection.active? - @connection.update('set @@wait_timeout=1') - sleep 2 - assert !@connection.active? - end - - def test_successful_reconnection_after_timeout_with_manual_reconnect - assert @connection.active? - @connection.update('set @@wait_timeout=1') - sleep 2 - @connection.reconnect! - assert @connection.active? - end - - def test_successful_reconnection_after_timeout_with_verify - assert @connection.active? - @connection.update('set @@wait_timeout=1') - sleep 2 - @connection.verify! - assert @connection.active? - end - - # Test that MySQL allows multiple results for stored procedures - if Mysql.const_defined?(:CLIENT_MULTI_RESULTS) - def test_multi_results - rows = ActiveRecord::Base.connection.select_rows('CALL ten();') - assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" - end - end - - private - - def run_without_connection - original_connection = ActiveRecord::Base.remove_connection - begin - yield original_connection - ensure - ActiveRecord::Base.establish_connection(original_connection) - end - end -end diff --git a/activerecord/test/cases/copy_table_test_sqlite.rb b/activerecord/test/cases/copy_table_test_sqlite.rb deleted file mode 100644 index 575b4806c1..0000000000 --- a/activerecord/test/cases/copy_table_test_sqlite.rb +++ /dev/null @@ -1,80 +0,0 @@ -require "cases/helper" - -class CopyTableTest < ActiveRecord::TestCase - fixtures :customers, :companies, :comments - - def setup - @connection = ActiveRecord::Base.connection - class << @connection - public :copy_table, :table_structure, :indexes - end - end - - def test_copy_table(from = 'customers', to = 'customers2', options = {}) - assert_nothing_raised {copy_table(from, to, options)} - assert_equal row_count(from), row_count(to) - - if block_given? - yield from, to, options - else - assert_equal column_names(from), column_names(to) - end - - @connection.drop_table(to) rescue nil - end - - def test_copy_table_renaming_column - test_copy_table('customers', 'customers2', - :rename => {'name' => 'person_name'}) do |from, to, options| - expected = column_values(from, 'name') - assert_equal expected, column_values(to, 'person_name') - assert expected.any?, "No values in table: #{expected.inspect}" - end - end - - def test_copy_table_with_index - test_copy_table('comments', 'comments_with_index') do - @connection.add_index('comments_with_index', ['post_id', 'type']) - test_copy_table('comments_with_index', 'comments_with_index2') do - assert_equal table_indexes_without_name('comments_with_index'), - table_indexes_without_name('comments_with_index2') - end - end - end - - def test_copy_table_without_primary_key - test_copy_table('developers_projects', 'programmers_projects') - end - - def test_copy_table_with_id_col_that_is_not_primary_key - test_copy_table('goofy_string_id', 'goofy_string_id2') do |from, to, options| - original_id = @connection.columns('goofy_string_id').detect{|col| col.name == 'id' } - copied_id = @connection.columns('goofy_string_id2').detect{|col| col.name == 'id' } - assert_equal original_id.type, copied_id.type - assert_equal original_id.sql_type, copied_id.sql_type - assert_equal original_id.limit, copied_id.limit - assert_equal original_id.primary, copied_id.primary - end - end - -protected - def copy_table(from, to, options = {}) - @connection.copy_table(from, to, {:temporary => true}.merge(options)) - end - - def column_names(table) - @connection.table_structure(table).map {|column| column['name']} - end - - def column_values(table, column) - @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]} - end - - def table_indexes_without_name(table) - @connection.indexes('comments_with_index').delete(:name) - end - - def row_count(table) - @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count'] - end -end diff --git a/activerecord/test/cases/datatype_test_postgresql.rb b/activerecord/test/cases/datatype_test_postgresql.rb deleted file mode 100644 index 3c2d9fb7bd..0000000000 --- a/activerecord/test/cases/datatype_test_postgresql.rb +++ /dev/null @@ -1,243 +0,0 @@ -require "cases/helper" - -class PostgresqlArray < ActiveRecord::Base -end - -class PostgresqlMoney < ActiveRecord::Base -end - -class PostgresqlNumber < ActiveRecord::Base -end - -class PostgresqlTime < ActiveRecord::Base -end - -class PostgresqlNetworkAddress < ActiveRecord::Base -end - -class PostgresqlBitString < ActiveRecord::Base -end - -class PostgresqlOid < ActiveRecord::Base -end - -class PostgresqlTimestampWithZone < ActiveRecord::Base -end - -class PostgresqlDataTypeTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - def setup - @connection = ActiveRecord::Base.connection - @connection.execute("set lc_monetary = 'C'") - - @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )") - @first_array = PostgresqlArray.find(1) - - @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('567.89'::money)") - @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-567.89'::money)") - @first_money = PostgresqlMoney.find(1) - @second_money = PostgresqlMoney.find(2) - - @connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)") - @first_number = PostgresqlNumber.find(1) - - @connection.execute("INSERT INTO postgresql_times (time_interval) VALUES ('1 year 2 days ago')") - @first_time = PostgresqlTime.find(1) - - @connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')") - @first_network_address = PostgresqlNetworkAddress.find(1) - - @connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')") - @first_bit_string = PostgresqlBitString.find(1) - - @connection.execute("INSERT INTO postgresql_oids (obj_id) VALUES (1234)") - @first_oid = PostgresqlOid.find(1) - - @connection.execute("INSERT INTO postgresql_timestamp_with_zones (time) VALUES ('2010-01-01 10:00:00-1')") - end - - def test_data_type_of_array_types - assert_equal :string, @first_array.column_for_attribute(:commission_by_quarter).type - assert_equal :string, @first_array.column_for_attribute(:nicknames).type - end - - def test_data_type_of_money_types - assert_equal :decimal, @first_money.column_for_attribute(:wealth).type - 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 - end - - def test_data_type_of_time_types - assert_equal :string, @first_time.column_for_attribute(:time_interval).type - end - - def test_data_type_of_network_address_types - assert_equal :string, @first_network_address.column_for_attribute(:cidr_address).type - assert_equal :string, @first_network_address.column_for_attribute(:inet_address).type - assert_equal :string, @first_network_address.column_for_attribute(:mac_address).type - end - - def test_data_type_of_bit_string_types - assert_equal :string, @first_bit_string.column_for_attribute(:bit_string).type - assert_equal :string, @first_bit_string.column_for_attribute(:bit_string_varying).type - end - - def test_data_type_of_oid_types - assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type - end - - def test_array_values - assert_equal '{35000,21000,18000,17000}', @first_array.commission_by_quarter - assert_equal '{foo,bar,baz}', @first_array.nicknames - end - - def test_money_values - assert_equal 567.89, @first_money.wealth - assert_equal(-567.89, @second_money.wealth) - end - - def test_number_values - assert_equal 123.456, @first_number.single - assert_equal 123456.789, @first_number.double - end - - def test_time_values - assert_equal '-1 years -2 days', @first_time.time_interval - end - - def test_network_address_values - assert_equal '192.168.0.0/24', @first_network_address.cidr_address - assert_equal '172.16.1.254', @first_network_address.inet_address - assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address - end - - def test_bit_string_values - assert_equal '00010101', @first_bit_string.bit_string - assert_equal '00010101', @first_bit_string.bit_string_varying - end - - def test_oid_values - assert_equal 1234, @first_oid.obj_id - end - - def test_update_integer_array - new_value = '{32800,95000,29350,17000}' - assert @first_array.commission_by_quarter = new_value - assert @first_array.save - assert @first_array.reload - assert_equal @first_array.commission_by_quarter, new_value - assert @first_array.commission_by_quarter = new_value - assert @first_array.save - assert @first_array.reload - assert_equal @first_array.commission_by_quarter, new_value - end - - def test_update_text_array - new_value = '{robby,robert,rob,robbie}' - assert @first_array.nicknames = new_value - assert @first_array.save - assert @first_array.reload - assert_equal @first_array.nicknames, new_value - assert @first_array.nicknames = new_value - assert @first_array.save - assert @first_array.reload - assert_equal @first_array.nicknames, new_value - end - - def test_update_money - new_value = BigDecimal.new('123.45') - assert @first_money.wealth = new_value - assert @first_money.save - assert @first_money.reload - assert_equal new_value, @first_money.wealth - end - - def test_update_number - new_single = 789.012 - new_double = 789012.345 - assert @first_number.single = new_single - assert @first_number.double = new_double - assert @first_number.save - assert @first_number.reload - assert_equal @first_number.single, new_single - assert_equal @first_number.double, new_double - end - - def test_update_time - assert @first_time.time_interval = '2 years 3 minutes' - assert @first_time.save - assert @first_time.reload - assert_equal @first_time.time_interval, '2 years 00:03:00' - end - - def test_update_network_address - new_cidr_address = '10.1.2.3/32' - new_inet_address = '10.0.0.0/8' - new_mac_address = 'bc:de:f0:12:34:56' - assert @first_network_address.cidr_address = new_cidr_address - assert @first_network_address.inet_address = new_inet_address - assert @first_network_address.mac_address = new_mac_address - assert @first_network_address.save - assert @first_network_address.reload - assert_equal @first_network_address.cidr_address, new_cidr_address - assert_equal @first_network_address.inet_address, new_inet_address - assert_equal @first_network_address.mac_address, new_mac_address - end - - def test_update_bit_string - new_bit_string = '11111111' - new_bit_string_varying = 'FF' - assert @first_bit_string.bit_string = new_bit_string - assert @first_bit_string.bit_string_varying = new_bit_string_varying - assert @first_bit_string.save - assert @first_bit_string.reload - assert_equal @first_bit_string.bit_string, new_bit_string - assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying - end - - def test_update_oid - new_value = 567890 - assert @first_oid.obj_id = new_value - assert @first_oid.save - assert @first_oid.reload - assert_equal @first_oid.obj_id, new_value - end - - def test_timestamp_with_zone_values_with_rails_time_zone_support - old_tz = ActiveRecord::Base.time_zone_aware_attributes - old_default_tz = ActiveRecord::Base.default_timezone - - ActiveRecord::Base.time_zone_aware_attributes = true - ActiveRecord::Base.default_timezone = :utc - - @connection.reconnect! - - @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) - assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time - ensure - ActiveRecord::Base.default_timezone = old_default_tz - ActiveRecord::Base.time_zone_aware_attributes = old_tz - @connection.reconnect! - end - - def test_timestamp_with_zone_values_without_rails_time_zone_support - old_tz = ActiveRecord::Base.time_zone_aware_attributes - old_default_tz = ActiveRecord::Base.default_timezone - - ActiveRecord::Base.time_zone_aware_attributes = false - ActiveRecord::Base.default_timezone = :local - - @connection.reconnect! - - @first_timestamp_with_zone = PostgresqlTimestampWithZone.find(1) - assert_equal Time.utc(2010,1,1, 11,0,0), @first_timestamp_with_zone.time - ensure - ActiveRecord::Base.default_timezone = old_default_tz - ActiveRecord::Base.time_zone_aware_attributes = old_tz - @connection.reconnect! - end -end diff --git a/activerecord/test/cases/default_test_firebird.rb b/activerecord/test/cases/default_test_firebird.rb deleted file mode 100644 index 713c7e11bf..0000000000 --- a/activerecord/test/cases/default_test_firebird.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "cases/helper" -require 'models/default' - -class DefaultTest < ActiveRecord::TestCase - def test_default_timestamp - default = Default.new - assert_instance_of(Time, default.default_timestamp) - assert_equal(:datetime, default.column_for_attribute(:default_timestamp).type) - - # Variance should be small; increase if required -- e.g., if test db is on - # remote host and clocks aren't synchronized. - t1 = Time.new - accepted_variance = 1.0 - assert_in_delta(t1.to_f, default.default_timestamp.to_f, accepted_variance) - end -end diff --git a/activerecord/test/cases/migration_test_firebird.rb b/activerecord/test/cases/migration_test_firebird.rb deleted file mode 100644 index 710661b9bd..0000000000 --- a/activerecord/test/cases/migration_test_firebird.rb +++ /dev/null @@ -1,124 +0,0 @@ -require "cases/helper" -require 'models/course' - -class FirebirdMigrationTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - def setup - # using Course connection for tests -- need a db that doesn't already have a BOOLEAN domain - @connection = Course.connection - @fireruby_connection = @connection.instance_variable_get(:@connection) - end - - def teardown - @connection.drop_table :foo rescue nil - @connection.execute("DROP DOMAIN D_BOOLEAN") rescue nil - end - - def test_create_table_with_custom_sequence_name - assert_nothing_raised do - @connection.create_table(:foo, :sequence => 'foo_custom_seq') do |f| - f.column :bar, :string - end - end - assert !sequence_exists?('foo_seq') - assert sequence_exists?('foo_custom_seq') - - assert_nothing_raised { @connection.drop_table(:foo, :sequence => 'foo_custom_seq') } - assert !sequence_exists?('foo_custom_seq') - ensure - FireRuby::Generator.new('foo_custom_seq', @fireruby_connection).drop rescue nil - end - - def test_create_table_without_sequence - assert_nothing_raised do - @connection.create_table(:foo, :sequence => false) do |f| - f.column :bar, :string - end - end - assert !sequence_exists?('foo_seq') - assert_nothing_raised { @connection.drop_table :foo } - - assert_nothing_raised do - @connection.create_table(:foo, :id => false) do |f| - f.column :bar, :string - end - end - assert !sequence_exists?('foo_seq') - assert_nothing_raised { @connection.drop_table :foo } - end - - def test_create_table_with_boolean_column - assert !boolean_domain_exists? - assert_nothing_raised do - @connection.create_table :foo do |f| - f.column :bar, :string - f.column :baz, :boolean - end - end - assert boolean_domain_exists? - end - - def test_add_boolean_column - assert !boolean_domain_exists? - @connection.create_table :foo do |f| - f.column :bar, :string - end - - assert_nothing_raised { @connection.add_column :foo, :baz, :boolean } - assert boolean_domain_exists? - assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "baz" }.type - end - - def test_change_column_to_boolean - assert !boolean_domain_exists? - # Manually create table with a SMALLINT column, which can be changed to a BOOLEAN - @connection.execute "CREATE TABLE foo (bar SMALLINT)" - assert_equal :integer, @connection.columns(:foo).find { |c| c.name == "bar" }.type - - assert_nothing_raised { @connection.change_column :foo, :bar, :boolean } - assert boolean_domain_exists? - assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "bar" }.type - end - - def test_rename_table_with_data_and_index - @connection.create_table :foo do |f| - f.column :baz, :string, :limit => 50 - end - 100.times { |i| @connection.execute "INSERT INTO foo VALUES (GEN_ID(foo_seq, 1), 'record #{i+1}')" } - @connection.add_index :foo, :baz - - assert_nothing_raised { @connection.rename_table :foo, :bar } - assert !@connection.tables.include?("foo") - assert @connection.tables.include?("bar") - assert_equal "index_bar_on_baz", @connection.indexes("bar").first.name - assert_equal 100, FireRuby::Generator.new("bar_seq", @fireruby_connection).last - assert_equal 100, @connection.select_one("SELECT COUNT(*) FROM bar")["count"] - ensure - @connection.drop_table :bar rescue nil - end - - def test_renaming_table_with_fk_constraint_raises_error - @connection.create_table :parent do |p| - p.column :name, :string - end - @connection.create_table :child do |c| - c.column :parent_id, :integer - end - @connection.execute "ALTER TABLE child ADD CONSTRAINT fk_child_parent FOREIGN KEY(parent_id) REFERENCES parent(id)" - assert_raise(ActiveRecord::ActiveRecordError) { @connection.rename_table :child, :descendant } - ensure - @connection.drop_table :child rescue nil - @connection.drop_table :descendant rescue nil - @connection.drop_table :parent rescue nil - end - - private - def boolean_domain_exists? - !@connection.select_one("SELECT 1 FROM rdb$fields WHERE rdb$field_name = 'D_BOOLEAN'").nil? - end - - def sequence_exists?(sequence_name) - FireRuby::Generator.exists?(sequence_name, @fireruby_connection) - end -end diff --git a/activerecord/test/cases/reserved_word_test_mysql.rb b/activerecord/test/cases/reserved_word_test_mysql.rb deleted file mode 100644 index 90d8b0d923..0000000000 --- a/activerecord/test/cases/reserved_word_test_mysql.rb +++ /dev/null @@ -1,176 +0,0 @@ -require "cases/helper" - -class Group < ActiveRecord::Base - Group.table_name = 'group' - belongs_to :select, :class_name => 'Select' - has_one :values -end - -class Select < ActiveRecord::Base - Select.table_name = 'select' - has_many :groups -end - -class Values < ActiveRecord::Base - Values.table_name = 'values' -end - -class Distinct < ActiveRecord::Base - Distinct.table_name = 'distinct' - has_and_belongs_to_many :selects - has_many :values, :through => :groups -end - -# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with -# reserved word names (ie: group, order, values, etc...) -class MysqlReservedWordTest < ActiveRecord::TestCase - def setup - @connection = ActiveRecord::Base.connection - - # we call execute directly here (and do similar below) because ActiveRecord::Base#create_table() - # will fail with these table names if these test cases fail - - create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int', - 'select'=>'id int auto_increment primary key', - 'values'=>'id int auto_increment primary key, group_id int', - 'distinct'=>'id int auto_increment primary key', - 'distincts_selects'=>'distinct_id int, select_id int' - end - - def teardown - drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order'] - end - - # create tables with reserved-word names and columns - def test_create_tables - assert_nothing_raised { - @connection.create_table :order do |t| - t.column :group, :string - end - } - end - - # rename tables with reserved-word names - def test_rename_tables - assert_nothing_raised { @connection.rename_table(:group, :order) } - end - - # alter column with a reserved-word name in a table with a reserved-word name - def test_change_columns - assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') } - #the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter - assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) } - assert_nothing_raised { @connection.rename_column(:group, :order, :values) } - end - - # dump structure of table with reserved word name - def test_structure_dump - assert_nothing_raised { @connection.structure_dump } - end - - # introspect table with reserved word name - def test_introspect - assert_nothing_raised { @connection.columns(:group) } - assert_nothing_raised { @connection.indexes(:group) } - end - - #fixtures - self.use_instantiated_fixtures = true - self.use_transactional_fixtures = false - - #fixtures :group - - def test_fixtures - f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - - assert_nothing_raised { - f.each do |x| - x.delete_existing_fixtures - end - } - - assert_nothing_raised { - f.each do |x| - x.insert_fixtures - end - } - end - - #activerecord model class with reserved-word table name - def test_activerecord_model - create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - x = nil - assert_nothing_raised { x = Group.new } - x.order = 'x' - assert_nothing_raised { x.save } - x.order = 'y' - assert_nothing_raised { x.save } - assert_nothing_raised { y = Group.find_by_order('y') } - assert_nothing_raised { y = Group.find(1) } - x = Group.find(1) - end - - # has_one association with reserved-word table name - def test_has_one_associations - create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - v = nil - assert_nothing_raised { v = Group.find(1).values } - assert_equal 2, v.id - end - - # belongs_to association with reserved-word table name - def test_belongs_to_associations - create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - gs = nil - assert_nothing_raised { gs = Select.find(2).groups } - assert_equal gs.length, 2 - assert(gs.collect{|x| x.id}.sort == [2, 3]) - end - - # has_and_belongs_to_many with reserved-word table name - def test_has_and_belongs_to_many - create_test_fixtures :select, :distinct, :group, :values, :distincts_selects - s = nil - assert_nothing_raised { s = Distinct.find(1).selects } - assert_equal s.length, 2 - assert(s.collect{|x|x.id}.sort == [1, 2]) - end - - # activerecord model introspection with reserved-word table and column names - def test_activerecord_introspection - assert_nothing_raised { Group.table_exists? } - assert_nothing_raised { Group.columns } - end - - # Calculations - def test_calculations_work_with_reserved_words - assert_nothing_raised { Group.count } - end - - def test_associations_work_with_reserved_words - assert_nothing_raised { Select.find(:all, :include => [:groups]) } - end - - #the following functions were added to DRY test cases - - private - # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path - def create_test_fixtures(*fixture_names) - Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) - end - - # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name - def drop_tables_directly(table_names, connection = @connection) - table_names.each do |name| - connection.execute("DROP TABLE IF EXISTS `#{name}`") - end - end - - # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns - def create_tables_directly (tables, connection = @connection) - tables.each do |table_name, column_properties| - connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )") - end - end - -end diff --git a/activerecord/test/cases/schema_authorization_test_postgresql.rb b/activerecord/test/cases/schema_authorization_test_postgresql.rb deleted file mode 100644 index 2860f1ad48..0000000000 --- a/activerecord/test/cases/schema_authorization_test_postgresql.rb +++ /dev/null @@ -1,84 +0,0 @@ -require "cases/helper" - -class SchemaThing < ActiveRecord::Base -end - -class SchemaAuthorizationTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - TABLE_NAME = 'schema_things' - COLUMNS = [ - 'id serial primary key', - 'name character varying(50)' - ] - USERS = ['rails_pg_schema_user1', 'rails_pg_schema_user2'] - - def setup - @connection = ActiveRecord::Base.connection - @connection.execute "SET search_path TO '$user',public" - set_session_auth - USERS.each do |u| - @connection.execute "CREATE USER #{u}" rescue nil - @connection.execute "CREATE SCHEMA AUTHORIZATION #{u}" rescue nil - set_session_auth u - @connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" - @connection.execute "INSERT INTO #{TABLE_NAME} (name) VALUES ('#{u}')" - set_session_auth - end - end - - def teardown - set_session_auth - @connection.execute "RESET search_path" - USERS.each do |u| - @connection.execute "DROP SCHEMA #{u} CASCADE" - @connection.execute "DROP USER #{u}" - end - end - - def test_schema_invisible - assert_raise(ActiveRecord::StatementInvalid) do - set_session_auth - @connection.execute "SELECT * FROM #{TABLE_NAME}" - end - end - - def test_schema_uniqueness - assert_nothing_raised do - set_session_auth - USERS.each do |u| - set_session_auth u - assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = 1") - set_session_auth - end - end - end - - def test_sequence_schema_caching - assert_nothing_raised do - USERS.each do |u| - set_session_auth u - st = SchemaThing.new :name => 'TEST1' - st.save! - st = SchemaThing.new :id => 5, :name => 'TEST2' - st.save! - set_session_auth - end - end - end - - def test_tables_in_current_schemas - assert !@connection.tables.include?(TABLE_NAME) - USERS.each do |u| - set_session_auth u - assert @connection.tables.include?(TABLE_NAME) - set_session_auth - end - end - - private - def set_session_auth auth = nil - @connection.execute "SET SESSION AUTHORIZATION #{auth || 'default'}" - end - -end diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb deleted file mode 100644 index 3ed9b1974d..0000000000 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ /dev/null @@ -1,193 +0,0 @@ -require "cases/helper" - -class SchemaTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - SCHEMA_NAME = 'test_schema' - SCHEMA2_NAME = 'test_schema2' - TABLE_NAME = 'things' - CAPITALIZED_TABLE_NAME = 'Things' - INDEX_A_NAME = 'a_index_things_on_name' - INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema' - INDEX_A_COLUMN = 'name' - INDEX_B_COLUMN_S1 = 'email' - INDEX_B_COLUMN_S2 = 'moment' - COLUMNS = [ - 'id integer', - 'name character varying(50)', - 'email character varying(50)', - 'moment timestamp without time zone default now()' - ] - - class Thing1 < ActiveRecord::Base - set_table_name "test_schema.things" - end - - class Thing2 < ActiveRecord::Base - set_table_name "test_schema2.things" - end - - class Thing3 < ActiveRecord::Base - set_table_name 'test_schema."things.table"' - end - - class Thing4 < ActiveRecord::Base - set_table_name 'test_schema."Things"' - end - - def setup - @connection = ActiveRecord::Base.connection - @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" - @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})" - @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})" - @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" - @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" - @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" - @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S1});" - @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S2});" - end - - def teardown - @connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE" - @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE" - end - - def test_table_exists? - [Thing1, Thing2, Thing3, Thing4].each do |klass| - name = klass.table_name - assert @connection.table_exists?(name), "'#{name}' table should exist" - end - end - - def test_table_exists_wrong_schema - assert(!@connection.table_exists?("foo.things"), "table should not exist") - end - - def test_table_exists_quoted_table - assert(@connection.table_exists?('"things.table"'), "table should exist") - end - - def test_with_schema_prefixed_table_name - assert_nothing_raised do - assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}") - end - end - - def test_with_schema_prefixed_capitalized_table_name - assert_nothing_raised do - assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}") - end - end - - def test_with_schema_search_path - assert_nothing_raised do - with_schema_search_path(SCHEMA_NAME) do - assert_equal COLUMNS, columns(TABLE_NAME) - end - end - end - - - def test_proper_encoding_of_table_name - assert_equal '"table_name"', @connection.quote_table_name('table_name') - assert_equal '"table.name"', @connection.quote_table_name('"table.name"') - assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name') - assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"') - assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name') - assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"') - end - - def test_classes_with_qualified_schema_name - assert_equal 0, Thing1.count - assert_equal 0, Thing2.count - assert_equal 0, Thing3.count - assert_equal 0, Thing4.count - - Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) - assert_equal 1, Thing1.count - assert_equal 0, Thing2.count - assert_equal 0, Thing3.count - assert_equal 0, Thing4.count - - Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) - assert_equal 1, Thing1.count - assert_equal 1, Thing2.count - assert_equal 0, Thing3.count - assert_equal 0, Thing4.count - - Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) - assert_equal 1, Thing1.count - assert_equal 1, Thing2.count - assert_equal 1, Thing3.count - assert_equal 0, Thing4.count - - Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) - assert_equal 1, Thing1.count - assert_equal 1, Thing2.count - assert_equal 1, Thing3.count - assert_equal 1, Thing4.count - end - - def test_raise_on_unquoted_schema_name - assert_raise(ActiveRecord::StatementInvalid) do - with_schema_search_path '$user,public' - end - end - - def test_without_schema_search_path - assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) } - end - - def test_ignore_nil_schema_search_path - assert_nothing_raised { with_schema_search_path nil } - end - - def test_dump_indexes_for_schema_one - do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1) - end - - def test_dump_indexes_for_schema_two - do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2) - end - - def test_with_uppercase_index_name - ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" - assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"} - - ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" - ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME - assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "things_Index"} - ActiveRecord::Base.connection.schema_search_path = "public" - end - - private - def columns(table_name) - @connection.send(:column_definitions, table_name).map do |name, type, default| - "#{name} #{type}" + (default ? " default #{default}" : '') - end - end - - def with_schema_search_path(schema_search_path) - @connection.schema_search_path = schema_search_path - yield if block_given? - ensure - @connection.schema_search_path = "'$user', public" - end - - def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name) - with_schema_search_path(this_schema_name) do - indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name} - assert_equal 2,indexes.size - - do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name) - do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name) - end - end - - def do_dump_index_assertions_for_one_index(this_index, this_index_name, this_index_column) - assert_equal TABLE_NAME, this_index.table - assert_equal 1, this_index.columns.size - assert_equal this_index_column, this_index.columns[0] - assert_equal this_index_name, this_index.name - end -end diff --git a/activerecord/test/cases/synonym_test_oracle.rb b/activerecord/test/cases/synonym_test_oracle.rb deleted file mode 100644 index b9a422a6ca..0000000000 --- a/activerecord/test/cases/synonym_test_oracle.rb +++ /dev/null @@ -1,17 +0,0 @@ -require "cases/helper" -require 'models/topic' -require 'models/subject' - -# confirm that synonyms work just like tables; in this case -# the "subjects" table in Oracle (defined in oci.sql) is just -# a synonym to the "topics" table - -class TestOracleSynonym < ActiveRecord::TestCase - - def test_oracle_synonym - topic = Topic.new - subject = Subject.new - assert_equal(topic.attributes, subject.attributes) - end - -end -- cgit v1.2.3 From 158e22dae006b7e630cdb20e6e37cf93fd8d1c56 Mon Sep 17 00:00:00 2001 From: Paul Mucur Date: Fri, 25 Jun 2010 15:35:11 +0100 Subject: Alias ActiveSupport::OrderedHash#update to ActiveSupport::OrderedHash.merge! This ensures that an OrderedHash's keys are set up appropriately when using update. [#4973 state:committed] Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/ordered_hash.rb | 2 ++ activesupport/test/ordered_hash_test.rb | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 91de722748..a19d6c3532 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -149,6 +149,8 @@ module ActiveSupport self end + alias_method :update, :merge! + def merge(other_hash, &block) dup.merge!(other_hash, &block) end diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 0f36f5204d..dca5c5d0c0 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -244,4 +244,10 @@ class OrderedHashTest < Test::Unit::TestCase assert_equal @ordered_hash.keys, @deserialized_ordered_hash.keys assert_equal @ordered_hash.values, @deserialized_ordered_hash.values end + + def test_update_sets_keys + @updated_ordered_hash = ActiveSupport::OrderedHash.new + @updated_ordered_hash.update(:name => "Bob") + assert_equal [:name], @updated_ordered_hash.keys + end end -- cgit v1.2.3 From cae33c41589373d62c76e2bb3cdb3fa023ec8521 Mon Sep 17 00:00:00 2001 From: rohit Date: Fri, 25 Jun 2010 17:33:36 +0530 Subject: Remove previously defined class method logger to supress warnings in Active Support test suites. [#4618 state:open] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/log_subscriber.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index a1ffb8eece..891d718af3 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -36,6 +36,10 @@ module ActiveSupport class_attribute :logger + class << self + remove_method :logger + end + def self.logger @logger ||= Rails.logger if defined?(Rails) end -- cgit v1.2.3 From a2513aea07255877b66b846fcf7ebb35629199df Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 19:22:45 -0300 Subject: Test for concatenated orders added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4972] Signed-off-by: José Valim --- activerecord/test/cases/relations_test.rb | 6 ++++++ activerecord/test/fixtures/topics.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 5b1c6b8f22..821b4585a7 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -110,6 +110,12 @@ class RelationTest < ActiveRecord::TestCase assert_equal topics(:first).title, topics.first.title end + def test_finding_with_order_concatenated + topics = Topic.order('author_name').order('title') + assert_equal 4, topics.to_a.size + assert_equal topics(:fourth).title, topics.first.title + end + def test_finding_with_order_and_take entrants = Entrant.order("id ASC").limit(2).to_a diff --git a/activerecord/test/fixtures/topics.yml b/activerecord/test/fixtures/topics.yml index 1769152445..93f48aedc4 100644 --- a/activerecord/test/fixtures/topics.yml +++ b/activerecord/test/fixtures/topics.yml @@ -24,7 +24,7 @@ second: third: id: 3 title: The Third Topic of the day - author_name: Nick + author_name: Carl written_on: 2005-07-15t15:28:00.0099+01:00 content: I'm a troll approved: true -- cgit v1.2.3 From 65aa6a7db16b7387d5a3bf1ad7f5a602804a2f21 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 19:34:51 -0300 Subject: reorder method added to ActiveRelation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4972 state:committed] Signed-off-by: José Valim --- activerecord/lib/active_record/relation/query_methods.rb | 8 ++++++++ activerecord/test/cases/relations_test.rb | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4dbb30c777..f7c4c7991b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -21,6 +21,14 @@ module ActiveRecord CEVAL end + def reorder(*args, &block) + new_relation = clone + new_relation.send(:apply_modules, Module.new(&block)) if block_given? + value = Array.wrap(args.flatten).reject {|x| x.blank? } + new_relation.order_values = value if value.present? + new_relation + end + def select(*args) if block_given? to_a.select { |*block_args| yield(*block_args) } diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 821b4585a7..d37f7bd09c 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -116,6 +116,12 @@ class RelationTest < ActiveRecord::TestCase assert_equal topics(:fourth).title, topics.first.title end + def test_finding_with_reorder + topics = Topic.order('author_name').order('title').reorder('id') + assert_equal 4, topics.to_a.size + assert_equal topics(:first).title, topics.first.title + end + def test_finding_with_order_and_take entrants = Entrant.order("id ASC").limit(2).to_a -- cgit v1.2.3 From 4464d10e68045e6723d4eb62734213c4296ef339 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 25 Jun 2010 15:49:15 -0700 Subject: index dump should not include full text indexes. Thanks Ken Mayer for the original patch! [#4949 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/connection_adapters/postgresql_adapter.rb | 11 +++-------- activerecord/test/cases/adapters/postgresql/schema_test.rb | 4 ++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index ef58a32074..26d88158d3 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -609,9 +609,7 @@ module ActiveRecord SQL - indexes = [] - - indexes = result.map do |row| + result.map do |row| index_name = row[0] unique = row[1] == 't' indkey = row[2].split(" ") @@ -625,11 +623,8 @@ module ActiveRecord SQL column_names = indkey.map {|attnum| columns[attnum] } - IndexDefinition.new(table_name, index_name, unique, column_names) - - end - - indexes + column_names.compact.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names) + end.compact end # Returns the list of all column definitions for a table. diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 3ed9b1974d..a5c3e69af9 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -9,9 +9,11 @@ class SchemaTest < ActiveRecord::TestCase CAPITALIZED_TABLE_NAME = 'Things' INDEX_A_NAME = 'a_index_things_on_name' INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema' + INDEX_C_NAME = 'c_index_full_text_search' INDEX_A_COLUMN = 'name' INDEX_B_COLUMN_S1 = 'email' INDEX_B_COLUMN_S2 = 'moment' + INDEX_C_COLUMN = %q{(to_tsvector('english', coalesce(things.name, '')))} COLUMNS = [ 'id integer', 'name character varying(50)', @@ -45,6 +47,8 @@ class SchemaTest < ActiveRecord::TestCase @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S1});" @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S2});" + @connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING gin (#{INDEX_C_COLUMN});" + @connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING gin (#{INDEX_C_COLUMN});" end def teardown -- cgit v1.2.3 From 11ff3da5f4fe71a8d93180ab9fa69c6190a2e26e Mon Sep 17 00:00:00 2001 From: Andrew White Date: Thu, 18 Mar 2010 16:49:23 +0000 Subject: Add column and index query methods to ActiveRecord::Schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4219 state:resolved] Signed-off-by: José Valim --- .../abstract/schema_definitions.rb | 10 ++ .../abstract/schema_statements.rb | 57 ++++++++++- .../test/cases/active_schema_test_mysql.rb | 6 +- activerecord/test/cases/migration_test.rb | 108 ++++++++++++++++++++- 4 files changed, 170 insertions(+), 11 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 7d58bc2adf..7691b6a788 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -582,6 +582,11 @@ module ActiveRecord @base.add_column(@table_name, column_name, type, options) end + # Checks to see if a column exists. See SchemaStatements#column_exists? + def column_exists?(column_name, type = nil, options = nil) + @base.column_exists?(@table_name, column_name, type, options) + end + # Adds a new index to the table. +column_name+ can be a single Symbol, or # an Array of Symbols. See SchemaStatements#add_index # @@ -596,6 +601,11 @@ module ActiveRecord @base.add_index(@table_name, column_name, options) end + # Checks to see if an index exists. See SchemaStatements#index_exists? + def index_exists?(column_name, options = {}) + @base.index_exists?(@table_name, column_name, options) + end + # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps # ===== Example # t.timestamps diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 638e5d7236..5625dba45f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -24,10 +24,59 @@ module ActiveRecord # Returns an array of indexes for the given table. # def indexes(table_name, name = nil) end + # Checks to see if an index exists on a table for a given index definition + # + # === Examples + # # Check an index exists + # index_exists?(:suppliers, :company_id) + # + # # Check an index on multiple columns exists + # index_exists?(:suppliers, [:company_id, :company_type]) + # + # # Check a unique index exists + # index_exists?(:suppliers, :company_id, :unique => true) + # + # # Check an index with a custom name exists + # index_exists?(:suppliers, :company_id, :name => "idx_company_id" + def index_exists?(table_name, column_name, options = {}) + column_names = Array.wrap(column_name) + index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names) + if options[:unique] + indexes(table_name).any?{ |i| i.unique && i.name == index_name } + else + indexes(table_name).any?{ |i| i.name == index_name } + end + end + # Returns an array of Column objects for the table specified by +table_name+. # See the concrete implementation for details on the expected parameter values. def columns(table_name, name = nil) end + # Checks to see if a column exists in a given table. + # + # === Examples + # # Check a column exists + # column_exists?(:suppliers, :name) + # + # # Check a column exists of a particular type + # column_exists?(:suppliers, :name, :string) + # + # # Check a column exists with a specific definition + # column_exists?(:suppliers, :name, :string, :limit => 100) + def column_exists?(table_name, column_name, type = nil, options = nil) + column_name = column_name.to_s + if type + if options + sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale]) + columns(table_name).any?{ |c| c.name == column_name && c.sql_type == sql_type } + else + columns(table_name).any?{ |c| c.name == column_name && c.type == type } + end + else + columns(table_name).any?{ |c| c.name == column_name } + end + end + # Creates a new table with the name +table_name+. +table_name+ may either # be a String or a Symbol. # @@ -293,7 +342,7 @@ module ActiveRecord @logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.") return end - if index_exists?(table_name, index_name, false) + if index_name_exists?(table_name, index_name, false) @logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.") return end @@ -314,7 +363,7 @@ module ActiveRecord # remove_index :accounts, :name => :by_branch_party def remove_index(table_name, options = {}) index_name = index_name(table_name, options) - unless index_exists?(table_name, index_name, true) + unless index_name_exists?(table_name, index_name, true) @logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.") return end @@ -351,11 +400,11 @@ module ActiveRecord end end - # Verify the existence of an index. + # Verify the existence of an index with a given name. # # The default argument is returned if the underlying implementation does not define the indexes method, # as there's no way to determine the correct answer in that case. - def index_exists?(table_name, index_name, default) + def index_name_exists?(table_name, index_name, default) return default unless respond_to?(:indexes) indexes(table_name).detect { |i| i.name == index_name } end diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb index d7431e5158..6e6645511c 100644 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ b/activerecord/test/cases/active_schema_test_mysql.rb @@ -17,8 +17,8 @@ class ActiveSchemaTest < ActiveRecord::TestCase end def test_add_index - # add_index calls index_exists? which can't work since execute is stubbed - ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_exists?) do |*| + # add_index calls index_name_exists? which can't work since execute is stubbed + ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_name_exists?) do |*| false end expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" @@ -35,7 +35,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10}) - ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_exists?) + ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_name_exists?) end def test_drop_table diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 9ece2fbab1..99a3a12a8b 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -128,9 +128,9 @@ if ActiveRecord::Base.connection.supports_migrations? good_index_name = 'x' * Person.connection.index_name_length too_long_index_name = good_index_name + 'x' assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => too_long_index_name) } - assert !Person.connection.index_exists?("people", too_long_index_name, false) + assert !Person.connection.index_name_exists?("people", too_long_index_name, false) assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) } - assert Person.connection.index_exists?("people", good_index_name, false) + assert Person.connection.index_name_exists?("people", good_index_name, false) end def test_remove_nonexistent_index @@ -146,8 +146,8 @@ if ActiveRecord::Base.connection.supports_migrations? Person.connection.add_index('people', [:first_name], :name => 'old_idx') assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') } # if the adapter doesn't support the indexes call, pick defaults that let the test pass - assert !Person.connection.index_exists?('people', 'old_idx', false) - assert Person.connection.index_exists?('people', 'new_idx', true) + assert !Person.connection.index_name_exists?('people', 'old_idx', false) + assert Person.connection.index_name_exists?('people', 'new_idx', true) end end @@ -158,6 +158,53 @@ if ActiveRecord::Base.connection.supports_migrations? end end + def test_index_exists + Person.connection.create_table :testings do |t| + t.column :foo, :string, :limit => 100 + t.column :bar, :string, :limit => 100 + end + Person.connection.add_index :testings, :foo + + assert Person.connection.index_exists?(:testings, :foo) + assert !Person.connection.index_exists?(:testings, :bar) + ensure + Person.connection.drop_table :testings rescue nil + end + + def test_index_exists_on_multiple_columns + Person.connection.create_table :testings do |t| + t.column :foo, :string, :limit => 100 + t.column :bar, :string, :limit => 100 + end + Person.connection.add_index :testings, [:foo, :bar] + + assert Person.connection.index_exists?(:testings, [:foo, :bar]) + ensure + Person.connection.drop_table :testings rescue nil + end + + def test_unique_index_exists + Person.connection.create_table :testings do |t| + t.column :foo, :string, :limit => 100 + end + Person.connection.add_index :testings, :foo, :unique => true + + assert Person.connection.index_exists?(:testings, :foo, :unique => true) + ensure + Person.connection.drop_table :testings rescue nil + end + + def test_named_index_exists + Person.connection.create_table :testings do |t| + t.column :foo, :string, :limit => 100 + end + Person.connection.add_index :testings, :foo, :name => "custom_index_name" + + assert Person.connection.index_exists?(:testings, :foo, :name => "custom_index_name") + ensure + Person.connection.drop_table :testings rescue nil + end + def testing_table_with_only_foo_attribute Person.connection.create_table :testings, :id => false do |t| t.column :foo, :string @@ -974,6 +1021,45 @@ if ActiveRecord::Base.connection.supports_migrations? assert_nil Person.new.first_name end + def test_column_exists + Person.connection.create_table :testings do |t| + t.column :foo, :string + end + + assert Person.connection.column_exists?(:testings, :foo) + assert !Person.connection.column_exists?(:testings, :bar) + ensure + Person.connection.drop_table :testings rescue nil + end + + def test_column_exists_with_type + Person.connection.create_table :testings do |t| + t.column :foo, :string + t.column :bar, :decimal, :precision => 8, :scale => 2 + end + + assert Person.connection.column_exists?(:testings, :foo, :string) + assert !Person.connection.column_exists?(:testings, :foo, :integer) + assert Person.connection.column_exists?(:testings, :bar, :decimal) + assert !Person.connection.column_exists?(:testings, :bar, :integer) + ensure + Person.connection.drop_table :testings rescue nil + end + + def test_column_exists_with_definition + Person.connection.create_table :testings do |t| + t.column :foo, :string, :limit => 100 + t.column :bar, :decimal, :precision => 8, :scale => 2 + end + + assert Person.connection.column_exists?(:testings, :foo, :string, :limit => 100) + assert !Person.connection.column_exists?(:testings, :foo, :string, :limit => 50) + assert Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2) + assert !Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2) + ensure + Person.connection.drop_table :testings rescue nil + end + def test_add_table assert !Reminder.table_exists? @@ -1684,6 +1770,20 @@ if ActiveRecord::Base.connection.supports_migrations? end end + def test_index_exists + with_change_table do |t| + @connection.expects(:index_exists?).with(:delete_me, :bar, {}) + t.index_exists?(:bar) + end + end + + def test_index_exists_with_options + with_change_table do |t| + @connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true}) + t.index_exists?(:bar, :unique => true) + end + end + def test_change_changes_column with_change_table do |t| @connection.expects(:change_column).with(:delete_me, :bar, :string, {}) -- cgit v1.2.3 From 47134a04bbce216f9c31473b1a0c1a077d624692 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 25 Jun 2010 20:31:10 -0300 Subject: blocks removed from all the ActiveRelation query_methods, extend method added instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/relation.rb | 9 +++++++++ activerecord/lib/active_record/relation/query_methods.rb | 12 ++++-------- activerecord/test/cases/relations_test.rb | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a51b317dc3..fd0660a138 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -328,6 +328,15 @@ module ActiveRecord to_a.inspect end + def extend(*args, &block) + if block_given? + apply_modules Module.new(&block) + self + else + super + end + end + protected def method_missing(method, *args, &block) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index f7c4c7991b..ff5bce9d8b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -11,9 +11,8 @@ module ActiveRecord next if [:where, :having, :select].include?(query_method) class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args, &block) + def #{query_method}(*args) new_relation = clone - new_relation.send(:apply_modules, Module.new(&block)) if block_given? value = Array.wrap(args.flatten).reject {|x| x.blank? } new_relation.#{query_method}_values += value if value.present? new_relation @@ -21,9 +20,8 @@ module ActiveRecord CEVAL end - def reorder(*args, &block) + def reorder(*args) new_relation = clone - new_relation.send(:apply_modules, Module.new(&block)) if block_given? value = Array.wrap(args.flatten).reject {|x| x.blank? } new_relation.order_values = value if value.present? new_relation @@ -42,9 +40,8 @@ module ActiveRecord [:where, :having].each do |query_method| class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args, &block) + def #{query_method}(*args) new_relation = clone - new_relation.send(:apply_modules, Module.new(&block)) if block_given? value = build_where(*args) new_relation.#{query_method}_values += Array.wrap(value) if value.present? new_relation @@ -56,9 +53,8 @@ module ActiveRecord attr_accessor :"#{query_method}_value" class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(value = true, &block) + def #{query_method}(value = true) new_relation = clone - new_relation.send(:apply_modules, Module.new(&block)) if block_given? new_relation.#{query_method}_value = value new_relation end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index d37f7bd09c..d27a69ea1c 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -618,7 +618,7 @@ class RelationTest < ActiveRecord::TestCase end def test_anonymous_extension - relation = Post.where(:author_id => 1).order('id ASC') do + relation = Post.where(:author_id => 1).order('id ASC').extend do def author 'lifo' end -- cgit v1.2.3 From 6e655732226eef6fa04d8fc0c4ee1f0436688c49 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 25 Jun 2010 16:32:09 -0700 Subject: refactoring the postgres adapter index method to avoid inject and use values_at. [#4976 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 26d88158d3..2fe2ae7136 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -615,15 +615,15 @@ module ActiveRecord indkey = row[2].split(" ") oid = row[3] - columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist} - SELECT a.attname, a.attnum + columns = Hash[query(<<-SQL, "Columns for index #{row[0]} on #{table_name}")] + SELECT a.attnum, a.attname FROM pg_attribute a WHERE a.attrelid = #{oid} AND a.attnum IN (#{indkey.join(",")}) SQL - column_names = indkey.map {|attnum| columns[attnum] } - column_names.compact.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names) + column_names = columns.values_at(*indkey).compact + column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names) end.compact end -- cgit v1.2.3 From bba19603c27e0439eb22a9bce7e3adf6924b224b Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 26 Jun 2010 01:54:13 +0100 Subject: URL fragments should not have safe characters escaped. Ref: Appendix A, http://tools.ietf.org/rfc/rfc3986.txt [#4762 state:resolved] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_dispatch/routing/route_set.rb | 2 +- actionpack/test/controller/url_for_test.rb | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 57a73dde75..97fe137633 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -447,7 +447,7 @@ module ActionDispatch # ROUTES TODO: This can be called directly, so script_name should probably be set in the router rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path) - rewritten_url << "##{Rack::Utils.escape(options[:anchor].to_param.to_s)}" if options[:anchor] + rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor] rewritten_url end diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 907acf9573..4b30b0af36 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -34,9 +34,15 @@ module AbstractController ) end - def test_anchor_should_be_cgi_escaped - assert_equal('/c/a#anc%2Fhor', - W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor')) + def test_anchor_should_escape_unsafe_pchar + assert_equal('/c/a#%23anchor', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('#anchor')) + ) + end + + def test_anchor_should_not_escape_safe_pchar + assert_equal('/c/a#name=user&email=user@domain.com', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('name=user&email=user@domain.com')) ) end -- cgit v1.2.3 From 9a6fc9a540cd23af3ca061cb7406a6cdd5ad4294 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 26 Jun 2010 08:15:33 +0100 Subject: Don't force pluralization of controller name when defining a resource [#4980 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/mapper.rb | 10 +++++++++- actionpack/test/dispatch/routing_test.rb | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 67a629036b..0d22716ba7 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -443,7 +443,7 @@ module ActionDispatch def initialize(entities, options = {}) @name = entities.to_s @path = options.delete(:path) || @name - @controller = options.delete(:controller) || @name.to_s.pluralize + @controller = (options.delete(:controller) || @name).to_s @as = options.delete(:as) @options = options end @@ -547,6 +547,14 @@ module ActionDispatch [:show, :create, :update, :destroy, :new, :edit] end + def initialize(entities, options) + @name = entities.to_s + @path = options.delete(:path) || @name + @controller = (options.delete(:controller) || @name.to_s.pluralize).to_s + @as = options.delete(:as) + @options = options + end + def member_name name end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 68e3dd6de3..8881838aef 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -318,6 +318,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + resources :content + match '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/ end end @@ -1457,6 +1459,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_resources_controller_name_is_not_pluralized + with_test_routes do + get '/content' + assert_equal 'content#index', @response.body + end + end + private def with_test_routes yield -- cgit v1.2.3 From cfaaed3f40e820d2b4d60c2d8bc1f9a005cee086 Mon Sep 17 00:00:00 2001 From: Norman Clarke Date: Fri, 25 Jun 2010 23:55:25 -0300 Subject: Move some methods into 1.8.x-only proxy. [#4978 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These methods had been overridden because they had bugs on 1.9.1. Since Rails now supports only 1.9.2, and these methods now work properly on that version, there's no longer any need to override them. Signed-off-by: José Valim --- .../lib/active_support/multibyte/chars.rb | 79 +++++++++++----------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 04193bfa65..c107aad6bb 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -195,6 +195,45 @@ module ActiveSupport #:nodoc: Unicode.u_unpack(@wrapped_string)[0] end + # Works just like String#rjust, only integer specifies characters instead of bytes. + # + # Example: + # + # "¾ cup".mb_chars.rjust(8).to_s + # #=> " ¾ cup" + # + # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace + # #=> "   ¾ cup" + def rjust(integer, padstr=' ') + justify(integer, :right, padstr) + end + + # Works just like String#ljust, only integer specifies characters instead of bytes. + # + # Example: + # + # "¾ cup".mb_chars.rjust(8).to_s + # #=> "¾ cup " + # + # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace + # #=> "¾ cup   " + def ljust(integer, padstr=' ') + justify(integer, :left, padstr) + end + + # Works just like String#center, only integer specifies characters instead of bytes. + # + # Example: + # + # "¾ cup".mb_chars.center(8).to_s + # #=> " ¾ cup " + # + # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace + # #=> " ¾ cup  " + def center(integer, padstr=' ') + justify(integer, :center, padstr) + end + else def =~(other) @wrapped_string =~ other @@ -250,46 +289,6 @@ module ActiveSupport #:nodoc: end end - # Works just like String#rjust, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.rjust(8).to_s - # #=> " ¾ cup" - # - # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace - # #=> "   ¾ cup" - def rjust(integer, padstr=' ') - justify(integer, :right, padstr) - end - - # Works just like String#ljust, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.rjust(8).to_s - # #=> "¾ cup " - # - # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace - # #=> "¾ cup   " - def ljust(integer, padstr=' ') - justify(integer, :left, padstr) - end - - # Works just like String#center, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.center(8).to_s - # #=> " ¾ cup " - # - # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace - # #=> " ¾ cup  " - def center(integer, padstr=' ') - justify(integer, :center, padstr) - end - - # Reverses all characters in the string. # # Example: -- cgit v1.2.3 From 7bd85a8fc2d216a5e2b1d0380df572f782a54d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 26 Jun 2010 11:57:43 +0200 Subject: Work around the fact the JSON gem was overwriting to_json implementation for all Ruby core classes. This is required because the JSON gem is incompatible with Rails behavior and was not allowing ActiveModel::Errors to be serialized. So we need to ensure Rails implementation is the one triggered. [#4890 state:resolved] --- activemodel/lib/active_model/errors.rb | 17 +++++++---------- .../cases/validations/presence_validation_test.rb | 2 +- activemodel/test/cases/validations_test.rb | 21 +++++++++++++++------ activesupport/lib/active_support/json/encoding.rb | 19 ++++++++++++++----- activesupport/test/ordered_hash_test.rb | 7 +++++++ 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index ae7822d8d5..d42fc5291d 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/hash/reverse_merge' require 'active_support/ordered_hash' module ActiveModel @@ -164,15 +165,12 @@ module ActiveModel # # name must be specified # # def to_xml(options={}) - require 'builder' unless defined? ::Builder - options[:root] ||= "errors" - options[:indent] ||= 2 - options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent]) - - options[:builder].instruct! unless options.delete(:skip_instruct) - options[:builder].errors do |e| - to_a.each { |error| e.error(error) } - end + to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true) + end + + # Returns an array as JSON representation for this object. + def as_json(options=nil) + to_a end # Adds +message+ to the error messages on +attribute+, which will be returned on a call to @@ -283,7 +281,6 @@ module ActiveModel #
  • errors.attributes.title.blank
  • #
  • errors.messages.blank
  • # - def generate_message(attribute, type = :invalid, options = {}) type = options.delete(:message) if options[:message].is_a?(Symbol) diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb index b1450586a8..510c13a7c3 100644 --- a/activemodel/test/cases/validations/presence_validation_test.rb +++ b/activemodel/test/cases/validations/presence_validation_test.rb @@ -32,7 +32,7 @@ class PresenceValidationTest < ActiveModel::TestCase assert t.valid? end - test 'accepts array arguments' do + def test_accepts_array_arguments Topic.validates_presence_of %w(title content) t = Topic.new assert t.invalid? diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 228c1c074f..e94d8ce88c 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -6,6 +6,9 @@ require 'models/reply' require 'models/custom_reader' require 'models/automobile' +require 'active_support/json' +require 'active_support/xml_mini' + class ValidationsTest < ActiveModel::TestCase def setup @@ -158,12 +161,18 @@ class ValidationsTest < ActiveModel::TestCase end end - def test_errors_to_xml - r = Reply.new :title => "Wrong Create" - assert r.invalid? - xml = r.errors.to_xml(:skip_instruct => true) - assert_equal "", xml.first(8) - assert xml.include?("Content is Empty") + def test_errors_conversions + Topic.validates_presence_of %w(title content) + t = Topic.new + assert t.invalid? + + xml = t.errors.to_xml + assert_match %r{}, xml + assert_match %r{Title can't be blank}, xml + assert_match %r{Content can't be blank}, xml + + json = t.errors.to_json + assert_equal t.errors.to_a.to_json, json end def test_validation_order diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 02c233595d..dd94315111 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -128,12 +128,21 @@ module ActiveSupport end end -class Object - # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. - def to_json(options = nil) - ActiveSupport::JSON.encode(self, options) - end +# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting +# their default behavior. That said, we need to define the basic to_json method in all of them, +# otherwise they will always use to_json gem implementation, which is backwards incompatible in +# several cases (for instance, the JSON implementation for Hash does not work) with inheritance +# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json. +[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass| + klass.class_eval <<-RUBY, __FILE__, __LINE__ + # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. + def to_json(options = nil) + ActiveSupport::JSON.encode(self, options) + end + RUBY +end +class Object def as_json(options = nil) #:nodoc: if respond_to?(:to_hash) to_hash diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index dca5c5d0c0..f3d2ec0286 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/json' class OrderedHashTest < Test::Unit::TestCase def setup @@ -185,6 +186,12 @@ class OrderedHashTest < Test::Unit::TestCase assert @ordered_hash.inspect.include?(@hash.inspect) end + def test_json + ordered_hash = ActiveSupport::OrderedHash[:foo, :bar] + hash = Hash[:foo, :bar] + assert_equal ordered_hash.to_json, hash.to_json + end + def test_alternate_initialization_with_splat alternate = ActiveSupport::OrderedHash[1,2,3,4] assert_kind_of ActiveSupport::OrderedHash, alternate -- cgit v1.2.3 From 59b24ceb0c558715714e272737ca0ca06f6043d5 Mon Sep 17 00:00:00 2001 From: Paul Barry Date: Fri, 25 Jun 2010 22:01:56 -0400 Subject: Fixed normalize_path in Routing::Mapper to handle optional prefix segments with static and dynamic parts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- actionpack/test/dispatch/routing_test.rb | 46 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 0d22716ba7..ca57c302ef 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -194,7 +194,7 @@ module ActionDispatch # for root cases, where the latter is the correct one. def self.normalize_path(path) path = Rack::Mount::Utils.normalize_path(path) - path.sub!(%r{/(\(+)/?:}, '\1/:') unless path =~ %r{^/\(+:.*\)$} + path.sub!(%r{/(\(+)/?([^:]*):}, '\1/\2:') unless path =~ %r{^/\(+:.*\)$} path end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 8881838aef..719323a85c 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -308,6 +308,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match "index", :to => 'private#index' end + get "(/:username)/followers" => "followers#index" + get "/groups(/user/:username)" => "groups#index" + get "(/user/:username)/photos" => "photos#index" + match "whatever/:controller(/:action(/:id))" resource :profile do @@ -1466,6 +1470,48 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_url_generator_for_optional_prefix_dynamic_segment + with_test_routes do + get '/bob/followers' + assert_equal 'followers#index', @response.body + assert_equal 'http://www.example.com/bob/followers', + url_for(:controller => "followers", :action => "index", :username => "bob") + + get '/followers' + assert_equal 'followers#index', @response.body + assert_equal 'http://www.example.com/followers', + url_for(:controller => "followers", :action => "index", :username => nil) + end + end + + def test_url_generator_for_optional_suffix_static_and_dynamic_segment + with_test_routes do + get '/groups/user/bob' + assert_equal 'groups#index', @response.body + assert_equal 'http://www.example.com/groups/user/bob', + url_for(:controller => "groups", :action => "index", :username => "bob") + + get '/groups' + assert_equal 'groups#index', @response.body + assert_equal 'http://www.example.com/groups', + url_for(:controller => "groups", :action => "index", :username => nil) + end + end + + def test_url_generator_for_optional_prefix_static_and_dynamic_segment + with_test_routes do + get 'user/bob/photos' + assert_equal 'photos#index', @response.body + assert_equal 'http://www.example.com/user/bob/photos', + url_for(:controller => "photos", :action => "index", :username => "bob") + + get 'photos' + assert_equal 'photos#index', @response.body + assert_equal 'http://www.example.com/photos', + url_for(:controller => "photos", :action => "index", :username => nil) + end + end + private def with_test_routes yield -- cgit v1.2.3 From d4e1a2ef0d35f322803284a980575fc31ff7b4b6 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 26 Jun 2010 10:46:24 +0100 Subject: Support optional static segements as well [#4832 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- actionpack/test/dispatch/routing_test.rb | 34 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index ca57c302ef..388f695187 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -194,7 +194,7 @@ module ActionDispatch # for root cases, where the latter is the correct one. def self.normalize_path(path) path = Rack::Mount::Utils.normalize_path(path) - path.sub!(%r{/(\(+)/?([^:]*):}, '\1/\2:') unless path =~ %r{^/\(+:.*\)$} + path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^/]+\)$} path end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 719323a85c..cf92b039e3 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -312,6 +312,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get "/groups(/user/:username)" => "groups#index" get "(/user/:username)/photos" => "photos#index" + scope '(groups)' do + scope '(discussions)' do + resources :messages + end + end + match "whatever/:controller(/:action(/:id))" resource :profile do @@ -1512,6 +1518,34 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_url_recognition_for_optional_static_segments + with_test_routes do + get '/groups/discussions/messages' + assert_equal 'messages#index', @response.body + + get '/groups/discussions/messages/1' + assert_equal 'messages#show', @response.body + + get '/groups/messages' + assert_equal 'messages#index', @response.body + + get '/groups/messages/1' + assert_equal 'messages#show', @response.body + + get '/discussions/messages' + assert_equal 'messages#index', @response.body + + get '/discussions/messages/1' + assert_equal 'messages#show', @response.body + + get '/messages' + assert_equal 'messages#index', @response.body + + get '/messages/1' + assert_equal 'messages#show', @response.body + end + end + private def with_test_routes yield -- cgit v1.2.3 From 9958950f78638e4a6c6800404d84f22effc0748e Mon Sep 17 00:00:00 2001 From: chaitanyav Date: Sat, 26 Jun 2010 01:52:20 -0700 Subject: Add OrderedHash#invert to preserve order in ruby 1.8 [#4875] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/ordered_hash.rb | 4 ++++ activesupport/test/ordered_hash_test.rb | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index a19d6c3532..6b563b9063 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -162,6 +162,10 @@ module ActiveSupport self end + def invert + OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}] + end + def inspect "#" end diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index f3d2ec0286..fc467b41de 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -257,4 +257,10 @@ class OrderedHashTest < Test::Unit::TestCase @updated_ordered_hash.update(:name => "Bob") assert_equal [:name], @updated_ordered_hash.keys end + + def test_invert + @ordered_hash = ActiveSupport::OrderedHash[[["foo", "FOO"], ["bar", "BAR"]]] + @inverted_ordered_hash = @ordered_hash.invert + assert_equal [["FOO", "foo"], ["BAR", "bar"]], @inverted_ordered_hash.to_a + end end -- cgit v1.2.3 From aa48ab05f40788bd9f54124f91b0ad86869a7b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 26 Jun 2010 12:09:40 +0200 Subject: Tidy up tests in previous commit since they did not assure an OrderedHash is returned (the test would pass for an array and would pass by chance for hashes). [#4875 state:resolved] --- activesupport/test/ordered_hash_test.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index fc467b41de..89855814d7 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -259,8 +259,7 @@ class OrderedHashTest < Test::Unit::TestCase end def test_invert - @ordered_hash = ActiveSupport::OrderedHash[[["foo", "FOO"], ["bar", "BAR"]]] - @inverted_ordered_hash = @ordered_hash.invert - assert_equal [["FOO", "foo"], ["BAR", "bar"]], @inverted_ordered_hash.to_a + assert_kind_of ActiveSupport::OrderedHash, @ordered_hash.invert + assert_equal @values.zip(@keys), @ordered_hash.invert.to_a end end -- cgit v1.2.3 From 3782010377a7774c27011ff57e0e783b0f85a928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 26 Jun 2010 12:14:25 +0200 Subject: Oops. Make previous commit pass on 1.9.2. --- activesupport/test/ordered_hash_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 89855814d7..3d1bae163f 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -259,7 +259,8 @@ class OrderedHashTest < Test::Unit::TestCase end def test_invert - assert_kind_of ActiveSupport::OrderedHash, @ordered_hash.invert + expected = ActiveSupport::OrderedHash[@values.zip(@keys)] + assert_equal expected, @ordered_hash.invert assert_equal @values.zip(@keys), @ordered_hash.invert.to_a end end -- cgit v1.2.3 From 7eb5766bd1d7300e598fb81769da79ffbaa7b62a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 26 Jun 2010 15:48:47 +0200 Subject: Small changes to responder: * resources is always an array; * Lazy retrieve request and formats; * Alias api_location and navigation_location to resource_location, making easier to change its behavior without affecting each other and without a need to reimplement any of the behavior methods. --- actionpack/lib/action_controller/metal/responder.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index 22bdcd0f3c..cb644dfd16 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -89,9 +89,7 @@ module ActionController #:nodoc: def initialize(controller, resources, options={}) @controller = controller - @request = controller.request - @format = controller.formats.first - @resource = resources.is_a?(Array) ? resources.last : resources + @resource = resources.last @resources = resources @options = options @action = options.delete(:action) @@ -101,6 +99,14 @@ module ActionController #:nodoc: delegate :head, :render, :redirect_to, :to => :controller delegate :get?, :post?, :put?, :delete?, :to => :request + def request + @request ||= @controller.request + end + + def format + @format ||= @controller.formats.first + end + # Undefine :to_json and :to_yaml since it's defined on Object undef_method(:to_json) if method_defined?(:to_json) undef_method(:to_yaml) if method_defined?(:to_yaml) @@ -147,7 +153,7 @@ module ActionController #:nodoc: elsif has_errors? && default_action render :action => default_action else - redirect_to resource_location + redirect_to navigation_location end end @@ -160,7 +166,7 @@ module ActionController #:nodoc: elsif has_errors? display resource.errors, :status => :unprocessable_entity elsif post? - display resource, :status => :created, :location => resource_location + display resource, :status => :created, :location => api_location else head :ok end @@ -178,6 +184,8 @@ module ActionController #:nodoc: def resource_location options[:location] || resources end + alias :navigation_location :resource_location + alias :api_location :resource_location # If a given response block was given, use it, otherwise call render on # controller. -- cgit v1.2.3 From df083b482d3f22aebb5e48e608811a740037d2aa Mon Sep 17 00:00:00 2001 From: David Trasbo Date: Sat, 26 Jun 2010 19:16:17 +0200 Subject: Make sure ActiveResource::Errors#from_json doesn't pass nil to #from_array [#3650 state:commited] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activeresource/lib/active_resource/validations.rb | 2 +- activeresource/test/cases/base_errors_test.rb | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb index a19e0d0ac9..026d81e44a 100644 --- a/activeresource/lib/active_resource/validations.rb +++ b/activeresource/lib/active_resource/validations.rb @@ -27,7 +27,7 @@ module ActiveResource # Grabs errors from a json response. def from_json(json, save_cache = false) - array = ActiveSupport::JSON.decode(json)['errors'] rescue [] + array = Array.wrap(ActiveSupport::JSON.decode(json)['errors']) rescue [] from_array array, save_cache end diff --git a/activeresource/test/cases/base_errors_test.rb b/activeresource/test/cases/base_errors_test.rb index b4fd75fba3..5063916d10 100644 --- a/activeresource/test/cases/base_errors_test.rb +++ b/activeresource/test/cases/base_errors_test.rb @@ -17,7 +17,7 @@ class BaseErrorsTest < Test::Unit::TestCase end end - def test_should_parse_xml_errors + def test_should_parse_json_and_xml_errors [ :json, :xml ].each do |format| invalid_user_using_format(format) do assert_kind_of ActiveResource::Errors, @person.errors @@ -26,6 +26,17 @@ class BaseErrorsTest < Test::Unit::TestCase end end + def test_should_parse_json_errors_when_no_errors_key + ActiveResource::HttpMock.respond_to do |mock| + mock.post "/people.json", {}, '{}', 422, {'Content-Type' => 'application/json; charset=utf-8'} + end + + invalid_user_using_format(:json) do + assert_kind_of ActiveResource::Errors, @person.errors + assert_equal 0, @person.errors.size + end + end + def test_should_parse_errors_to_individual_attributes [ :json, :xml ].each do |format| invalid_user_using_format(format) do -- cgit v1.2.3 From 51be8dbdedd1279d74cd7a7e277c5042f278b8a2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 26 Jun 2010 15:40:14 -0300 Subject: Move constantize from conversions to inflections. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes ActiveModel dependency on TZInfo. [#4979 state:committed] Signed-off-by: José Valim --- activemodel/lib/active_model/observing.rb | 1 - .../lib/active_support/core_ext/string/conversions.rb | 11 ----------- .../lib/active_support/core_ext/string/inflections.rb | 11 +++++++++++ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 14f8bf72dc..d0f36ce3b1 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -2,7 +2,6 @@ require 'singleton' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/string/inflections' -require 'active_support/core_ext/string/conversions' module ActiveModel module Observing diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index cd7a42ed2b..6a243fe982 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -47,15 +47,4 @@ class String d[5] += d.pop ::DateTime.civil(*d) end - - # +constantize+ tries to find a declared constant with the name specified - # in the string. It raises a NameError when the name is not in CamelCase - # or is not initialized. - # - # Examples - # "Module".constantize # => Module - # "Class".constantize # => Class - def constantize - ActiveSupport::Inflector.constantize(self) - end end diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index ef479d559e..66c4034781 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -28,6 +28,17 @@ class String ActiveSupport::Inflector.singularize(self) end + # +constantize+ tries to find a declared constant with the name specified + # in the string. It raises a NameError when the name is not in CamelCase + # or is not initialized. + # + # Examples + # "Module".constantize # => Module + # "Class".constantize # => Class + def constantize + ActiveSupport::Inflector.constantize(self) + end + # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize # is set to :lower then camelize produces lowerCamelCase. # -- cgit v1.2.3 From 926ca9c102db5034d4eb1ab0f915ea643145e284 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 30 May 2010 01:37:23 -0300 Subject: Load JSON additions (as to_json) on active_support/all. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4730 state:committed] Signed-off-by: José Valim --- activesupport/lib/active_support/all.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb index f537818300..def8eca89f 100644 --- a/activesupport/lib/active_support/all.rb +++ b/activesupport/lib/active_support/all.rb @@ -1,3 +1,4 @@ require 'active_support' require 'active_support/time' require 'active_support/core_ext' +require 'active_support/json' -- cgit v1.2.3 From 5b91c97763f1183f131e46e51ed33129c6b1edcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 26 Jun 2010 22:37:12 +0200 Subject: Create a little bit less objects in ARel. --- activerecord/lib/active_record/relation/query_methods.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index ff5bce9d8b..caa55929f4 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -12,18 +12,20 @@ module ActiveRecord next if [:where, :having, :select].include?(query_method) class_eval <<-CEVAL, __FILE__, __LINE__ + 1 def #{query_method}(*args) + args.flatten! + args.reject! { |a| a.blank? } new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.#{query_method}_values += value if value.present? + new_relation.#{query_method}_values += args if args.present? new_relation end CEVAL end def reorder(*args) + args.flatten! + args.reject! { |a| a.blank? } new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.order_values = value if value.present? + new_relation.order_values = args if args.present? new_relation end @@ -31,9 +33,10 @@ module ActiveRecord if block_given? to_a.select { |*block_args| yield(*block_args) } else + args.flatten! + args.reject! { |a| a.blank? } new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.select_values += value if value.present? + new_relation.select_values += args if args.present? new_relation end end -- cgit v1.2.3 From 100d2282e306f68d8ce0324128da1506eafc43a2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 26 Jun 2010 13:34:50 -0700 Subject: adding adapter tests, avoiding private apis, fixing code in 1.9 [#4986 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../connection_adapters/sqlite3_adapter.rb | 10 ++-- .../cases/adapters/sqlite/sqlite3_adapter_test.rb | 56 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index f295af16f0..0d9a86a1ea 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -6,6 +6,10 @@ module ActiveRecord def self.sqlite3_connection(config) # :nodoc: parse_sqlite_config!(config) + unless 'sqlite3' == config[:adapter] + raise ArgumentError, 'adapter name should be "sqlite3"' + end + unless self.class.const_defined?(:SQLite3) require_library_or_gem(config[:adapter]) end @@ -24,13 +28,13 @@ module ActiveRecord module ConnectionAdapters #:nodoc: class SQLite3Adapter < SQLiteAdapter # :nodoc: - + # Returns the current database encoding format as a string, eg: 'UTF-8' def encoding if @connection.respond_to?(:encoding) - @connection.encoding[0]['encoding'] + @connection.encoding.to_s else - encoding = @connection.send(:get_query_pragma, 'encoding') + encoding = @connection.execute('PRAGMA encoding') encoding[0]['encoding'] end end diff --git a/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb new file mode 100644 index 0000000000..934cf72f72 --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb @@ -0,0 +1,56 @@ +require "cases/helper" + +module ActiveRecord + module ConnectionAdapters + class SQLite3AdapterTest < ActiveRecord::TestCase + def test_connection_no_db + assert_raises(ArgumentError) do + Base.sqlite3_connection {} + end + end + + def test_connection_no_adapter + assert_raises(ArgumentError) do + Base.sqlite3_connection :database => ':memory:' + end + end + + def test_connection_wrong_adapter + assert_raises(ArgumentError) do + Base.sqlite3_connection :database => ':memory:',:adapter => 'vuvuzela' + end + end + + def test_bad_timeout + assert_raises(TypeError) do + Base.sqlite3_connection :database => ':memory:', + :adapter => 'sqlite3', + :timeout => 'usa' + end + end + + # connection is OK with a nil timeout + def test_nil_timeout + conn = Base.sqlite3_connection :database => ':memory:', + :adapter => 'sqlite3', + :timeout => nil + assert conn, 'made a connection' + end + + def test_connect + conn = Base.sqlite3_connection :database => ':memory:', + :adapter => 'sqlite3', + :timeout => 100 + assert conn, 'should have connection' + end + + # sqlite3 defaults to UTF-8 encoding + def test_encoding + conn = Base.sqlite3_connection :database => ':memory:', + :adapter => 'sqlite3', + :timeout => 100 + assert_equal 'UTF-8', conn.encoding + end + end + end +end -- cgit v1.2.3 From a39f2657b1ae8d61c6018766f8d250b1401beace Mon Sep 17 00:00:00 2001 From: Evgeniy Dolzhenko Date: Thu, 27 May 2010 03:22:45 -0700 Subject: Add module_eval missing file_name and line_number args [#4712 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/autosave_association.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index c378e19864..7517896235 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -147,12 +147,12 @@ module ActiveRecord # add_autosave_association_callbacks(reflect_on_association(name)) # end ASSOCIATION_TYPES.each do |type| - module_eval %{ + module_eval <<-CODE, __FILE__, __LINE__ + 1 def #{type}(name, options = {}) super add_autosave_association_callbacks(reflect_on_association(name)) end - } + CODE end # Adds a validate and save callback for the association as specified by -- cgit v1.2.3 From 64fee27a554151eb87b2350c3834bbf8812520c8 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 26 Jun 2010 17:43:40 -0300 Subject: Removes useless flatten --- activerecord/lib/active_record/relation/query_methods.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index caa55929f4..201a4799a4 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -9,10 +9,9 @@ module ActiveRecord (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).each do |query_method| attr_accessor :"#{query_method}_values" - next if [:where, :having, :select].include?(query_method) + next if [:where, :having, :select, :joins].include?(query_method) class_eval <<-CEVAL, __FILE__, __LINE__ + 1 def #{query_method}(*args) - args.flatten! args.reject! { |a| a.blank? } new_relation = clone new_relation.#{query_method}_values += args if args.present? @@ -22,7 +21,6 @@ module ActiveRecord end def reorder(*args) - args.flatten! args.reject! { |a| a.blank? } new_relation = clone new_relation.order_values = args if args.present? @@ -33,7 +31,6 @@ module ActiveRecord if block_given? to_a.select { |*block_args| yield(*block_args) } else - args.flatten! args.reject! { |a| a.blank? } new_relation = clone new_relation.select_values += args if args.present? @@ -41,6 +38,14 @@ module ActiveRecord end end + def joins(*args) + args.flatten! + args.reject! { |a| a.blank? } + new_relation = clone + new_relation.joins_values += args if args.present? + new_relation + end + [:where, :having].each do |query_method| class_eval <<-CEVAL, __FILE__, __LINE__ + 1 def #{query_method}(*args) -- cgit v1.2.3 From 8f358f397f11a87c649f76644690461f0017cbff Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 26 Jun 2010 20:12:23 -0300 Subject: Refactor of column_exists? method and this works with PostgreSQL --- .../connection_adapters/abstract/schema_statements.rb | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 5625dba45f..0216a8f4ac 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -63,18 +63,12 @@ module ActiveRecord # # # Check a column exists with a specific definition # column_exists?(:suppliers, :name, :string, :limit => 100) - def column_exists?(table_name, column_name, type = nil, options = nil) - column_name = column_name.to_s - if type - if options - sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale]) - columns(table_name).any?{ |c| c.name == column_name && c.sql_type == sql_type } - else - columns(table_name).any?{ |c| c.name == column_name && c.type == type } - end - else - columns(table_name).any?{ |c| c.name == column_name } - end + def column_exists?(table_name, column_name, type = nil, options = {}) + columns(table_name).any?{ |c| c.name == column_name.to_s && + (!type || c.type == type) && + (!options[:limit] || c.limit == options[:limit]) && + (!options[:precision] || c.precision == options[:precision]) && + (!options[:scale] || c.scale == options[:scale]) } end # Creates a new table with the name +table_name+. +table_name+ may either -- cgit v1.2.3 From 0abf4b07462d4383691581bc38cd6bf1b5887281 Mon Sep 17 00:00:00 2001 From: David Trasbo Date: Sat, 26 Jun 2010 22:36:05 +0200 Subject: Change reference to Test::Unit::AssertionFailedError to the generic ActiveSupport::TestCase::Assertion [#4987 state:commited] Signed-off-by: Jeremy Kemper --- actionpack/test/template/test_case_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index a747f46527..a0c46f8a59 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -248,7 +248,7 @@ module ActionView test "supports specifying locals (failing)" do controller.controller_path = "test" render(:template => "test/calling_partial_with_layout") - assert_raise Test::Unit::AssertionFailedError, /Somebody else.*David/m do + assert_raise ActiveSupport::TestCase::Assertion, /Somebody else.*David/m do assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "Somebody Else" } end end -- cgit v1.2.3 From 8d9545389f6a53dcd1ebae28c8b966c296da95f2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 26 Jun 2010 23:30:43 -0300 Subject: Refactor: metaprogramming here it's confusing and make use of tap Signed-off-by: Jeremy Kemper --- .../lib/active_record/relation/query_methods.rb | 137 +++++++++++---------- 1 file changed, 73 insertions(+), 64 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 201a4799a4..015ca8c24c 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -5,89 +5,98 @@ module ActiveRecord module QueryMethods extend ActiveSupport::Concern - included do - (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).each do |query_method| - attr_accessor :"#{query_method}_values" - - next if [:where, :having, :select, :joins].include?(query_method) - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args) - args.reject! { |a| a.blank? } - new_relation = clone - new_relation.#{query_method}_values += args if args.present? - new_relation - end - CEVAL - end + attr_accessor :includes_values, :eager_load_values, :preload_values, + :select_values, :group_values, :order_values, :joins_values, :where_values, :having_values, + :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value - def reorder(*args) - args.reject! { |a| a.blank? } - new_relation = clone - new_relation.order_values = args if args.present? - new_relation - end + def includes(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.includes_values += args if args.present? } + end - def select(*args) - if block_given? - to_a.select { |*block_args| yield(*block_args) } - else - args.reject! { |a| a.blank? } - new_relation = clone - new_relation.select_values += args if args.present? - new_relation - end - end + def eager_load(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.eager_load_values += args if args.present? } + end - def joins(*args) - args.flatten! + def preload(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.preload_values += args if args.present? } + end + + def select(*args) + if block_given? + to_a.select { |*block_args| yield(*block_args) } + else args.reject! { |a| a.blank? } - new_relation = clone - new_relation.joins_values += args if args.present? - new_relation + clone.tap { |r| r.select_values += args if args.present? } end + end - [:where, :having].each do |query_method| - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args) - new_relation = clone - value = build_where(*args) - new_relation.#{query_method}_values += Array.wrap(value) if value.present? - new_relation - end - CEVAL - end + def group(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.group_values += args if args.present? } + end - ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method| - attr_accessor :"#{query_method}_value" + def order(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.order_values += args if args.present? } + end - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(value = true) - new_relation = clone - new_relation.#{query_method}_value = value - new_relation - end - CEVAL - end + def reorder(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.order_values = args if args.present? } end - def extending(*modules) - new_relation = clone - new_relation.send :apply_modules, *modules - new_relation + def joins(*args) + args.flatten! + args.reject! { |a| a.blank? } + clone.tap { |r| r.joins_values += args if args.present? } end - def lock(locks = true, &block) - relation = clone - relation.send(:apply_modules, Module.new(&block)) if block_given? + def where(*args) + value = build_where(*args) + clone.tap { |r| r.where_values += Array.wrap(value) if value.present? } + end + + def having(*args) + value = build_where(*args) + clone.tap { |r| r.having_values += Array.wrap(value) if value.present? } + end + + def limit(value = true) + clone.tap { |r| r.limit_value = value } + end + + def offset(value = true) + clone.tap { |r| r.offset_value = value } + end + def lock(locks = true) case locks when String, TrueClass, NilClass - clone.tap {|new_relation| new_relation.lock_value = locks || true } + clone.tap { |r| r.lock_value = locks || true } else - clone.tap {|new_relation| new_relation.lock_value = false } + clone.tap { |r| r.lock_value = false } end end + def readonly(value = true) + clone.tap { |r| r.readonly_value = value } + end + + def create_with(value = true) + clone.tap { |r| r.create_with_value = value } + end + + def from(value = true) + clone.tap { |r| r.from_value = value } + end + + def extending(*modules) + clone.tap { |r| r.send :apply_modules, *modules } + end + def reverse_order order_clause = arel.send(:order_clauses).join(', ') relation = except(:order) -- cgit v1.2.3 From abd568bf1c48e9082c0be7407eca1155c5fe0599 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 26 Jun 2010 17:06:48 -0700 Subject: removing useless code. [#4988 state:resolved] Signed-off-by: Jeremy Kemper --- .../connection_adapters/sqlite_adapter.rb | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index ad6314c530..1927585c49 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -151,7 +151,7 @@ module ActiveRecord # DATABASE STATEMENTS ====================================== def execute(sql, name = nil) #:nodoc: - catch_schema_changes { log(sql, name) { @connection.execute(sql) } } + log(sql, name) { @connection.execute(sql) } end def update_sql(sql, name = nil) #:nodoc: @@ -176,15 +176,15 @@ module ActiveRecord end def begin_db_transaction #:nodoc: - catch_schema_changes { @connection.transaction } + @connection.transaction end def commit_db_transaction #:nodoc: - catch_schema_changes { @connection.commit } + @connection.commit end def rollback_db_transaction #:nodoc: - catch_schema_changes { @connection.rollback } + @connection.rollback end # SCHEMA STATEMENTS ======================================== @@ -391,17 +391,6 @@ module ActiveRecord end end - def catch_schema_changes - return yield - rescue ActiveRecord::StatementInvalid => exception - if exception.message =~ /database schema has changed/ - reconnect! - retry - else - raise - end - end - def sqlite_version @sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)')) end -- cgit v1.2.3 From 91b52c795f62aa15505f2f098bc86d6f6db75105 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sun, 27 Jun 2010 08:03:39 +0100 Subject: Normalize recall params when the route is not a standard route otherwise :controller and :action may appear in the generated url [#4326 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_dispatch/routing/route_set.rb | 29 +++++++++++++++++++--- actionpack/test/template/url_helper_test.rb | 22 ++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 97fe137633..7be79d3200 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -185,7 +185,7 @@ module ActionDispatch end end - attr_accessor :routes, :named_routes + attr_accessor :set, :routes, :named_routes attr_accessor :disable_clear_and_finalize, :resources_path_names attr_accessor :default_url_options, :request_class @@ -296,6 +296,7 @@ module ActionDispatch @extras = extras normalize_options! + normalize_recall! normalize_controller_action_id! use_relative_controller! controller.sub!(%r{^/}, '') if controller @@ -336,6 +337,15 @@ module ActionDispatch end end + def normalize_recall! + # If the target route is not a standard route then remove controller and action + # from the options otherwise they will appear in the url parameters + if block_or_proc_route_target? + recall.delete(:controller) unless segment_keys.include?(:controller) + recall.delete(:action) unless segment_keys.include?(:action) + end + end + # This pulls :controller, :action, and :id out of the recall. # The recall key is only used if there is no key in the options # or if the key in the options is identical. If any of @@ -371,7 +381,7 @@ module ActionDispatch def generate error = ActionController::RoutingError.new("No route matches #{options.inspect}") - path, params = @set.generate(:path_info, named_route, options, recall, opts) + path, params = @set.set.generate(:path_info, named_route, options, recall, opts) raise error unless path @@ -402,6 +412,19 @@ module ActionDispatch return false unless current_controller controller.to_param != current_controller.to_param end + + private + def named_route_exists? + named_route && set.named_routes[named_route] + end + + def block_or_proc_route_target? + named_route_exists? && !set.named_routes[named_route].app.is_a?(Dispatcher) + end + + def segment_keys + named_route_exists? ? set.named_routes[named_route].segment_keys : [] + end end # Generate the path indicated by the arguments, and return an array of @@ -415,7 +438,7 @@ module ActionDispatch end def generate(options, recall = {}, extras = false) - Generator.new(options, recall, @set, extras).generate + Generator.new(options, recall, self, extras).generate end RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash] diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 299d6dd5bd..72d4897630 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -429,6 +429,10 @@ class UrlHelperControllerTest < ActionController::TestCase map.connect ":controller/:action/:id" # match "/:controller(/:action(/:id))" + + match 'url_helper_controller_test/url_helper/normalize_recall_params', + :to => UrlHelperController.action(:normalize_recall), + :as => :normalize_recall_params end def show_url_for @@ -447,6 +451,14 @@ class UrlHelperControllerTest < ActionController::TestCase render :inline => '<%= url_for(nil) %>' end + def normalize_recall_params + render :inline => '<%= normalize_recall_params_path %>' + end + + def recall_params_not_changed + render :inline => '<%= url_for(:action => :show_url_for) %>' + end + def rescue_action(e) raise e end end @@ -488,6 +500,16 @@ class UrlHelperControllerTest < ActionController::TestCase get :show_named_route, :kind => 'url' assert_equal 'http://testtwo.host/url_helper_controller_test/url_helper/show_named_route', @response.body end + + def test_recall_params_should_be_normalized_when_using_block_route + get :normalize_recall_params + assert_equal '/url_helper_controller_test/url_helper/normalize_recall_params', @response.body + end + + def test_recall_params_should_not_be_changed_when_using_normal_route + get :recall_params_not_changed + assert_equal '/url_helper_controller_test/url_helper/show_url_for', @response.body + end end class TasksController < ActionController::Base -- cgit v1.2.3 From 0c0b0aa0f223523331afdc157fb3992a121bf497 Mon Sep 17 00:00:00 2001 From: George Montana Harkin Date: Sun, 27 Jun 2010 19:05:51 +0530 Subject: Fixes #2415 by creating a new instance of the Model when saving attributes to that model and the associated attributes already exist. Tests included. [#2415 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_record/nested_attributes.rb | 19 ++++++++------- activerecord/test/cases/nested_attributes_test.rb | 28 ++++++++++++---------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 12a75f5d16..c0302136ea 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -296,7 +296,9 @@ module ActiveRecord assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) elsif attributes['id'] - raise_nested_attributes_record_not_found(association_name, attributes['id']) + existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id']) + assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) + self.send(association_name.to_s+'=', existing_record) elsif !reject_new_record?(association_name, attributes) method = "build_#{association_name}" @@ -366,11 +368,16 @@ module ActiveRecord unless reject_new_record?(association_name, attributes) association.build(attributes.except(*UNASSIGNABLE_KEYS)) end + + elsif existing_records.count == 0 #Existing record but not yet associated + existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id']) + association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded? + assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) + elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s } association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded? assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) - else - raise_nested_attributes_record_not_found(association_name, attributes['id']) + end end end @@ -390,7 +397,7 @@ module ActiveRecord ConnectionAdapters::Column.value_to_boolean(hash['_destroy']) end - # Determines if a new record should be build by checking for + # Determines if a new record should be built by checking for # has_destroy_flag? or if a :reject_if proc exists for this # association and evaluates to +true+. def reject_new_record?(association_name, attributes) @@ -406,9 +413,5 @@ module ActiveRecord end end - def raise_nested_attributes_record_not_found(association_name, record_id) - reflection = self.class.reflect_on_association(association_name) - raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}" - end end end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 65d6080ea5..3c797076e0 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -176,12 +176,6 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name end - def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record - assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}" do - @pirate.ship_attributes = { :id => 1234567890 } - end - end - def test_should_take_a_hash_with_string_keys_and_update_the_associated_model @pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' } @@ -331,11 +325,14 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase assert_equal 'Arr', @ship.pirate.catchphrase end - def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record - assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}" do - @ship.pirate_attributes = { :id => 1234567890 } - end - end + def test_should_associate_with_record_if_parent_record_is_not_saved + @ship.destroy + @pirate = Pirate.create(:catchphrase => 'Arr') + @ship = Ship.new(:name => 'Nights Dirty Lightning', :pirate_attributes => { :id => @pirate.id, :catchphrase => @pirate.catchphrase}) + + assert_equal @ship.name, 'Nights Dirty Lightning' + assert_equal @pirate, @ship.pirate + end def test_should_take_a_hash_with_string_keys_and_update_the_associated_model @ship.reload.pirate_attributes = { 'id' => @pirate.id, 'catchphrase' => 'Arr' } @@ -444,6 +441,11 @@ module NestedAttributesOnACollectionAssociationTests assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name] end + def test_should_assign_existing_children_if_parent_is_new + @pirate = Pirate.new({:catchphrase => "Don' botharr talkin' like one, savvy?"}.merge(@alternate_params)) + assert_equal ['Grace OMalley', 'Privateers Greed'], [@pirate.send(@association_name)[0].name, @pirate.send(@association_name)[1].name] + end + def test_should_also_work_with_a_HashWithIndifferentAccess @pirate.send(association_setter, HashWithIndifferentAccess.new('foo' => HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley'))) @pirate.save @@ -501,8 +503,8 @@ module NestedAttributesOnACollectionAssociationTests assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name] end - def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record - assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}" do + def test_should_not_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record + assert_nothing_raised ActiveRecord::RecordNotFound do @pirate.attributes = { association_getter => [{ :id => 1234567890 }] } end end -- cgit v1.2.3 From fa96638bf2c5c97571849715cb8298c7a3b058ca Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 27 Jun 2010 13:08:38 -0300 Subject: Added getbyte as a core_ext to Ruby < 1.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/core_ext/string/conversions.rb | 4 ++++ activesupport/test/core_ext/string_ext_test.rb | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index 6a243fe982..af277f9995 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -28,6 +28,10 @@ class String self[0] end unless method_defined?(:ord) + def getbyte(index) + self[index] + end if RUBY_VERSION < '1.9' + # Form can be either :utc (default) or :local. def to_time(form = :utc) return nil if self.blank? diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index d9702dd9ff..affa1b5e18 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -110,6 +110,14 @@ class StringInflectionsTest < Test::Unit::TestCase assert_equal 97, 'abc'.ord end + if RUBY_VERSION < '1.9' + def test_getbyte + assert_equal 97, 'a'.getbyte(0) + assert_equal 99, 'abc'.getbyte(2) + assert_nil 'abc'.getbyte(3) + end + end + def test_string_to_time assert_equal Time.utc(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time(:local) -- cgit v1.2.3 From a822ce78b39db60fef9d8c3280551f199c91c6b3 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 27 Jun 2010 13:09:41 -0300 Subject: String#[] doesn't return the byte representation on 1.9.2, we should use getbyte that was already added as a Ruby < 1.9 core_ext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/cache/mem_cache_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 3c46ae5424..852defeae8 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -159,7 +159,7 @@ module ActiveSupport private def escape_key(key) - key = key.to_s.gsub(ESCAPE_KEY_CHARS){|match| "%#{match[0].to_s(16).upcase}"} + key = key.to_s.gsub(ESCAPE_KEY_CHARS){|match| "%#{match.getbyte(0).to_s(16).upcase}"} key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250 key end -- cgit v1.2.3 From ebee77a28a7267d5f23a28ba23c1eb88a2d7d527 Mon Sep 17 00:00:00 2001 From: Michael Lovitt Date: Sun, 27 Jun 2010 14:35:31 -0400 Subject: Fixed that an ArgumentError is thrown when request.session_options[:id] is read in the following scenario: when the cookie store is used, and the session contains a serialized object of an unloaded class, and no session data accesses have occurred yet. Pushed the stale_session_check responsibility out of the SessionHash and down into the session store, closer to where the deserialization actually occurs. Added some test coverage for this case and others related to deserialization of unloaded types. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4938] Signed-off-by: José Valim --- .../middleware/session/abstract_store.rb | 64 +++++++++++----------- .../middleware/session/cookie_store.rb | 10 ++-- actionpack/test/abstract_unit.rb | 15 +++++ .../test/dispatch/session/cookie_store_test.rb | 26 +++++++++ .../test/dispatch/session/mem_cache_store_test.rb | 24 ++++++++ .../session_autoload_test/foo.rb | 10 ++++ 6 files changed, 113 insertions(+), 36 deletions(-) create mode 100644 actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index bc1d6fab83..08bc80dbc2 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -88,9 +88,7 @@ module ActionDispatch def exists? return @exists if instance_variable_defined?(:@exists) - stale_session_check! do - @exists = @by.send(:exists?, @env) - end + @exists = @by.send(:exists?, @env) end def loaded? @@ -115,29 +113,12 @@ module ActionDispatch end def load! - stale_session_check! do - id, session = @by.send(:load_session, @env) - @env[ENV_SESSION_OPTIONS_KEY][:id] = id - replace(session.stringify_keys) - @loaded = true - end + id, session = @by.send(:load_session, @env) + @env[ENV_SESSION_OPTIONS_KEY][:id] = id + replace(session.stringify_keys) + @loaded = true end - def stale_session_check! - yield - rescue ArgumentError => argument_error - if argument_error.message =~ %r{undefined class/module ([\w:]*\w)} - begin - # Note that the regexp does not allow $1 to end with a ':' - $1.constantize - rescue LoadError, NameError => const_error - raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n" - end - retry - else - raise - end - end end DEFAULT_OPTIONS = { @@ -204,16 +185,20 @@ module ActionDispatch end def load_session(env) - sid = current_session_id(env) - sid, session = get_session(env, sid) - [sid, session] + stale_session_check! do + sid = current_session_id(env) + sid, session = get_session(env, sid) + [sid, session] + end end def extract_session_id(env) - request = ActionDispatch::Request.new(env) - sid = request.cookies[@key] - sid ||= request.params[@key] unless @cookie_only - sid + stale_session_check! do + request = ActionDispatch::Request.new(env) + sid = request.cookies[@key] + sid ||= request.params[@key] unless @cookie_only + sid + end end def current_session_id(env) @@ -229,6 +214,22 @@ module ActionDispatch end end + def stale_session_check! + yield + rescue ArgumentError => argument_error + if argument_error.message =~ %r{undefined class/module ([\w:]*\w)} + begin + # Note that the regexp does not allow $1 to end with a ':' + $1.constantize + rescue LoadError, NameError => const_error + raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n" + end + retry + else + raise + end + end + def exists?(env) current_session_id(env).present? end @@ -245,7 +246,6 @@ module ActionDispatch def destroy(env) raise '#destroy needs to be implemented.' end - end end end diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 7ebd532f4a..dce47c63bc 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -63,11 +63,13 @@ module ActionDispatch def unpacked_cookie_data(env) env["action_dispatch.request.unsigned_session_cookie"] ||= begin - request = ActionDispatch::Request.new(env) - if data = request.cookie_jar.signed[@key] - data.stringify_keys! + stale_session_check! do + request = ActionDispatch::Request.new(env) + if data = request.cookie_jar.signed[@key] + data.stringify_keys! + end + data || {} end - data || {} end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 84c5395610..2640e96453 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -202,6 +202,21 @@ class ActionController::IntegrationTest < ActiveSupport::TestCase self.class.app = old_app silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) } end + + def with_autoload_path(path) + path = File.join(File.dirname(__FILE__), "fixtures", path) + if ActiveSupport::Dependencies.autoload_paths.include?(path) + yield + else + begin + ActiveSupport::Dependencies.autoload_paths << path + yield + ensure + ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path} + ActiveSupport::Dependencies.clear + end + end + end end # Temporary base class diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 6aca22b456..fd63f5ad5e 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -96,6 +96,31 @@ class CookieStoreTest < ActionController::IntegrationTest end end + # {:foo=>#, :session_id=>"ce8b0752a6ab7c7af3cdb8a80e6b9e46"} + SignedSerializedCookie = "BAh7BzoIZm9vbzodU2Vzc2lvbkF1dG9sb2FkVGVzdDo6Rm9vBjoJQGJhciIIYmF6Og9zZXNzaW9uX2lkIiVjZThiMDc1MmE2YWI3YzdhZjNjZGI4YTgwZTZiOWU0Ng==--2bf3af1ae8bd4e52b9ac2099258ace0c380e601c" + + def test_deserializes_unloaded_classes_on_get_id + with_test_route_set do + with_autoload_path "session_autoload_test" do + cookies[SessionKey] = SignedSerializedCookie + get '/get_session_id' + assert_response :success + assert_equal 'id: ce8b0752a6ab7c7af3cdb8a80e6b9e46', response.body, "should auto-load unloaded class" + end + end + end + + def test_deserializes_unloaded_classes_on_get_value + with_test_route_set do + with_autoload_path "session_autoload_test" do + cookies[SessionKey] = SignedSerializedCookie + get '/get_session_value' + assert_response :success + assert_equal 'foo: #', response.body, "should auto-load unloaded class" + end + end + end + def test_close_raises_when_data_overflows with_test_route_set do assert_raise(ActionDispatch::Cookies::CookieOverflow) { @@ -247,4 +272,5 @@ class CookieStoreTest < ActionController::IntegrationTest yield end end + end diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index d388992b98..9bd6f9b8c4 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -11,6 +11,11 @@ class MemCacheStoreTest < ActionController::IntegrationTest session[:foo] = "bar" head :ok end + + def set_serialized_session_value + session[:foo] = SessionAutoloadTest::Foo.new + head :ok + end def get_session_value render :text => "foo: #{session[:foo].inspect}" @@ -117,6 +122,25 @@ class MemCacheStoreTest < ActionController::IntegrationTest end end + def test_deserializes_unloaded_class + with_test_route_set do + with_autoload_path "session_autoload_test" do + get '/set_serialized_session_value' + assert_response :success + assert cookies['_session_id'] + end + with_autoload_path "session_autoload_test" do + get '/get_session_id' + assert_response :success + end + with_autoload_path "session_autoload_test" do + get '/get_session_value' + assert_response :success + assert_equal 'foo: #', response.body, "should auto-load unloaded class" + end + end + end + def test_doesnt_write_session_cookie_if_session_id_is_already_exists with_test_route_set do get '/set_session_value' diff --git a/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb b/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb new file mode 100644 index 0000000000..4ee7a24561 --- /dev/null +++ b/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb @@ -0,0 +1,10 @@ +module SessionAutoloadTest + class Foo + def initialize(bar='baz') + @bar = bar + end + def inspect + "#<#{self.class} bar:#{@bar.inspect}>" + end + end +end -- cgit v1.2.3