diff options
63 files changed, 542 insertions, 666 deletions
@@ -21,6 +21,12 @@ elsif RUBY_ENGINE == "jruby" gem "jruby-openssl" end +# AS +gem "memcache-client", ">= 1.7.5" + +# AM +gem "text-format", "~> 1.0.0" + # AR if mri || RUBY_ENGINE == "rbx" gem "sqlite3-ruby", "= 1.3.0.beta.2", :require => 'sqlite3' @@ -39,7 +45,6 @@ elsif RUBY_ENGINE == "jruby" end # AP -gem "rack-test", "0.5.3", :require => 'rack/test' gem "RedCloth", ">= 4.2.2" group :documentation do diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index 280771e4db..4706b63b79 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -21,5 +21,4 @@ Gem::Specification.new do |s| s.add_dependency('actionpack', version) s.add_dependency('mail', '~> 2.2.1') - s.add_dependency('text-format', '~> 1.0.0') end diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 70cc312634..6e2d288082 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -49,9 +49,3 @@ module ActionMailer autoload :TestCase autoload :TestHelper end - -module Text - extend ActiveSupport::Autoload - - autoload :Format, 'text/format' -end diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index ab5c3469b2..aab6e12387 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -3,6 +3,13 @@ module ActionMailer # Uses Text::Format to take the text and format it, indented two spaces for # each line, and wrapped at 72 columns. def block_format(text) + begin + require 'text/format' + rescue LoadError => e + $stderr.puts "You don't have text-format installed in your application. Please add it to your Gemfile and run bundle install" + raise e + end unless defined?(Text::Format) + formatted = text.split(/\n\r\n/).collect { |paragraph| Text::Format.new( :columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index a5abe9be10..0f45cb5a4a 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -21,8 +21,11 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) + s.add_dependency('builder', '~> 2.1.2') + s.add_dependency('i18n', '~> 0.4.0') s.add_dependency('rack', '~> 1.1.0') - s.add_dependency('rack-test', '~> 0.5.0') + s.add_dependency('rack-test', '~> 0.5.4') s.add_dependency('rack-mount', '~> 0.6.3') + s.add_dependency('tzinfo', '~> 0.3.16') s.add_dependency('erubis', '~> 2.6.5') end diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 30aa34d956..775a5002e2 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -1,6 +1,48 @@ require 'active_support/core_ext/class/attribute' +require 'active_support/core_ext/object/blank' +require 'action_dispatch/middleware/stack' module ActionController + # Extend ActionDispatch middleware stack to make it aware of options + # allowing the following syntax in controllers: + # + # class PostsController < ApplicationController + # use AuthenticationMiddleware, :except => [:index, :show] + # end + # + class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc: + class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc: + def initialize(klass, *args) + options = args.extract_options! + @only = Array(options.delete(:only)).map(&:to_s) + @except = Array(options.delete(:except)).map(&:to_s) + args << options unless options.empty? + super + end + + def valid?(action) + if @only.present? + @only.include?(action) + elsif @except.present? + !@except.include?(action) + else + true + end + end + end + + def build(action, app=nil, &block) + app ||= block + action = action.to_s + raise "MiddlewareStack#build requires an app" unless app + + reverse.inject(app) do |a, middleware| + middleware.valid?(action) ? + middleware.build(a) : a + end + end + end + # ActionController::Metal provides a way to get a valid Rack application from a controller. # # In AbstractController, dispatching is triggered directly by calling #process on a new controller. @@ -91,10 +133,10 @@ module ActionController end class_attribute :middleware_stack - self.middleware_stack = ActionDispatch::MiddlewareStack.new + self.middleware_stack = ActionController::MiddlewareStack.new def self.inherited(base) - self.middleware_stack = base.middleware_stack.dup + base.middleware_stack = self.middleware_stack.dup super end @@ -120,7 +162,7 @@ module ActionController # ==== Returns # Proc:: A rack application def self.action(name, klass = ActionDispatch::Request) - middleware_stack.build do |env| + middleware_stack.build(name.to_s) do |env| new.dispatch(name, klass.new(env)) end end diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index dfb8919561..cdf81c6648 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -43,7 +43,6 @@ module ActionDispatch autoload_under 'middleware' do autoload :Callbacks - autoload :Cascade autoload :Cookies autoload :Flash autoload :Head diff --git a/actionpack/lib/action_dispatch/middleware/cascade.rb b/actionpack/lib/action_dispatch/middleware/cascade.rb deleted file mode 100644 index 9f5c9891f0..0000000000 --- a/actionpack/lib/action_dispatch/middleware/cascade.rb +++ /dev/null @@ -1,29 +0,0 @@ -module ActionDispatch - class Cascade - def self.new(*apps) - apps = apps.flatten - - case apps.length - when 0 - raise ArgumentError, "app is required" - when 1 - apps.first - else - super(apps) - end - end - - def initialize(apps) - @apps = apps - end - - def call(env) - result = nil - @apps.each do |app| - result = app.call(env) - break unless result[1]["X-Cascade"] == "pass" - end - result - end - end -end diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index 5c5362ce4a..e3180dba77 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -3,49 +3,15 @@ require "active_support/inflector/methods" module ActionDispatch class MiddlewareStack < Array class Middleware - def self.new(klass, *args, &block) - if klass.is_a?(self) - klass - else - super - end - end - attr_reader :args, :block def initialize(klass, *args, &block) - @klass = klass - - options = args.extract_options! - if options.has_key?(:if) - @conditional = options.delete(:if) - else - @conditional = true - end - args << options unless options.empty? - - @args = args - @block = block + @klass, @args, @block = klass, args, block end def klass - if @klass.respond_to?(:new) - @klass - elsif @klass.respond_to?(:call) - @klass.call - else - ActiveSupport::Inflector.constantize(@klass.to_s) - end - end - - def active? - return false unless klass - - if @conditional.respond_to?(:call) - @conditional.call - else - @conditional - end + return @klass if @klass.respond_to?(:new) + @klass = ActiveSupport::Inflector.constantize(@klass.to_s) end def ==(middleware) @@ -58,7 +24,7 @@ module ActionDispatch if lazy_compare?(@klass) && lazy_compare?(middleware) normalize(@klass) == normalize(middleware) else - klass.name == middleware.to_s + klass.name == normalize(middleware.to_s) end end end @@ -68,25 +34,18 @@ module ActionDispatch end def build(app) - if block - klass.new(app, *build_args, &block) - else - klass.new(app, *build_args) - end + klass.new(app, *args, &block) end - private - def lazy_compare?(object) - object.is_a?(String) || object.is_a?(Symbol) - end + private - def normalize(object) - object.to_s.strip.sub(/^::/, '') - end + def lazy_compare?(object) + object.is_a?(String) || object.is_a?(Symbol) + end - def build_args - Array(args).map { |arg| arg.respond_to?(:call) ? arg.call : arg } - end + def normalize(object) + object.to_s.strip.sub(/^::/, '') + end end def initialize(*args, &block) @@ -96,7 +55,7 @@ module ActionDispatch def insert(index, *args, &block) index = self.index(index) unless index.is_a?(Integer) - middleware = Middleware.new(*args, &block) + middleware = self.class::Middleware.new(*args, &block) super(index, middleware) end @@ -114,20 +73,19 @@ module ActionDispatch end def use(*args, &block) - middleware = Middleware.new(*args, &block) + middleware = self.class::Middleware.new(*args, &block) push(middleware) end def active - find_all { |middleware| middleware.active? } + ActiveSupport::Deprecation.warn "All middlewares in the chaing are active since the laziness " << + "was removed from the middleware stack", caller end - def build(app = nil, &blk) - app ||= blk - + def build(app = nil, &block) + app ||= block raise "MiddlewareStack#build requires an app" unless app - - active.reverse.inject(app) { |a, e| e.build(a) } + reverse.inject(app) { |a, e| e.build(a) } end end end diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 735398d972..f4af763afe 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -165,6 +165,10 @@ module ActionView #:nodoc: cattr_accessor :debug_rjs @@debug_rjs = false + # Specify the proc used to decorate input tags that refer to attributes with errors. + cattr_accessor :field_error_proc + @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe } + class_attribute :helpers remove_method :helpers attr_reader :helpers diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb index 0cddd09eb0..8054de0af6 100644 --- a/actionpack/lib/action_view/helpers/active_model_helper.rb +++ b/actionpack/lib/action_view/helpers/active_model_helper.rb @@ -4,13 +4,6 @@ require 'active_support/core_ext/enumerable' require 'active_support/core_ext/object/blank' module ActionView - ActiveSupport.on_load(:action_view) do - class ActionView::Base - @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe } - cattr_accessor :field_error_proc - end - end - module Helpers module ActiveModelHelper %w(input form error_messages_for error_message_on).each do |method| diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 0e1bc139ff..bfad9f8d31 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/string/filters' require 'action_view/helpers/tag_helper' module ActionView @@ -42,28 +43,25 @@ module ActionView # ==== Examples # # truncate("Once upon a time in a world far far away") - # # => Once upon a time in a world... + # # => "Once upon a time in a world..." # - # truncate("Once upon a time in a world far far away", :separator => ' ') - # # => Once upon a time in a world... + # truncate("Once upon a time in a world far far away", :length => 17) + # # => "Once upon a ti..." # - # truncate("Once upon a time in a world far far away", :length => 14) - # # => Once upon a... + # truncate("Once upon a time in a world far far away", :lenght => 17, :separator => ' ') + # # => "Once upon a..." # - # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)") - # # => And they found t(clipped) - # - # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 25) - # # => And they f... (continued) + # truncate("And they found that many people were sleeping better.", :length => 25, :omission => '... (continued)') + # # => "And they f... (continued)" # # You can still use <tt>truncate</tt> with the old API that accepts the # +length+ as its optional second and the +ellipsis+ as its # optional third parameter: # truncate("Once upon a time in a world far far away", 14) - # # => Once upon a... + # # => "Once upon a..." # # truncate("And they found that many people were sleeping better.", 25, "... (continued)") - # # => And they f... (continued) + # # => "And they f... (continued)" def truncate(text, *args) options = args.extract_options! unless args.empty? @@ -73,14 +71,10 @@ module ActionView options[:length] = args[0] || 30 options[:omission] = args[1] || "..." end - options.reverse_merge!(:length => 30, :omission => "...") - if text - l = options[:length] - options[:omission].mb_chars.length - chars = text.mb_chars - stop = options[:separator] ? (chars.rindex(options[:separator].mb_chars, l) || l) : l - (chars.length > options[:length] ? chars[0...stop] + options[:omission] : text).to_s - end + options.reverse_merge!(:length => 30) + + text.truncate(options.delete(:length), options) if text end # Highlights one or more +phrases+ everywhere in +text+ by inserting it into @@ -537,9 +531,14 @@ module ActionView end AUTO_LINK_RE = %r{ - ( https?:// | www\. ) + (?: ([\w+.:-]+:)// | www\. ) [^\s<]+ - }x unless const_defined?(:AUTO_LINK_RE) + }x + + # regexps for determining context, used high-volume + AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i] + + AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/ BRACKETS = { ']' => '[', ')' => '(', '}' => '{' } @@ -548,11 +547,10 @@ module ActionView def auto_link_urls(text, html_options = {}) link_attributes = html_options.stringify_keys text.gsub(AUTO_LINK_RE) do - href = $& + scheme, href = $1, $& punctuation = [] - left, right = $`, $' - # detect already linked URLs and URLs in the middle of a tag - if left =~ /<[^>]+$/ && right =~ /^[^>]*>/ + + if auto_linked?($`, $') # do not change string; URL is already linked href else @@ -566,9 +564,9 @@ module ActionView end link_text = block_given?? yield(href) : href - href = 'http://' + href unless href =~ %r{^[a-z]+://}i + href = 'http://' + href unless scheme - content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation.reverse.join('') + content_tag(:a, link_text, link_attributes.merge('href' => href)) + punctuation.reverse.join('') end end end @@ -576,11 +574,10 @@ module ActionView # Turns all email addresses into clickable links. If a block is given, # each email is yielded and the result is used as the link text. def auto_link_email_addresses(text, html_options = {}) - body = text.dup - text.gsub(/([\w\.!#\$%\-+]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do - text = $1 + text.gsub(AUTO_EMAIL_RE) do + text = $& - if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/) + if auto_linked?($`, $') text else display_text = (block_given?) ? yield(text) : text @@ -588,6 +585,12 @@ module ActionView end end end + + # Detects already linked context or position in the middle of a tag + def auto_linked?(left, right) + (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or + (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3]) + end end end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 5d8ac6b115..53ad24fdc6 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -22,6 +22,10 @@ module ActionView # users will see diamonds with question marks in them in # the browser. # + # For the rest of this documentation, when we say "UTF-8", + # we mean "UTF-8 or whatever the default_internal encoding + # is set to". By default, it will be UTF-8. + # # To mitigate this problem, we use a few strategies: # 1. If the source is not valid UTF-8, we raise an exception # when the template is compiled to alert the user @@ -32,8 +36,7 @@ module ActionView # to the resulting compiled source returned by the # template handler. # 3. In all cases, we transcode the resulting String to - # the <tt>default_internal</tt> encoding (which defaults - # to UTF-8). + # the UTF-8. # # This means that other parts of Rails can always assume # that templates are encoded in UTF-8, even if the original @@ -60,14 +63,14 @@ module ActionView # # If you want to provide an alternate mechanism for # specifying encodings (like ERB does via <%# encoding: ... %>), - # you may indicate that you are willing to accept - # BINARY data by implementing <tt>self.accepts_binary?</tt> + # you may indicate that you will handle encodings yourself + # by implementing <tt>self.handles_encoding?</tt> # on your handler. # - # If you do, Rails will not raise an exception if - # the template's encoding could not be determined, - # assuming that you have another mechanism for - # making the determination. + # If you do, Rails will not try to encode the String + # into the default_internal, passing you the unaltered + # bytes tagged with the assumed encoding (from + # default_external). # # In this case, make sure you return a String from # your handler encoded in the default_internal. Since @@ -171,7 +174,12 @@ module ActionView # before passing the source on to the template engine, leaving a # blank line in its stead. # - # Note that after we figure out the correct encoding, we then + # If the template engine handles encodings, we send the encoded + # String to the engine without further processing. This allows + # the template engine to support additional mechanisms for + # specifying the encoding. For instance, ERB supports <%# encoding: %> + # + # Otherwise, after we figure out the correct encoding, we then # encode the source into Encoding.default_internal. In general, # this means that templates will be UTF-8 inside of Rails, # regardless of the original source encoding. @@ -182,8 +190,11 @@ module ActionView locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join if source.encoding_aware? + # Look for # encoding: *. If we find one, we'll encode the + # String in that encoding, otherwise, we'll use the + # default external encoding. if source.sub!(/\A#{ENCODING_FLAG}/, '') - encoding = $1 + encoding = magic_encoding = $1 else encoding = Encoding.default_external end @@ -192,34 +203,28 @@ module ActionView # or the encoding specified in the file source.force_encoding(encoding) - # If the original encoding is BINARY, the actual - # encoding is either stored out-of-band (such as - # in ERB <%# %> style magic comments) or missing. - # This is also true if the original encoding is - # something other than BINARY, but it's invalid. - if source.encoding != Encoding::BINARY && source.valid_encoding? + # If the user didn't specify an encoding, and the handler + # handles encodings, we simply pass the String as is to + # the handler (with the default_external tag) + if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding? + source + # Otherwise, if the String is valid in the encoding, + # encode immediately to default_internal. This means + # that if a handler doesn't handle encodings, it will + # always get Strings in the default_internal + elsif source.valid_encoding? source.encode! - # If the assumed encoding is incorrect, check to - # see whether the handler accepts BINARY. If it - # does, it has another mechanism for determining - # the true encoding of the String. - elsif @handler.respond_to?(:accepts_binary?) && @handler.accepts_binary? - source.force_encoding(Encoding::BINARY) - # If the handler does not accept BINARY, the - # assumed encoding (either the default_external, - # or the explicit encoding specified by the user) - # is incorrect. We raise an exception here. + # Otherwise, since the String is invalid in the encoding + # specified, raise an exception else raise WrongEncodingError.new(source, encoding) end - - # Don't validate the encoding yet -- the handler - # may treat the String as raw bytes and extract - # the encoding some other way end code = @handler.call(self) + # Make sure that the resulting String to be evalled is in the + # encoding of the code source = <<-end_src def #{method_name}(local_assigns) _old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code} @@ -229,20 +234,16 @@ module ActionView end_src if source.encoding_aware? - # Handlers should return their source Strings in either the - # default_internal or BINARY. If the handler returns a BINARY - # String, we assume its encoding is the one we determined - # earlier, and encode the resulting source in the default_internal. - if source.encoding == Encoding::BINARY - source.force_encoding(Encoding.default_internal) - end + # Make sure the source is in the encoding of the returned code + source.force_encoding(code.encoding) # In case we get back a String from a handler that is not in # BINARY or the default_internal, encode it to the default_internal source.encode! # Now, validate that the source we got back from the template - # handler is valid in the default_internal + # handler is valid in the default_internal. This is for handlers + # that handle encoding but screw up unless source.valid_encoding? raise WrongEncodingError.new(@source, Encoding.default_internal) end diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index d3a53d2147..e50de7e5af 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -13,6 +13,7 @@ module ActionView end def message + @string.force_encoding("BINARY") "Your template was not saved as valid #{@encoding}. Please " \ "either specify #{@encoding} as the encoding for your template " \ "in your text editor, or mark the template with its " \ diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index cbed0108cf..ce609e01af 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -79,51 +79,49 @@ module ActionView ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*") - def self.accepts_binary? + def self.handles_encoding? true end def compile(template) if template.source.encoding_aware? - # Even though Rails has given us a String tagged with the - # default_internal encoding (likely UTF-8), it is possible - # that the String is actually encoded using a different - # encoding, specified via an ERB magic comment. If the - # String is not actually UTF-8, the regular expression - # engine will (correctly) raise an exception. For now, - # we'll reset the String to BINARY so we can run regular - # expressions against it + # First, convert to BINARY, so in case the encoding is + # wrong, we can still find an encoding tag + # (<%# encoding %>) inside the String using a regular + # expression template_source = template.source.dup.force_encoding("BINARY") - # Erubis does not have direct support for encodings. - # As a result, we will extract the ERB-style magic - # comment, give the String to Erubis as BINARY data, - # and then tag the resulting String with the extracted - # encoding later erb = template_source.gsub(ENCODING_TAG, '') encoding = $2 - if !encoding && (template.source.encoding == Encoding::BINARY) - raise WrongEncodingError.new(template_source, Encoding.default_external) - end + erb.force_encoding valid_encoding(template.source.dup, encoding) + + # Always make sure we return a String in the default_internal + erb.encode! else erb = template.source.dup end - result = self.class.erb_implementation.new( + self.class.erb_implementation.new( erb, :trim => (self.class.erb_trim_mode == "-") ).src + end + + private + def valid_encoding(string, encoding) + # If a magic encoding comment was found, tag the + # String with this encoding. This is for a case + # where the original String was assumed to be, + # for instance, UTF-8, but a magic comment + # proved otherwise + string.force_encoding(encoding) if encoding + + # If the String is valid, return the encoding we found + return string.encoding if string.valid_encoding? - # If an encoding tag was found, tag the String - # we're returning with that encoding. Otherwise, - # return a BINARY String, which is what ERB - # returns. Note that if a magic comment was - # not specified, we will return the data to - # Rails as BINARY, which will then use its - # own encoding logic to create a UTF-8 String. - result = "\n#{result}".force_encoding(encoding).encode if encoding - result + # Otherwise, raise an exception + raise WrongEncodingError.new(string, string.encoding) end end end diff --git a/actionpack/test/controller/new_base/middleware_test.rb b/actionpack/test/controller/new_base/middleware_test.rb index 65942ebc15..26a66c91a6 100644 --- a/actionpack/test/controller/new_base/middleware_test.rb +++ b/actionpack/test/controller/new_base/middleware_test.rb @@ -28,7 +28,6 @@ module MiddlewareTest class MyController < ActionController::Metal use MyMiddleware - middleware.insert_before MyMiddleware, ExclaimerMiddleware def index @@ -39,8 +38,23 @@ module MiddlewareTest class InheritedController < MyController end - module MiddlewareTests - extend ActiveSupport::Testing::Declarative + class ActionsController < ActionController::Metal + use MyMiddleware, :only => :show + middleware.insert_before MyMiddleware, ExclaimerMiddleware, :except => :index + + def index + self.response_body = "index" + end + + def show + self.response_body = "show" + end + end + + class TestMiddleware < ActiveSupport::TestCase + def setup + @app = MyController.action(:index) + end test "middleware that is 'use'd is called as part of the Rack application" do result = @app.call(env_for("/")) @@ -52,13 +66,13 @@ module MiddlewareTest result = @app.call(env_for("/")) assert_equal "First!", result[1]["Middleware-Order"] end - end - class TestMiddleware < ActiveSupport::TestCase - include MiddlewareTests + test "middleware stack accepts only and except as options" do + result = ActionsController.action(:show).call(env_for("/")) + assert_equal "First!", result[1]["Middleware-Order"] - def setup - @app = MyController.action(:index) + result = ActionsController.action(:index).call(env_for("/")) + assert_nil result[1]["Middleware-Order"] end def env_for(url) @@ -70,8 +84,5 @@ module MiddlewareTest def setup @app = InheritedController.action(:index) end - - test "middleware inherits" do - end end end diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index 7cf6365af3..170c5b8565 100644 --- a/actionpack/test/dispatch/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -66,29 +66,14 @@ class MiddlewareStackTest < ActiveSupport::TestCase assert_equal BazMiddleware, @stack[0].klass end - test "active returns all only enabled middleware" do - assert_no_difference "@stack.active.size" do - assert_difference "@stack.size" do - @stack.use BazMiddleware, :if => lambda { false } - end - end - end - test "lazy evaluates middleware class" do assert_difference "@stack.size" do - @stack.use lambda { BazMiddleware } + @stack.use "MiddlewareStackTest::BazMiddleware" end assert_equal BazMiddleware, @stack.last.klass end - test "lazy evaluates middleware arguments" do - assert_difference "@stack.size" do - @stack.use BazMiddleware, lambda { :foo } - end - assert_equal [:foo], @stack.last.send(:build_args) - end - - test "lazy compares so unloaded constants can be loaded" do + test "lazy compares so unloaded constants are not loaded" do @stack.use "UnknownMiddleware" @stack.use :"MiddlewareStackTest::BazMiddleware" assert @stack.include?("::MiddlewareStackTest::BazMiddleware") diff --git a/actionpack/test/fixtures/test/scoped_translation.erb b/actionpack/test/fixtures/test/scoped_translation.erb index 30baa61c77..3be63ab3cc 100644 --- a/actionpack/test/fixtures/test/scoped_translation.erb +++ b/actionpack/test/fixtures/test/scoped_translation.erb @@ -1 +1 @@ -<%= t('.foo.bar') %>
\ No newline at end of file +<%= t('.foo.bar').join %>
\ No newline at end of file diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb index 995d728d50..18e0e83ec3 100644 --- a/actionpack/test/template/template_test.rb +++ b/actionpack/test/template/template_test.rb @@ -114,10 +114,12 @@ class TestERBTemplate < ActiveSupport::TestCase end def test_encoding_can_be_specified_with_magic_comment_in_erb - @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat") - result = render - assert_equal Encoding::UTF_8, render.encoding - assert_equal "hello \u{fc}mlat", render + with_external_encoding Encoding::UTF_8 do + @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat") + result = render + assert_equal Encoding::UTF_8, render.encoding + assert_equal "hello \u{fc}mlat", render + end end def test_error_when_template_isnt_valid_utf8 @@ -126,5 +128,12 @@ class TestERBTemplate < ActiveSupport::TestCase render end end + + def with_external_encoding(encoding) + old, Encoding.default_external = Encoding.default_external, encoding + yield + ensure + Encoding.default_external = old + end end end
\ No newline at end of file diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 8b6c107a21..bb808b77a5 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -84,8 +84,9 @@ class TextHelperTest < ActionView::TestCase end else def test_truncate_multibyte - assert_equal "\354\225\210\353\205\225\355...", - truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10) + # .mb_chars always returns a UTF-8 String. + # assert_equal "\354\225\210\353\205\225\355...", + # truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10) assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'), truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10) @@ -218,7 +219,8 @@ class TextHelperTest < ActionView::TestCase else def test_excerpt_with_utf8 assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', 8)) - assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', 8)) + # .mb_chars always returns UTF-8, even in 1.9. This is not great, but it's how it works. Let's work this out. + # assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped".force_encoding("BINARY"), 'could', 8)) end end @@ -304,6 +306,7 @@ class TextHelperTest < ActionView::TestCase assert_equal %(<p>Link #{link_result_with_options}</p>), auto_link("<p>Link #{link_raw}</p>", :all, {:target => "_blank"}) assert_equal %(Go to #{link_result}.), auto_link(%(Go to #{link_raw}.)) assert_equal %(<p>Go to #{link_result}, then say hello to #{email_result}.</p>), auto_link(%(<p>Go to #{link_raw}, then say hello to #{email_raw}.</p>)) + assert_equal %(#{link_result} #{link_result}), auto_link(%(#{link_result} #{link_raw})) email2_raw = '+david@loudthinking.com' email2_result = %{<a href="mailto:#{email2_raw}">#{email2_raw}</a>} @@ -376,24 +379,38 @@ class TextHelperTest < ActionView::TestCase end def test_auto_link_other_protocols - silence_warnings do - begin - old_re_value = ActionView::Helpers::TextHelper::AUTO_LINK_RE - ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, %r{(ftp://)[^\s<]+} - link_raw = 'ftp://example.com/file.txt' - link_result = generate_result(link_raw) - assert_equal %(Download #{link_result}), auto_link("Download #{link_raw}") - ensure - ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, old_re_value - end - end + ftp_raw = 'ftp://example.com/file.txt' + assert_equal %(Download #{generate_result(ftp_raw)}), auto_link("Download #{ftp_raw}") + + file_scheme = 'file:///home/username/RomeoAndJuliet.pdf' + z39_scheme = 'z39.50r://host:696/db' + chrome_scheme = 'chrome://package/section/path' + view_source = 'view-source:http://en.wikipedia.org/wiki/URI_scheme' + assert_equal generate_result(z39_scheme), auto_link(z39_scheme) + assert_equal generate_result(chrome_scheme), auto_link(chrome_scheme) + assert_equal generate_result(view_source), auto_link(view_source) end def test_auto_link_already_linked linked1 = generate_result('Ruby On Rails', 'http://www.rubyonrails.com') - linked2 = generate_result('www.rubyonrails.com', 'http://www.rubyonrails.com') + linked2 = %('<a href="http://www.example.com">www.example.com</a>') + linked3 = %('<a href="http://www.example.com" rel="nofollow">www.example.com</a>') + linked4 = %('<a href="http://www.example.com"><b>www.example.com</b></a>') + linked5 = %('<a href="#close">close</a> <a href="http://www.example.com"><b>www.example.com</b></a>') assert_equal linked1, auto_link(linked1) assert_equal linked2, auto_link(linked2) + assert_equal linked3, auto_link(linked3) + assert_equal linked4, auto_link(linked4) + assert_equal linked5, auto_link(linked5) + + linked_email = %Q(<a href="mailto:david@loudthinking.com">Mail me</a>) + assert_equal linked_email, auto_link(linked_email) + end + + def test_auto_link_within_tags + link_raw = 'http://www.rubyonrails.org/images/rails.png' + link_result = %Q(<img src="#{link_raw}" />) + assert_equal link_result, auto_link(link_result) end def test_auto_link_with_brackets @@ -413,12 +430,6 @@ class TextHelperTest < ActionView::TestCase assert_equal "{link: #{link3_result}}", auto_link("{link: #{link3_raw}}") end - def test_auto_link_in_tags - link_raw = 'http://www.rubyonrails.org/images/rails.png' - link_result = %Q(<img src="#{link_raw}" />) - assert_equal link_result, auto_link(link_result) - end - def test_auto_link_at_eol url1 = "http://api.rubyonrails.com/Foo.html" url2 = "http://www.ruby-doc.org/core/Bar.html" @@ -432,6 +443,19 @@ class TextHelperTest < ActionView::TestCase assert_equal %(<p><a href="#{url}">#{url[0...7]}...</a><br /><a href="mailto:#{email}">#{email[0...7]}...</a><br /></p>), auto_link("<p>#{url}<br />#{email}<br /></p>") { |url| truncate(url, :length => 10) } end + + def test_auto_link_with_block_with_html + pic = "http://example.com/pic.png" + url = "http://example.com/album?a&b=c" + + assert_equal %(My pic: <a href="#{pic}"><img src="#{pic}" width="160px"></a> -- full album here #{generate_result(url)}), auto_link("My pic: #{pic} -- full album here #{url}") { |link| + if link =~ /\.(jpg|gif|png|bmp|tif)$/i + raw %(<img src="#{link}" width="160px">) + else + link + end + } + end def test_auto_link_with_options_hash assert_dom_equal 'Welcome to my new blog at <a href="http://www.myblog.com/" class="menu" target="_blank">http://www.myblog.com/</a>. Please e-mail me at <a href="mailto:me@email.com" class="menu" target="_blank">me@email.com</a>.', diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index 9695911398..678007c0ef 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -20,4 +20,6 @@ Gem::Specification.new do |s| s.has_rdoc = true s.add_dependency('activesupport', version) + s.add_dependency('builder', '~> 2.1.2') + s.add_dependency('i18n', '~> 0.4.0') end diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 14afc5265f..15d468f5d8 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -170,13 +170,13 @@ module ActiveModel end end - # Adds an error message (+messsage+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt> - # for the same attribute and ensure that this error object returns false when asked if <tt>empty?</tt>. More than one - # error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>. - # If no +messsage+ is supplied, :invalid is assumed. + # Adds +message+ to the error messages on +attribute+, which will be returned on a call to + # <tt>on(attribute)</tt> for the same attribute. More than one error can be added to the same + # +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>. + # If no +message+ is supplied, <tt>:invalid</tt> is assumed. # - # If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error). - # If +message+ is a Proc, it will be called, allowing for things like Time.now to be used within an error + # If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+). + # If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> 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) diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 59caa53be0..44c8fb83e1 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -24,4 +24,5 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) s.add_dependency('arel', '~> 0.3.3') + s.add_dependency('tzinfo', '~> 0.3.16') end diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index cbebded995..9601ed6afd 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -16,11 +16,15 @@ module ActiveRecord def reset_counters(id, *counters) object = find(id) counters.each do |association| - child_class = reflect_on_association(association).klass - counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column + child_class = reflect_on_association(association.to_sym).klass + belongs_name = self.name.demodulize.underscore.to_sym + counter_name = child_class.reflect_on_association(belongs_name).counter_cache_column - connection.update("UPDATE #{quoted_table_name} SET #{connection.quote_column_name(counter_name)} = #{object.send(association).count} WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)}", "#{name} UPDATE") + self.unscoped.where(arel_table[self.primary_key].eq(object.id)).arel.update({ + arel_table[counter_name] => object.send(association).count + }) end + return true end # A generic "counter updater" implementation, intended primarily to be @@ -53,19 +57,13 @@ module ActiveRecord # # SET comment_count = comment_count + 1, # # WHERE id IN (10, 15) def update_counters(id, counters) - updates = counters.inject([]) { |list, (counter_name, increment)| - sign = increment < 0 ? "-" : "+" - list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}" - }.join(", ") - - if id.is_a?(Array) - ids_list = id.map {|i| quote_value(i)}.join(', ') - condition = "IN (#{ids_list})" - else - condition = "= #{quote_value(id)}" + updates = counters.map do |counter_name, value| + operator = value < 0 ? '-' : '+' + quoted_column = connection.quote_column_name(counter_name) + "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}" end - update_all(updates, "#{connection.quote_column_name(primary_key)} #{condition}") + update_all(updates.join(', '), primary_key => id ) end # Increment a number field by one, usually representing a count. diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 0c7723c0e6..1d883f7ea8 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -708,55 +708,6 @@ class BasicsTest < ActiveRecord::TestCase assert Topic.find(2).approved? end - def test_increment_counter - Topic.increment_counter("replies_count", 1) - assert_equal 2, Topic.find(1).replies_count - - Topic.increment_counter("replies_count", 1) - assert_equal 3, Topic.find(1).replies_count - end - - def test_decrement_counter - Topic.decrement_counter("replies_count", 2) - assert_equal(-1, Topic.find(2).replies_count) - - Topic.decrement_counter("replies_count", 2) - assert_equal(-2, Topic.find(2).replies_count) - end - - def test_reset_counters - assert_equal 1, Topic.find(1).replies_count - - Topic.increment_counter("replies_count", 1) - assert_equal 2, Topic.find(1).replies_count - - Topic.reset_counters(1, :replies) - assert_equal 1, Topic.find(1).replies_count - end - - def test_update_counter - category = categories(:general) - assert_nil category.categorizations_count - assert_equal 2, category.categorizations.count - - Category.update_counters(category.id, "categorizations_count" => category.categorizations.count) - category.reload - assert_not_nil category.categorizations_count - assert_equal 2, category.categorizations_count - - Category.update_counters(category.id, "categorizations_count" => category.categorizations.count) - category.reload - assert_not_nil category.categorizations_count - assert_equal 4, category.categorizations_count - - category_2 = categories(:technology) - count_1, count_2 = (category.categorizations_count || 0), (category_2.categorizations_count || 0) - Category.update_counters([category.id, category_2.id], "categorizations_count" => 2) - category.reload; category_2.reload - assert_equal count_1 + 2, category.categorizations_count - assert_equal count_2 + 2, category_2.categorizations_count - end - def test_update_all assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'") assert_equal "bulk updated!", Topic.find(1).content diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb new file mode 100755 index 0000000000..377de168b9 --- /dev/null +++ b/activerecord/test/cases/counter_cache_test.rb @@ -0,0 +1,83 @@ +require 'cases/helper' +require 'models/topic' +require 'models/reply' +require 'models/category' +require 'models/categorization' + +class CounterCacheTest < ActiveRecord::TestCase + fixtures :topics, :categories, :categorizations + + class SpecialTopic < ::Topic + has_many :special_replies, :foreign_key => 'parent_id' + end + + class SpecialReply < ::Reply + belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count' + end + + setup do + @topic = Topic.find(1) + end + + test "increment counter" do + assert_difference '@topic.reload.replies_count' do + Topic.increment_counter(:replies_count, @topic.id) + end + end + + test "decrement counter" do + assert_difference '@topic.reload.replies_count', -1 do + Topic.decrement_counter(:replies_count, @topic.id) + end + end + + test "reset counters" do + # throw the count off by 1 + Topic.increment_counter(:replies_count, @topic.id) + + # check that it gets reset + assert_difference '@topic.reload.replies_count', -1 do + Topic.reset_counters(@topic.id, :replies) + end + end + + test "reset counters with string argument" do + Topic.increment_counter('replies_count', @topic.id) + + assert_difference '@topic.reload.replies_count', -1 do + Topic.reset_counters(@topic.id, 'replies') + end + end + + test "reset counters with modularized and camelized classnames" do + special = SpecialTopic.create!(:title => 'Special') + SpecialTopic.increment_counter(:replies_count, special.id) + + assert_difference 'special.reload.replies_count', -1 do + SpecialTopic.reset_counters(special.id, :special_replies) + end + end + + test "update counter with initial null value" do + category = categories(:general) + assert_equal 2, category.categorizations.count + assert_nil category.categorizations_count + + Category.update_counters(category.id, :categorizations_count => category.categorizations.count) + assert_equal 2, category.reload.categorizations_count + end + + test "update counter for decrement" do + assert_difference '@topic.reload.replies_count', -3 do + Topic.update_counters(@topic.id, :replies_count => -3) + end + end + + test "update counters of multiple records" do + t1, t2 = topics(:first, :second) + + assert_difference ['t1.reload.replies_count', 't2.reload.replies_count'], 2 do + Topic.update_counters([t1.id, t2.id], :replies_count => 2) + end + end +end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 0a25ad0406..d853788e00 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [beta 4/release candidate] (unreleased)* +* Extracted String#truncate from TextHelper#truncate [DHH] + * Ruby 1.9: support UTF-8 case folding. #4595 [Norman Clarke] * Renames Array#rand -> Array#random_element. [Santiago Pastorino, Rizwan Reza] diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index cfd85f61c9..8611a1e5fa 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -18,9 +18,4 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.has_rdoc = true - - s.add_dependency('i18n', '~> 0.4.0.beta1') - s.add_dependency('tzinfo', '~> 0.3.16') - s.add_dependency('builder', '~> 2.1.2') - s.add_dependency('memcache-client', '>= 1.7.5') end diff --git a/activesupport/lib/active_support/builder.rb b/activesupport/lib/active_support/builder.rb new file mode 100644 index 0000000000..321e462acd --- /dev/null +++ b/activesupport/lib/active_support/builder.rb @@ -0,0 +1,6 @@ +begin + require 'builder' +rescue LoadError => e + $stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index d8377a208f..e3a2688e2f 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -1,4 +1,9 @@ -require 'memcache' +begin + require 'memcache' +rescue LoadError => e + $stderr.puts "You don't have memcache installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end require 'digest/md5' module ActiveSupport diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 2b07f05d27..79e3828817 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -9,9 +9,15 @@ class Array # * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ") # * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ") def to_sentence(options = {}) - default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale]) - default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale]) - default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) + if defined?(I18n) + default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale]) + default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale]) + default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) + else + default_words_connector = ", " + default_two_words_connector = " and " + default_last_word_connector = ", and " + end options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale) options.reverse_merge! :words_connector => default_words_connector, :two_words_connector => default_two_words_connector, :last_word_connector => default_last_word_connector @@ -128,7 +134,7 @@ class Array # </messages> # def to_xml(options = {}) - require 'builder' unless defined?(Builder) + require 'active_support/builder' unless defined?(Builder) options = options.dup options[:indent] ||= 2 diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 14e5d2f8ac..565c9af7fb 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -55,7 +55,7 @@ class Hash # configure your own builder with the <tt>:builder</tt> option. The method also accepts # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder. def to_xml(options = {}) - require 'builder' unless defined?(Builder) + require 'active_support/builder' unless defined?(Builder) options = options.dup options[:indent] ||= 2 diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb index 6fda7efef5..e15a1df9c9 100644 --- a/activesupport/lib/active_support/core_ext/string/filters.rb +++ b/activesupport/lib/active_support/core_ext/string/filters.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/multibyte' + class String # Returns the string, first removing all whitespace on both ends of # the string, and then changing remaining consecutive whitespace @@ -17,4 +19,31 @@ class String gsub!(/\s+/, ' ') self end + + # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>: + # + # "Once upon a time in a world far far away".truncate(27) + # # => "Once upon a time in a wo..." + # + # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...") + # for a total length not exceeding <tt>:length</tt>: + # + # "Once upon a time in a world far far away".truncate(27, :separator => ' ') + # # => "Once upon a time in a..." + # + # Pass a <tt>:separator</tt> to truncate +text+ at a natural break: + # + # "And they found that many people were sleeping better.".truncate(25, :omission => "... (continued)") + # # => "And they f... (continued)" + def truncate(length, options = {}) + text = self.dup + options[:omission] ||= "..." + + length_with_room_for_omission = length - options[:omission].mb_chars.length + chars = text.mb_chars + stop = options[:separator] ? + (chars.rindex(options[:separator].mb_chars, length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission + + (chars.length > length ? chars[0...stop] + options[:omission] : text).to_s + end end diff --git a/activesupport/lib/active_support/core_ext/string/interpolation.rb b/activesupport/lib/active_support/core_ext/string/interpolation.rb index 932117cc10..7f764e9de1 100644 --- a/activesupport/lib/active_support/core_ext/string/interpolation.rb +++ b/activesupport/lib/active_support/core_ext/string/interpolation.rb @@ -1 +1,2 @@ +require 'active_support/i18n' require 'i18n/core_ext/string/interpolate' diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 11af48d67e..0ffdd904fd 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -1,3 +1,8 @@ -require 'i18n' +begin + require 'i18n' +rescue LoadError => e + $stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" -ActiveSupport.run_load_hooks(:i18n)
\ No newline at end of file +ActiveSupport.run_load_hooks(:i18n) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 2ac5134911..67b37785f5 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -1,5 +1,11 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/try' +begin + require 'tzinfo' +rescue LoadError => e + $stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install" + raise e +end # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following: # @@ -313,7 +319,7 @@ module ActiveSupport # TODO: Preload instead of lazy load for thread safety def self.find_tzinfo(name) - require 'tzinfo' unless defined?(::TZInfo) + require 'active_support/tzinfo' unless defined?(::TZInfo) ::TZInfo::TimezoneProxy.new(MAPPING[name] || name) end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 759b0ddcd6..d9702dd9ff 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -210,6 +210,35 @@ class StringInflectionsTest < Test::Unit::TestCase # And changes the original string: assert_equal original, expected end + + def test_truncate + assert_equal "Hello World!", "Hello World!".truncate(12) + assert_equal "Hello Wor...", "Hello World!!".truncate(12) + end + + def test_truncate_with_omission_and_seperator + assert_equal "Hello[...]", "Hello World!".truncate(10, :omission => "[...]") + assert_equal "Hello[...]", "Hello Big World!".truncate(13, :omission => "[...]", :separator => ' ') + assert_equal "Hello Big[...]", "Hello Big World!".truncate(14, :omission => "[...]", :separator => ' ') + assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, :omission => "[...]", :separator => ' ') + end + + if RUBY_VERSION < '1.9.0' + def test_truncate_multibyte + with_kcode 'none' do + assert_equal "\354\225\210\353\205\225\355...", "\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224".truncate(10) + end + with_kcode 'u' do + assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...", + "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".truncate(10) + end + end + else + def test_truncate_multibyte + assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'), + "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8').truncate(10) + end + end end class StringBehaviourTest < Test::Unit::TestCase diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index bd2289890a..ab72b48034 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -63,8 +63,6 @@ h4. Rails General Configuration * +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -* +config.metals+ accepts an array used as the metals to load. If this is set to nil, all metals will be loaded in alphabetical order. If this is set to [], no metals will be loaded. Otherwise metals will be loaded in the order specified - * +config.plugin_loader+ overrides the class that handles loading each plugin. Defaults to +Rails::Plugin::Loader+. * +config.plugin_locators+ overrides the class that handle finding the desired plugins that you‘d like to load for your application. By default it is the +Rails::Plugin::FileSystemLocator+. diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index cccbb9df06..58ae115ba7 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -668,7 +668,6 @@ This file requires _rails/railtie.rb_ which defines +Rails::Railtie+. * add_routing_namespaces * add_locales * add_view_paths -* add_metals * add_generator_templates * load_application_initializers * load_application_classes @@ -726,7 +725,6 @@ This file is used to set up the +Rails::Paths+ module which is used to set up he paths.app.helpers "app/helpers", :eager_load => true paths.app.models "app/models", :eager_load => true paths.app.mailers "app/mailers", :eager_load => true - paths.app.metals "app/metal", :eager_load => true paths.app.views "app/views", :eager_load => true paths.lib "lib", :load_path => true paths.lib.tasks "lib/tasks", :glob => "**/*.rake" @@ -3154,7 +3152,6 @@ This method is defined like this: middleware.use('ActionDispatch::Cookies') middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) middleware.use('ActionDispatch::Flash', :if => lambda { ActionController::Base.session_store }) - middleware.use(lambda { Rails::Rack::Metal.new(Rails.application.config.paths.app.metals.to_a, Rails.application.config.metals) }) middleware.use('ActionDispatch::ParamsParser') middleware.use('::Rack::MethodOverride') middleware.use('::ActionDispatch::Head') @@ -3288,7 +3285,7 @@ Finally, a +Rails::Application::Configuration+ object will be returned. On this <ruby> attr_accessor :after_initialize_blocks, :cache_classes, :colorize_logging, :consider_all_requests_local, :dependency_loading, - :load_once_paths, :logger, :metals, :plugins, + :load_once_paths, :logger, :plugins, :preload_frameworks, :reload_plugins, :serve_static_assets, :time_zone, :whiny_nils @@ -3574,7 +3571,6 @@ The +super+ method it references comes from +Rails::Engine::Configuration+ which paths.app.controllers "app/controllers", :eager_load => true paths.app.helpers "app/helpers", :eager_load => true paths.app.models "app/models", :eager_load => true - paths.app.metals "app/metal" paths.app.views "app/views" paths.lib "lib", :load_path => true paths.lib.tasks "lib/tasks", :glob => "**/*.rake" diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile index 512be43668..eaebb05f17 100644 --- a/railties/guides/source/rails_on_rack.textile +++ b/railties/guides/source/rails_on_rack.textile @@ -222,57 +222,6 @@ use MyOwnStackFromStratch run ActionController::Dispatcher.new </ruby> -h3. Rails Metal Applications - -Rails Metal applications are minimal Rack applications specially designed for integrating with a typical Rails application. As Rails Metal Applications skip all of the Action Controller stack, serving a request has no overhead from the Rails framework itself. This is especially useful for infrequent cases where the performance of the full stack Rails framework is an issue. - -Ryan Bates' "Railscast on Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal. - -h4. Generating a Metal Application - -Rails provides a generator called +metal+ for creating a new Metal application: - -<shell> -$ rails generate metal poller -</shell> - -This generates +poller.rb+ in the +app/metal+ directory: - -<ruby> -# Allow the metal piece to run in isolation -require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails) - -class Poller - def self.call(env) - if env["PATH_INFO"] =~ /^\/poller/ - [200, {"Content-Type" => "text/html"}, ["Hello, World!"]] - else - [404, {"Content-Type" => "text/html", "X-Cascade" => "pass"}, ["Not Found"]] - end - end -end -</ruby> - -Metal applications within +app/metal+ folders in plugins will also be discovered and added to the list. - -Metal applications are an optimization. You should make sure to "understand the related performance implications":http://weblog.rubyonrails.org/2008/12/20/performance-of-rails-metal before using it. - -WARNING: To continue the Metal chain execution, return an +X-Cascade+ HTTP header with a value of +pass+. - -h4. Execution Order - -All Metal Applications are executed in alphabetical order of their filenames, so +aaa.rb+ will come before +bbb.rb+ in the metal chain. - -You can override the default ordering in your environment. Simply add a line like the following to +config/application.rb+ - -It is, however, possible to override the default ordering in your environment. Simply add a line like the following to +config/environment.rb+ - -<ruby> -config.metals = ["Bbb", "Aaa"] -</ruby> - -Each string in the array should be the name of your metal class. If you do this then be warned that any metal applications not listed will not be loaded. - h3. Resources h4. Learning Rack diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index a3b3a56bc8..85ae8cbbb1 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -27,7 +27,7 @@ module Rails # Besides providing the same configuration as Rails::Engine and Rails::Railtie, # the application object has several specific configurations, for example # "allow_concurrency", "cache_classes", "consider_all_requests_local", "filter_parameters", - # "logger", "metals", "reload_engines", "reload_plugins" and so forth. + # "logger", "reload_engines", "reload_plugins" and so forth. # # Check Rails::Application::Configuration to see them all. # @@ -36,17 +36,15 @@ module Rails # The application object is also responsible for holding the routes and reloading routes # whenever the files change in development. # - # == Middlewares and metals + # == Middlewares # - # The Application is also responsible for building the middleware stack and setting up - # both application and engines metals. + # The Application is also responsible for building the middleware stack. # class Application < Engine autoload :Bootstrap, 'rails/application/bootstrap' autoload :Configurable, 'rails/application/configurable' autoload :Configuration, 'rails/application/configuration' autoload :Finisher, 'rails/application/finisher' - autoload :MetalLoader, 'rails/application/metal_loader' autoload :Railties, 'rails/application/railties' autoload :RoutesReloader, 'rails/application/routes_reloader' @@ -69,6 +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! ActiveSupport.run_load_hooks(:before_configuration, base.instance) end @@ -83,13 +82,23 @@ module Rails end end - delegate :middleware, :metal_loader, :to => :config + delegate :middleware, :to => :config + + def add_lib_to_load_paths! + path = config.root.join('lib').to_s + $LOAD_PATH.unshift(path) if File.exists?(path) + end def require_environment! environment = paths.config.environment.to_a.first require environment if environment end + def eager_load! + railties.all(&:eager_load!) + super + end + def routes @routes ||= ActionDispatch::Routing::RouteSet.new end @@ -126,7 +135,10 @@ module Rails end def app - @app ||= config.middleware.build(routes) + @app ||= begin + config.middleware = config.middleware.merge_into(default_middleware_stack) + config.middleware.build(routes) + end end def call(env) @@ -150,6 +162,29 @@ module Rails protected + def default_middleware_stack + ActionDispatch::MiddlewareStack.new.tap do |middleware| + middleware.use ::ActionDispatch::Static, paths.public.to_a.first if config.serve_static_assets + middleware.use ::Rack::Lock if !config.allow_concurrency + middleware.use ::Rack::Runtime + middleware.use ::Rails::Rack::Logger + middleware.use ::ActionDispatch::ShowExceptions, config.consider_all_requests_local if config.action_dispatch.show_exceptions + middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies + middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header + middleware.use ::ActionDispatch::Callbacks, !config.cache_classes + middleware.use ::ActionDispatch::Cookies + + if config.session_store + middleware.use config.session_store, config.session_options + middleware.use ::ActionDispatch::Flash + end + + middleware.use ::ActionDispatch::ParamsParser + middleware.use ::Rack::MethodOverride + middleware.use ::ActionDispatch::Head + end + end + def initialize_tasks require "rails/tasks" task :environment do diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index e62eed8a87..0a435f0f36 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -47,7 +47,7 @@ module Rails silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(config.cache_store) } if RAILS_CACHE.respond_to?(:middleware) - config.middleware.insert_after(:"Rack::Lock", RAILS_CACHE.middleware) + config.middleware.insert_before("Rack::Runtime", RAILS_CACHE.middleware) end end end diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 1b8af370f7..25e54e9dce 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -9,7 +9,7 @@ module Rails attr_accessor :allow_concurrency, :cache_classes, :cache_store, :encoding, :consider_all_requests_local, :dependency_loading, - :filter_parameters, :log_level, :logger, :metals, + :filter_parameters, :log_level, :logger, :middleware, :plugins, :preload_frameworks, :reload_engines, :reload_plugins, :secret_token, :serve_static_assets, :session_options, :time_zone, :whiny_nils @@ -25,6 +25,7 @@ module Rails @session_store = :cookie_store @session_options = {} @time_zone = "UTC" + @middleware = app_middleware end def encoding=(value) @@ -41,14 +42,6 @@ module Rails end end - def middleware - @middleware ||= app_middleware.merge_into(default_middleware_stack) - end - - def metal_loader - @metal_loader ||= Rails::Application::MetalLoader.new - end - def paths @paths ||= begin paths = super @@ -138,28 +131,6 @@ module Rails @session_options = args.shift || {} end end - - protected - - def default_middleware_stack - ActionDispatch::MiddlewareStack.new.tap do |middleware| - middleware.use('::ActionDispatch::Static', lambda { paths.public.to_a.first }, :if => lambda { serve_static_assets }) - middleware.use('::Rack::Lock', :if => lambda { !allow_concurrency }) - middleware.use('::Rack::Runtime') - middleware.use('::Rails::Rack::Logger') - middleware.use('::ActionDispatch::ShowExceptions', lambda { consider_all_requests_local }, :if => lambda { action_dispatch.show_exceptions }) - middleware.use('::ActionDispatch::RemoteIp', lambda { action_dispatch.ip_spoofing_check }, lambda { action_dispatch.trusted_proxies }) - middleware.use('::Rack::Sendfile', lambda { action_dispatch.x_sendfile_header }) - middleware.use('::ActionDispatch::Callbacks', lambda { !cache_classes }) - middleware.use('::ActionDispatch::Cookies') - middleware.use(lambda { session_store }, lambda { session_options }) - middleware.use('::ActionDispatch::Flash', :if => lambda { session_store }) - middleware.use('::ActionDispatch::ParamsParser') - middleware.use('::Rack::MethodOverride') - middleware.use('::ActionDispatch::Head') - middleware.use(lambda { metal_loader.build_middleware(metals) }, :if => lambda { metal_loader.metals.any? }) - end - end end end end diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index fbab4d5515..d7ff489336 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -38,7 +38,7 @@ module Rails initializer :eager_load! do if config.cache_classes && !$rails_rake_task ActiveSupport.run_load_hooks(:before_eager_load, self) - railties.all(&:eager_load!) + eager_load! end end diff --git a/railties/lib/rails/application/metal_loader.rb b/railties/lib/rails/application/metal_loader.rb deleted file mode 100644 index 2a43fa7892..0000000000 --- a/railties/lib/rails/application/metal_loader.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'action_dispatch' - -module Rails - class Application - class MetalLoader - attr_reader :paths, :metals - - def initialize - @paths, @metals = [], [] - end - - def build_middleware(list=nil) - load_metals!(list) - self - end - - def new(app) - ActionDispatch::Cascade.new(@metals, app) - end - - def name - ActionDispatch::Cascade.name - end - alias :to_s :name - - protected - - def load_metals!(list) - metals = [] - list = Array(list || :all).map(&:to_sym) - - paths.each do |path| - matcher = /\A#{Regexp.escape(path)}\/(.*)\.rb\Z/ - Dir.glob("#{path}/**/*.rb").sort.each do |metal_path| - metal = metal_path.sub(matcher, '\1').to_sym - next unless list.include?(metal) || list.include?(:all) - require_dependency metal.to_s - metals << metal - end - end - - metals = metals.sort_by do |m| - [list.index(m) || list.index(:all), m.to_s] - end - - @metals = metals.map { |m| m.to_s.camelize.constantize } - end - end - end -end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index b44755820c..cdb00a4eff 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -25,7 +25,7 @@ module Rails # end # # Then ensure that this file is loaded at the top of your config/application.rb (or in - # your Gemfile) and it will automatically load models, controllers, helpers and metals + # your Gemfile) and it will automatically load models, controllers and helpers # inside app, load routes at "config/routes.rb", load locales at "config/locales/*", # load tasks at "lib/tasks/*". # @@ -73,7 +73,6 @@ module Rails # paths.app.controllers = "app/controllers" # paths.app.helpers = "app/helpers" # paths.app.models = "app/models" - # paths.app.metals = "app/metal" # paths.app.views = "app/views" # paths.lib = "lib" # paths.lib.tasks = "lib/tasks" @@ -202,10 +201,6 @@ module Rails end end - initializer :add_metals do |app| - app.metal_loader.paths.unshift(*paths.app.metals.to_a) - end - initializer :load_config_initializers do paths.config.initializers.to_a.sort.each do |initializer| load(initializer) diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index c5411a0331..446fe0bda9 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -19,7 +19,6 @@ module Rails paths.app.helpers "app/helpers", :eager_load => true paths.app.models "app/models", :eager_load => true paths.app.mailers "app/mailers", :eager_load => true - paths.app.metals "app/metal", :eager_load => true paths.app.views "app/views" paths.lib "lib", :load_path => true paths.lib.tasks "lib/tasks", :glob => "**/*.rake" diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb index 766644bbc2..bd2260fc29 100644 --- a/railties/lib/rails/generators/base.rb +++ b/railties/lib/rails/generators/base.rb @@ -288,7 +288,7 @@ module Rails end # Removes the namespaces and get the generator name. For example, - # Rails::Generators::MetalGenerator will return "metal" as generator name. + # Rails::Generators::ModelGenerator will return "model" as generator name. # def self.generator_name @generator_name ||= begin diff --git a/railties/lib/rails/generators/rails/metal/USAGE b/railties/lib/rails/generators/rails/metal/USAGE deleted file mode 100644 index c88325a444..0000000000 --- a/railties/lib/rails/generators/rails/metal/USAGE +++ /dev/null @@ -1,8 +0,0 @@ -Description: - Cast some metal! - -Examples: - `rails generate metal poller` - - This will create: - Metal: app/metal/poller.rb diff --git a/railties/lib/rails/generators/rails/metal/metal_generator.rb b/railties/lib/rails/generators/rails/metal/metal_generator.rb deleted file mode 100644 index fe4f945cad..0000000000 --- a/railties/lib/rails/generators/rails/metal/metal_generator.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Rails - module Generators - class MetalGenerator < NamedBase - check_class_collision - - def create_metal_file - template "metal.rb", "app/metal/#{file_name}.rb" - end - end - end -end diff --git a/railties/lib/rails/generators/rails/metal/templates/metal.rb b/railties/lib/rails/generators/rails/metal/templates/metal.rb deleted file mode 100644 index 8cc3f1f258..0000000000 --- a/railties/lib/rails/generators/rails/metal/templates/metal.rb +++ /dev/null @@ -1,12 +0,0 @@ -# Allow the metal piece to run in isolation -require File.expand_path('../../../config/environment', __FILE__) unless defined?(Rails) - -class <%= class_name %> - def self.call(env) - if env["PATH_INFO"] =~ /^\/<%= file_name %>/ - [200, {"Content-Type" => "text/html"}, ["Hello, World!"]] - else - [404, {"Content-Type" => "text/html", "X-Cascade" => "pass"}, ["Not Found"]] - end - end -end diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb index 2c57158b1c..de0823749c 100644 --- a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb +++ b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class <%= class_name %>Test < ActionController::IntegrationTest +class <%= class_name %>Test < ActionDispatch::IntegrationTest fixtures :all # Replace this with your real tests. diff --git a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb index 362e3dc09f..e827aa918f 100644 --- a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb +++ b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb @@ -1,7 +1,7 @@ require 'test_helper' require 'rails/performance_test_help' -class <%= class_name %>Test < ActionController::PerformanceTest +class <%= class_name %>Test < ActionDispatch::PerformanceTest # Replace this with your real tests. def test_homepage get '/' diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 063a393bfc..ac510eee2e 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -37,7 +37,7 @@ namespace :rails do project_templates = "#{Rails.root}/lib/templates" default_templates = { "erb" => %w{controller mailer scaffold}, - "rails" => %w{controller helper metal scaffold_controller stylesheets} } + "rails" => %w{controller helper scaffold_controller stylesheets} } default_templates.each do |type, names| local_template_type_dir = File.join(project_templates, type) diff --git a/railties/lib/rails/tasks/middleware.rake b/railties/lib/rails/tasks/middleware.rake index 251da67c96..e670989345 100644 --- a/railties/lib/rails/tasks/middleware.rake +++ b/railties/lib/rails/tasks/middleware.rake @@ -1,6 +1,6 @@ desc 'Prints out your Rack middleware stack' task :middleware => :environment do - Rails.configuration.middleware.active.each do |middleware| + Rails.configuration.middleware.each do |middleware| puts "use #{middleware.inspect}" end puts "run #{Rails::Application.instance.class.name}.routes" diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb index b39b9ecaae..d31915e129 100644 --- a/railties/test/application/initializers/load_path_test.rb +++ b/railties/test/application/initializers/load_path_test.rb @@ -19,6 +19,23 @@ module ApplicationTests assert $:.include?("#{app_path}/app/models") end + test "initializing an application adds lib path on inheritance hook" do + app_file "lib/foo.rb", <<-RUBY + module Foo; end + RUBY + + add_to_config <<-RUBY + require "foo" + raise "Expected Foo to be defined" unless defined?(Foo) + RUBY + + assert_nothing_raised do + require "#{app_path}/config/environment" + end + + assert $:.include?("#{app_path}/lib") + end + test "initializing an application eager load any path under app" do app_file "app/anything/foo.rb", <<-RUBY module Foo; end diff --git a/railties/test/application/metal_test.rb b/railties/test/application/metal_test.rb deleted file mode 100644 index 1ec62282c8..0000000000 --- a/railties/test/application/metal_test.rb +++ /dev/null @@ -1,86 +0,0 @@ -require 'isolation/abstract_unit' - -module ApplicationTests - class MetalTest < Test::Unit::TestCase - include ActiveSupport::Testing::Isolation - - def setup - build_app - boot_rails - - require 'rack/test' - extend Rack::Test::Methods - end - - def app - @app ||= begin - require "#{app_path}/config/environment" - Rails.application - end - end - - test "single metal endpoint" do - app_file 'app/metal/foo_metal.rb', <<-RUBY - class FooMetal - def self.call(env) - [200, { "Content-Type" => "text/html"}, ["FooMetal"]] - end - end - RUBY - - get "/not/slash" - assert_equal 200, last_response.status - assert_equal "FooMetal", last_response.body - end - - test "multiple metal endpoints" do - app_file 'app/metal/metal_a.rb', <<-RUBY - class MetalA - def self.call(env) - [404, { "Content-Type" => "text/html", "X-Cascade" => "pass" }, ["Metal A"]] - end - end - RUBY - - app_file 'app/metal/metal_b.rb', <<-RUBY - class MetalB - def self.call(env) - [200, { "Content-Type" => "text/html"}, ["Metal B"]] - end - end - RUBY - - get "/not/slash" - assert_equal 200, last_response.status - assert_equal "Metal B", last_response.body - end - - test "pass through to application" do - app_file 'app/metal/foo_metal.rb', <<-RUBY - class FooMetal - def self.call(env) - [404, { "Content-Type" => "text/html", "X-Cascade" => "pass" }, ["Not Found"]] - end - end - RUBY - - controller :foo, <<-RUBY - class FooController < ActionController::Base - def index - render :text => "foo" - end - end - RUBY - - app_file 'config/routes.rb', <<-RUBY - AppTemplate::Application.routes.draw do |map| - match ':controller(/:action)' - end - RUBY - - get "/foo" - assert_equal 200, last_response.status - assert_equal "foo", last_response.body - end - end -end diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 617525bf78..bab17d8af5 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -82,12 +82,6 @@ module ApplicationTests assert_equal "Rack::Config", middleware.first end - test "shows cascade if any metal exists" do - app_file "app/metal/foo.rb", "class Foo; end" - boot! - assert middleware.include?("ActionDispatch::Cascade") - end - # x_sendfile_header middleware test "config.action_dispatch.x_sendfile_header defaults to ''" do make_basic_app @@ -170,7 +164,7 @@ module ApplicationTests end def middleware - AppTemplate::Application.middleware.active.map(&:klass).map(&:name) + AppTemplate::Application.middleware.map(&:klass).map(&:name) end def remote_ip(env = {}) diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb index 978d677efc..c98b11556b 100644 --- a/railties/test/application/paths_test.rb +++ b/railties/test/application/paths_test.rb @@ -38,7 +38,6 @@ module ApplicationTests test "booting up Rails yields a valid paths object" do assert_path @paths.app.models, "app", "models" - assert_path @paths.app.metals, "app", "metal" assert_path @paths.app.helpers, "app", "helpers" assert_path @paths.app.views, "app", "views" assert_path @paths.lib, "lib" @@ -73,7 +72,6 @@ module ApplicationTests assert_in_load_path "vendor" assert_not_in_load_path "app", "views" - assert_not_in_load_path "app", "metal" assert_not_in_load_path "config" assert_not_in_load_path "config", "locales" assert_not_in_load_path "config", "environments" diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb index cf282a0911..d05ed76d24 100644 --- a/railties/test/generators/integration_test_generator_test.rb +++ b/railties/test/generators/integration_test_generator_test.rb @@ -7,6 +7,6 @@ class IntegrationTestGeneratorTest < Rails::Generators::TestCase def test_integration_test_skeleton_is_created run_generator - assert_file "test/integration/integration_test.rb", /class IntegrationTest < ActionController::IntegrationTest/ + assert_file "test/integration/integration_test.rb", /class IntegrationTest < ActionDispatch::IntegrationTest/ end end diff --git a/railties/test/generators/metal_generator_test.rb b/railties/test/generators/metal_generator_test.rb deleted file mode 100644 index 615122c882..0000000000 --- a/railties/test/generators/metal_generator_test.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'generators/generators_test_helper' -require 'rails/generators/rails/metal/metal_generator' - -class MetalGeneratorTest < Rails::Generators::TestCase - include GeneratorsTestHelper - arguments %w(foo) - - def test_metal_skeleton_is_created - run_generator - assert_file "app/metal/foo.rb", /class Foo/ - end - - def test_check_class_collision - content = capture(:stderr){ run_generator ["object"] } - assert_match /The name 'Object' is either already used in your application or reserved/, content - end -end diff --git a/railties/test/generators/performance_test_generator_test.rb b/railties/test/generators/performance_test_generator_test.rb index 8fda83b36f..37f9857193 100644 --- a/railties/test/generators/performance_test_generator_test.rb +++ b/railties/test/generators/performance_test_generator_test.rb @@ -7,6 +7,6 @@ class PerformanceTestGeneratorTest < Rails::Generators::TestCase def test_performance_test_skeleton_is_created run_generator - assert_file "test/performance/performance_test.rb", /class PerformanceTest < ActionController::PerformanceTest/ + assert_file "test/performance/performance_test.rb", /class PerformanceTest < ActionDispatch::PerformanceTest/ end end diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb index 20328d402d..3f78d7d3fe 100644 --- a/railties/test/railties/shared_tests.rb +++ b/railties/test/railties/shared_tests.rb @@ -241,24 +241,6 @@ YAML assert_equal "1", I18n.t(:bar) end - def test_plugin_metals_added_to_middleware_stack - @plugin.write 'app/metal/foo_metal.rb', <<-RUBY - class FooMetal - def self.call(env) - [200, { "Content-Type" => "text/html"}, ["FooMetal"]] - end - end - RUBY - - boot_rails - require 'rack/test' - extend Rack::Test::Methods - - get "/not/slash" - assert_equal 200, last_response.status - assert_equal "FooMetal", last_response.body - end - def test_namespaced_controllers_with_namespaced_routes @plugin.write "config/routes.rb", <<-RUBY Rails.application.routes.draw do |