aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile7
-rw-r--r--actionmailer/actionmailer.gemspec1
-rw-r--r--actionmailer/lib/action_mailer.rb6
-rw-r--r--actionmailer/lib/action_mailer/mail_helper.rb7
-rw-r--r--actionpack/actionpack.gemspec5
-rw-r--r--actionpack/lib/action_controller/metal.rb48
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/cascade.rb29
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb80
-rw-r--r--actionpack/lib/action_view/base.rb4
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb65
-rw-r--r--actionpack/lib/action_view/template.rb77
-rw-r--r--actionpack/lib/action_view/template/error.rb1
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb52
-rw-r--r--actionpack/test/controller/new_base/middleware_test.rb33
-rw-r--r--actionpack/test/dispatch/middleware_stack_test.rb19
-rw-r--r--actionpack/test/fixtures/test/scoped_translation.erb2
-rw-r--r--actionpack/test/template/template_test.rb17
-rw-r--r--actionpack/test/template/text_helper_test.rb66
-rw-r--r--activemodel/activemodel.gemspec2
-rw-r--r--activemodel/lib/active_model/errors.rb12
-rw-r--r--activerecord/activerecord.gemspec1
-rw-r--r--activerecord/lib/active_record/counter_cache.rb26
-rwxr-xr-xactiverecord/test/cases/base_test.rb49
-rwxr-xr-xactiverecord/test/cases/counter_cache_test.rb83
-rw-r--r--activesupport/CHANGELOG2
-rw-r--r--activesupport/activesupport.gemspec5
-rw-r--r--activesupport/lib/active_support/builder.rb6
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb29
-rw-r--r--activesupport/lib/active_support/core_ext/string/interpolation.rb1
-rw-r--r--activesupport/lib/active_support/i18n.rb9
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb8
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb29
-rw-r--r--railties/guides/source/configuring.textile2
-rw-r--r--railties/guides/source/initialization.textile6
-rw-r--r--railties/guides/source/rails_on_rack.textile51
-rw-r--r--railties/lib/rails/application.rb49
-rw-r--r--railties/lib/rails/application/bootstrap.rb2
-rw-r--r--railties/lib/rails/application/configuration.rb33
-rw-r--r--railties/lib/rails/application/finisher.rb2
-rw-r--r--railties/lib/rails/application/metal_loader.rb50
-rw-r--r--railties/lib/rails/engine.rb7
-rw-r--r--railties/lib/rails/engine/configuration.rb1
-rw-r--r--railties/lib/rails/generators/base.rb2
-rw-r--r--railties/lib/rails/generators/rails/metal/USAGE8
-rw-r--r--railties/lib/rails/generators/rails/metal/metal_generator.rb11
-rw-r--r--railties/lib/rails/generators/rails/metal/templates/metal.rb12
-rw-r--r--railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb2
-rw-r--r--railties/lib/rails/tasks/framework.rake2
-rw-r--r--railties/lib/rails/tasks/middleware.rake2
-rw-r--r--railties/test/application/initializers/load_path_test.rb17
-rw-r--r--railties/test/application/metal_test.rb86
-rw-r--r--railties/test/application/middleware_test.rb8
-rw-r--r--railties/test/application/paths_test.rb2
-rw-r--r--railties/test/generators/integration_test_generator_test.rb2
-rw-r--r--railties/test/generators/metal_generator_test.rb17
-rw-r--r--railties/test/generators/performance_test_generator_test.rb2
-rw-r--r--railties/test/railties/shared_tests.rb18
63 files changed, 542 insertions, 666 deletions
diff --git a/Gemfile b/Gemfile
index 3b347ea9ef..d57afc8630 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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