aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmilio Tagua <miloops@gmail.com>2009-08-14 12:34:38 -0300
committerEmilio Tagua <miloops@gmail.com>2009-08-14 12:34:38 -0300
commit48c1e6d584409d774b184e0f4d2ef28e5b0e9c90 (patch)
treea2becf0cce37a47579e848780b02f420d335ccd9
parent9ac01fad193b27e517ea772e0d1e13e06f4ddf34 (diff)
parentc6bc8e662614be711f45a8d4b231d5f993b024a7 (diff)
downloadrails-48c1e6d584409d774b184e0f4d2ef28e5b0e9c90.tar.gz
rails-48c1e6d584409d774b184e0f4d2ef28e5b0e9c90.tar.bz2
rails-48c1e6d584409d774b184e0f4d2ef28e5b0e9c90.zip
Merge commit 'rails/master'
-rw-r--r--actionpack/examples/minimal.rb118
-rw-r--r--actionpack/examples/very_simple.rb50
-rw-r--r--actionpack/examples/views/_collection.erb1
-rw-r--r--actionpack/examples/views/_hello.erb1
-rw-r--r--actionpack/examples/views/_many_partials.erb10
-rw-r--r--actionpack/examples/views/_partial.erb10
-rw-r--r--actionpack/examples/views/layouts/alt.html.erb1
-rw-r--r--actionpack/examples/views/layouts/kaigi.html.erb1
-rw-r--r--actionpack/examples/views/template.html.erb1
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb69
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb12
-rw-r--r--actionpack/lib/action_controller/metal/hide_actions.rb14
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb5
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb10
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb3
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb10
-rw-r--r--actionpack/lib/action_controller/testing/process.rb2
-rw-r--r--actionpack/lib/action_controller/testing/test_case.rb1
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb28
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb140
-rw-r--r--actionpack/lib/action_view/render/partials.rb48
-rw-r--r--actionpack/lib/action_view/template/resolver.rb36
-rw-r--r--actionpack/lib/action_view/test_case.rb21
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/controller/caching_test.rb1
-rw-r--r--actionpack/test/controller/mime_responds_test.rb27
-rw-r--r--actionpack/test/dispatch/request_test.rb4
-rw-r--r--actionpack/test/fixtures/test/_customer_with_var.erb2
-rw-r--r--actionpack/test/lib/controller/fake_models.rb12
-rw-r--r--actionpack/test/tmp/.gitignore0
-rw-r--r--activemodel/lib/active_model.rb2
-rw-r--r--activemodel/lib/active_model/serialization.rb30
-rw-r--r--activemodel/lib/active_model/serializer.rb60
-rw-r--r--activemodel/lib/active_model/serializers/json.rb18
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb76
-rw-r--r--activerecord/lib/active_record/serialization.rb80
-rw-r--r--activerecord/lib/active_record/serializers/json_serializer.rb14
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb19
-rw-r--r--activesupport/lib/active_support/cache.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/class/delegating_attributes.rb7
-rwxr-xr-xci/ci_build.rb4
-rw-r--r--railties/lib/generators.rb90
-rw-r--r--railties/lib/generators/active_record.rb1
-rw-r--r--railties/test/fixtures/vendor/another_gem_path/xspec/lib/generators/xspec_generator.rb2
-rw-r--r--railties/test/fixtures/vendor/plugins/mspec/lib/generators/mspec_generator.rb (renamed from railties/test/fixtures/vendor/gems/gems/mspec/lib/generators/mspec_generator.rb)0
-rw-r--r--railties/test/generators/generators_test_helper.rb7
-rw-r--r--railties/test/generators/session_migration_generator_test.rb13
-rw-r--r--railties/test/generators_test.rb19
49 files changed, 506 insertions, 603 deletions
diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb
deleted file mode 100644
index a9015da053..0000000000
--- a/actionpack/examples/minimal.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-# Pass NEW=1 to run with the new Base
-ENV['RAILS_ENV'] ||= 'production'
-ENV['NO_RELOAD'] ||= '1'
-
-$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
-$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
-require 'action_controller'
-require 'action_controller/new_base' if ENV['NEW']
-require 'action_view'
-require 'benchmark'
-
-class Runner
- def initialize(app, output)
- @app, @output = app, output
- end
-
- def puts(*)
- super if @output
- end
-
- def call(env)
- env['n'].to_i.times { @app.call(env) }
- @app.call(env).tap { |response| report(env, response) }
- end
-
- def report(env, response)
- return unless ENV["DEBUG"]
- out = env['rack.errors']
- out.puts response[0], response[1].to_yaml, '---'
- response[2].each { |part| out.puts part }
- out.puts '---'
- end
-
- def self.puts(*)
- super if @output
- end
-
- def self.run(app, n, label, output = true)
- @output = output
- puts label, '=' * label.size if label
- env = Rack::MockRequest.env_for("/").merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout)
- t = Benchmark.realtime { new(app, output).call(env) }
- puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n]
- puts
- end
-end
-
-
-N = (ENV['N'] || 1000).to_i
-
-module ActionController::Rails2Compatibility
- instance_methods.each do |name|
- remove_method name
- end
-end
-
-class BasePostController < ActionController::Base
- append_view_path "#{File.dirname(__FILE__)}/views"
-
- def overhead
- self.response_body = ''
- end
-
- def index
- render :text => ''
- end
-
- def partial
- render :partial => "/partial"
- end
-
- def many_partials
- render :partial => "/many_partials"
- end
-
- def partial_collection
- render :partial => "/collection", :collection => [1,2,3,4,5,6,7,8,9,10]
- end
-
- def show_template
- render :template => "template"
- end
-end
-
-OK = [200, {}, []]
-MetalPostController = lambda { OK }
-
-class HttpPostController < ActionController::Metal
- def index
- self.response_body = ''
- end
-end
-
-unless ENV["PROFILE"]
- Runner.run(BasePostController.action(:overhead), N, 'overhead', false)
- Runner.run(BasePostController.action(:index), N, 'index', false)
- Runner.run(BasePostController.action(:partial), N, 'partial', false)
- Runner.run(BasePostController.action(:many_partials), N, 'many_partials', false)
- Runner.run(BasePostController.action(:partial_collection), N, 'collection', false)
- Runner.run(BasePostController.action(:show_template), N, 'template', false)
-
- (ENV["M"] || 1).to_i.times do
- Runner.run(BasePostController.action(:overhead), N, 'overhead')
- Runner.run(BasePostController.action(:index), N, 'index')
- Runner.run(BasePostController.action(:partial), N, 'partial')
- Runner.run(BasePostController.action(:many_partials), N, 'many_partials')
- Runner.run(BasePostController.action(:partial_collection), N, 'collection')
- Runner.run(BasePostController.action(:show_template), N, 'template')
- end
-else
- Runner.run(BasePostController.action(:many_partials), N, 'many_partials')
- require "ruby-prof"
- RubyProf.start
- Runner.run(BasePostController.action(:many_partials), N, 'many_partials')
- result = RubyProf.stop
- printer = RubyProf::CallStackPrinter.new(result)
- printer.print(File.open("output.html", "w"))
-end \ No newline at end of file
diff --git a/actionpack/examples/very_simple.rb b/actionpack/examples/very_simple.rb
deleted file mode 100644
index 6714185172..0000000000
--- a/actionpack/examples/very_simple.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-$:.push "rails/activesupport/lib"
-$:.push "rails/actionpack/lib"
-
-require "action_controller"
-
-class Kaigi < ActionController::Metal
- include AbstractController::Callbacks
- include ActionController::RackConvenience
- include ActionController::RenderingController
- include ActionController::Layouts
- include ActionView::Context
-
- before_filter :set_name
- append_view_path "views"
-
- def view_context
- self
- end
-
- def controller
- self
- end
-
- DEFAULT_LAYOUT = Object.new.tap {|l| def l.render(*) yield end }
-
- def render_template(template, layout = DEFAULT_LAYOUT, options = {}, partial = false)
- ret = template.render(self, {})
- layout.render(self, {}) { ret }
- end
-
- def index
- render :template => "template"
- end
-
- def alt
- render :template => "template", :layout => "alt"
- end
-
- private
- def set_name
- @name = params[:name]
- end
-end
-
-app = Rack::Builder.new do
- map("/kaigi") { run Kaigi.action(:index) }
- map("/kaigi/alt") { run Kaigi.action(:alt) }
-end.to_app
-
-Rack::Handler::Mongrel.run app, :Port => 3000
diff --git a/actionpack/examples/views/_collection.erb b/actionpack/examples/views/_collection.erb
deleted file mode 100644
index bcfe958e2c..0000000000
--- a/actionpack/examples/views/_collection.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= collection %> \ No newline at end of file
diff --git a/actionpack/examples/views/_hello.erb b/actionpack/examples/views/_hello.erb
deleted file mode 100644
index 5ab2f8a432..0000000000
--- a/actionpack/examples/views/_hello.erb
+++ /dev/null
@@ -1 +0,0 @@
-Hello \ No newline at end of file
diff --git a/actionpack/examples/views/_many_partials.erb b/actionpack/examples/views/_many_partials.erb
deleted file mode 100644
index 7e379d46f5..0000000000
--- a/actionpack/examples/views/_many_partials.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %>
-<%= render :partial => '/hello' %> \ No newline at end of file
diff --git a/actionpack/examples/views/_partial.erb b/actionpack/examples/views/_partial.erb
deleted file mode 100644
index 3ca8e80b52..0000000000
--- a/actionpack/examples/views/_partial.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
diff --git a/actionpack/examples/views/layouts/alt.html.erb b/actionpack/examples/views/layouts/alt.html.erb
deleted file mode 100644
index c4816337a6..0000000000
--- a/actionpack/examples/views/layouts/alt.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-+ <%= yield %> + \ No newline at end of file
diff --git a/actionpack/examples/views/layouts/kaigi.html.erb b/actionpack/examples/views/layouts/kaigi.html.erb
deleted file mode 100644
index 274607a96a..0000000000
--- a/actionpack/examples/views/layouts/kaigi.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-Hello <%= yield %> Goodbye \ No newline at end of file
diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb
deleted file mode 100644
index 5ab2f8a432..0000000000
--- a/actionpack/examples/views/template.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-Hello \ No newline at end of file
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 0063d54149..ac2154dffc 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -6,13 +6,51 @@ module AbstractController
included do
extlib_inheritable_accessor(:_layout_conditions) { Hash.new }
+ extlib_inheritable_accessor(:_action_has_layout) { Hash.new }
_write_layout_method
end
module ClassMethods
def inherited(klass)
super
- klass._write_layout_method
+ klass.class_eval do
+ _write_layout_method
+ @found_layouts = {}
+ end
+ end
+
+ def cache_layout(details)
+ layout = @found_layouts
+ values = details.values_at(:formats, :locale)
+
+ # Cache nil
+ if layout.key?(values)
+ return layout[values]
+ else
+ layout[values] = yield
+ end
+ end
+
+ # This module is mixed in if layout conditions are provided. This means
+ # that if no layout conditions are used, this method is not used
+ module LayoutConditions
+ # Determines whether the current action has a layout by checking the
+ # action name against the :only and :except conditions set on the
+ # layout.
+ #
+ # ==== Returns
+ # Boolean:: True if the action has a layout, false otherwise.
+ def _action_has_layout?
+ conditions = _layout_conditions
+
+ if only = conditions[:only]
+ only.include?(action_name)
+ elsif except = conditions[:except]
+ !except.include?(action_name)
+ else
+ true
+ end
+ end
end
# Specify the layout to use for this class.
@@ -31,6 +69,8 @@ module AbstractController
# :only<#to_s, Array[#to_s]>:: A list of actions to apply this layout to.
# :except<#to_s, Array[#to_s]>:: Apply this layout to all actions but this one
def layout(layout, conditions = {})
+ include LayoutConditions unless conditions.empty?
+
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
self._layout_conditions = conditions
@@ -76,10 +116,12 @@ module AbstractController
when nil
self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
def _layout(details)
- if view_paths.exists?("#{_implied_layout_name}", details, "layouts")
- "#{_implied_layout_name}"
- else
- super
+ self.class.cache_layout(details) do
+ if view_paths.exists?("#{_implied_layout_name}", details, "layouts")
+ "#{_implied_layout_name}"
+ else
+ super
+ end
end
end
ruby_eval
@@ -136,7 +178,7 @@ module AbstractController
view_paths.find(name, details, prefix)
end
- # Returns the default layout for this controller and a given set of details.
+ # Returns the default layout for this controller and a given set of details.
# Optionally raises an exception if the layout could not be found.
#
# ==== Parameters
@@ -162,21 +204,8 @@ module AbstractController
end
end
- # Determines whether the current action has a layout by checking the
- # action name against the :only and :except conditions set on the
- # layout.
- #
- # ==== Returns
- # Boolean:: True if the action has a layout, false otherwise.
def _action_has_layout?
- conditions = _layout_conditions
- if only = conditions[:only]
- only.include?(action_name)
- elsif except = conditions[:except]
- !except.include?(action_name)
- else
- true
- end
+ true
end
end
end
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index f94d1c669c..5b0165f0e7 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -25,8 +25,9 @@ module ActionController
cattr_accessor :relative_url_root
self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
- cattr_accessor :default_charset
- self.default_charset = "utf-8"
+ class << self
+ delegate :default_charset=, :to => "ActionDispatch::Response"
+ end
# cattr_reader :protected_instance_variables
cattr_accessor :protected_instance_variables
@@ -101,11 +102,10 @@ module ActionController
options[:template].sub!(/^\//, '')
end
- options[:text] = nil if options[:nothing] == true
+ options[:text] = nil if options.delete(:nothing) == true
+ options[:text] = " " if options.key?(:text) && options[:text].nil?
- body = super
- body = [' '] if body.blank?
- body
+ super || " "
end
def _handle_method_missing
diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb
index af68c772b1..cdacdc40a6 100644
--- a/actionpack/lib/action_controller/metal/hide_actions.rb
+++ b/actionpack/lib/action_controller/metal/hide_actions.rb
@@ -13,7 +13,9 @@ module ActionController
# Overrides AbstractController::Base#action_method? to return false if the
# action name is in the list of hidden actions.
def action_method?(action_name)
- !hidden_actions.include?(action_name) && super
+ self.class.visible_action?(action_name) do
+ !hidden_actions.include?(action_name) && super
+ end
end
module ClassMethods
@@ -25,6 +27,16 @@ module ActionController
hidden_actions.merge(args.map! {|a| a.to_s })
end
+ def inherited(klass)
+ klass.instance_variable_set("@visible_actions", {})
+ super
+ end
+
+ def visible_action?(action_name)
+ return @visible_actions[action_name] if @visible_actions.key?(action_name)
+ @visible_actions[action_name] = yield
+ end
+
# Overrides AbstractController::Base#action_methods to remove any methods
# that are listed as hidden methods.
def action_methods
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index c8d042acb5..950105e63f 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -226,10 +226,11 @@ module ActionController #:nodoc:
# is quite simple (it just needs to respond to call), you can even give
# a proc to it.
#
- def respond_with(resource, options={}, &block)
+ def respond_with(*resources, &block)
respond_to(&block)
rescue ActionView::MissingTemplate
- (options.delete(:responder) || responder).call(self, resource, options)
+ options = resources.extract_options!
+ (options.delete(:responder) || responder).call(self, resources, options)
end
def responder
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index 9ed99ca623..fc01a0924a 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -64,7 +64,7 @@ module ActionController #:nodoc:
# @project = Project.find(params[:project_id])
# @task = @project.comments.build(params[:task])
# flash[:notice] = 'Task was successfully created.' if @task.save
- # respond_with([@project, @task])
+ # respond_with(@project, @task)
# end
#
# Giving an array of resources, you ensure that the responder will redirect to
@@ -74,19 +74,19 @@ module ActionController #:nodoc:
# polymorphic urls. If a project has one manager which has many tasks, it
# should be invoked as:
#
- # respond_with([@project, :manager, @task])
+ # respond_with(@project, :manager, @task)
#
# Check polymorphic_url documentation for more examples.
#
class Responder
attr_reader :controller, :request, :format, :resource, :resource_location, :options
- def initialize(controller, resource, options={})
+ def initialize(controller, resources, options={})
@controller = controller
@request = controller.request
@format = controller.formats.first
- @resource = resource.is_a?(Array) ? resource.last : resource
- @resource_location = options[:location] || resource
+ @resource = resources.is_a?(Array) ? resources.last : resources
+ @resource_location = options[:location] || resources
@options = options
end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 57318e8747..4761763a26 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -145,7 +145,6 @@ module ActionController #:nodoc:
def send_data(data, options = {}) #:doc:
logger.info "Sending data #{options[:filename]}" if logger
send_file_headers! options.merge(:length => data.bytesize)
- @performed_render = false
render :status => options[:status], :text => data
end
@@ -175,6 +174,8 @@ module ActionController #:nodoc:
'Content-Transfer-Encoding' => 'binary'
)
+ response.sending_file = true
+
# Fix a problem with IE 6.0 on opening downloaded files:
# If Cache-Control: no-cache is set (which Rails does by default),
# IE removes the file it just downloaded from its cache immediately
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 7119c14cd3..14c6523045 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -4,15 +4,6 @@ module ActionController
include RackConvenience
- def process_action(*)
- initialize_current_url
- super
- end
-
- def initialize_current_url
- @url = UrlRewriter.new(request, params.clone)
- end
-
# Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
# the form of a hash, just like the one you would use for url_for directly. Example:
#
@@ -40,6 +31,7 @@ module ActionController
when String
options
when Hash
+ @url ||= UrlRewriter.new(request, params)
@url.rewrite(rewrite_options(options))
else
polymorphic_url(options)
diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb
index d32d5562e8..09b1a59254 100644
--- a/actionpack/lib/action_controller/testing/process.rb
+++ b/actionpack/lib/action_controller/testing/process.rb
@@ -52,7 +52,7 @@ module ActionController #:nodoc:
class TestResponse < ActionDispatch::TestResponse
def recycle!
@status = 200
- @header = Rack::Utils::HeaderHash.new
+ @header = {}
@writer = lambda { |x| @body << x }
@block = nil
@length = 0
diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/testing/test_case.rb
index a11755b517..b66a4c15ff 100644
--- a/actionpack/lib/action_controller/testing/test_case.rb
+++ b/actionpack/lib/action_controller/testing/test_case.rb
@@ -179,7 +179,6 @@ module ActionController
if @controller
@controller.request = @request
@controller.params = {}
- @controller.send(:initialize_current_url)
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 4190fa21cd..b23306af62 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -173,19 +173,15 @@ module ActionDispatch
end
end
- # Expand raw_formats by converting Mime::ALL to the Mime::SET.
- #
def formats
if ActionController::Base.use_accept_header
- raw_formats.tap do |ret|
- if ret == ONLY_ALL
- ret.replace Mime::SET
- elsif all = ret.index(Mime::ALL)
- ret.delete_at(all) && ret.insert(all, *Mime::SET)
- end
+ if param = parameters[:format]
+ Array.wrap(Mime[param])
+ else
+ accepts.dup
end
else
- raw_formats + Mime::SET
+ [format]
end
end
@@ -487,7 +483,7 @@ EOM
# matches the order array.
#
def negotiate_mime(order)
- raw_formats.each do |priority|
+ formats.each do |priority|
if priority == Mime::ALL
return order.first
elsif order.include?(priority)
@@ -500,18 +496,6 @@ EOM
private
- def raw_formats
- if ActionController::Base.use_accept_header
- if param = parameters[:format]
- Array.wrap(Mime[param])
- else
- accepts.dup
- end
- else
- [format]
- end
- end
-
def named_host?(host)
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 03d1780b77..055f29a972 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -32,18 +32,35 @@ module ActionDispatch # :nodoc:
# end
# end
class Response < Rack::Response
- attr_accessor :request
+ attr_accessor :request, :blank
attr_reader :cache_control
- attr_writer :header
+ attr_writer :header, :sending_file
alias_method :headers=, :header=
- delegate :default_charset, :to => 'ActionController::Base'
-
def initialize
- super
+ @status = 200
+ @header = {}
@cache_control = {}
- @header = Rack::Utils::HeaderHash.new
+
+ @writer = lambda { |x| @body << x }
+ @block = nil
+ @length = 0
+
+ @body, @cookie = [], []
+ @sending_file = false
+
+ yield self if block_given?
+ end
+
+ def cache_control
+ @cache_control ||= {}
+ end
+
+ def write(str)
+ s = str.to_s
+ @writer.call s
+ str
end
def status=(status)
@@ -71,7 +88,10 @@ module ActionDispatch # :nodoc:
str
end
+ EMPTY = " "
+
def body=(body)
+ @blank = true if body == EMPTY
@body = body.respond_to?(:to_str) ? [body] : body
end
@@ -113,41 +133,39 @@ module ActionDispatch # :nodoc:
end
def etag
- headers['ETag']
+ @etag
end
def etag?
- headers.include?('ETag')
+ @etag
end
def etag=(etag)
- if etag.blank?
- headers.delete('ETag')
- else
- headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
- end
+ key = ActiveSupport::Cache.expand_cache_key(etag)
+ @etag = %("#{Digest::MD5.hexdigest(key)}")
end
- def sending_file?
- headers["Content-Transfer-Encoding"] == "binary"
- end
+ CONTENT_TYPE = "Content-Type"
+
+ cattr_accessor(:default_charset) { "utf-8" }
def assign_default_content_type_and_charset!
- return if !headers["Content-Type"].blank?
+ return if headers[CONTENT_TYPE].present?
@content_type ||= Mime::HTML
- @charset ||= default_charset
+ @charset ||= self.class.default_charset
type = @content_type.to_s.dup
- type << "; charset=#{@charset}" unless sending_file?
+ type << "; charset=#{@charset}" unless @sending_file
- headers["Content-Type"] = type
+ headers[CONTENT_TYPE] = type
end
def prepare!
assign_default_content_type_and_charset!
handle_conditional_get!
- self["Set-Cookie"] ||= ""
+ self["Set-Cookie"] = @cookie.join("\n")
+ self["ETag"] = @etag if @etag
end
def each(&callback)
@@ -168,23 +186,12 @@ module ActionDispatch # :nodoc:
str
end
- def set_cookie(key, value)
- if value.has_key?(:http_only)
- ActiveSupport::Deprecation.warn(
- "The :http_only option in ActionController::Response#set_cookie " +
- "has been renamed. Please use :httponly instead.", caller)
- value[:httponly] ||= value.delete(:http_only)
- end
-
- super(key, value)
- end
-
# Returns the response cookies, converted to a Hash of (name => value) pairs
#
# assert_equal 'AuthorOfNewPage', r.cookies['author']
def cookies
cookies = {}
- if header = headers['Set-Cookie']
+ if header = @cookie
header = header.split("\n") if header.respond_to?(:to_str)
header.each do |cookie|
if pair = cookie.split(';').first
@@ -196,12 +203,43 @@ module ActionDispatch # :nodoc:
cookies
end
+ def set_cookie(key, value)
+ case value
+ when Hash
+ domain = "; domain=" + value[:domain] if value[:domain]
+ path = "; path=" + value[:path] if value[:path]
+ # According to RFC 2109, we need dashes here.
+ # N.B.: cgi.rb uses spaces...
+ expires = "; expires=" + value[:expires].clone.gmtime.
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
+ secure = "; secure" if value[:secure]
+ httponly = "; HttpOnly" if value[:httponly]
+ value = value[:value]
+ end
+ value = [value] unless Array === value
+ cookie = Rack::Utils.escape(key) + "=" +
+ value.map { |v| Rack::Utils.escape v }.join("&") +
+ "#{domain}#{path}#{expires}#{secure}#{httponly}"
+
+ @cookie << cookie
+ end
+
+ def delete_cookie(key, value={})
+ @cookie.reject! { |cookie|
+ cookie =~ /\A#{Rack::Utils.escape(key)}=/
+ }
+
+ set_cookie(key,
+ {:value => '', :path => nil, :domain => nil,
+ :expires => Time.at(0) }.merge(value))
+ end
+
private
def handle_conditional_get!
- if etag? || last_modified? || !cache_control.empty?
+ if etag? || last_modified? || !@cache_control.empty?
set_conditional_cache_control!
elsif nonempty_ok_response?
- self.etag = body
+ self.etag = @body
if request && request.etag_matches?(etag)
self.status = 304
@@ -215,29 +253,33 @@ module ActionDispatch # :nodoc:
end
def nonempty_ok_response?
- ok = !@status || @status == 200
- ok && string_body?
+ @status == 200 && string_body?
end
def string_body?
- !body_parts.respond_to?(:call) && body_parts.any? && body_parts.all? { |part| part.is_a?(String) }
+ !@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) }
end
+ DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
+
def set_conditional_cache_control!
- if cache_control.empty?
- cache_control.merge!(:public => false, :max_age => 0, :must_revalidate => true)
- end
+ control = @cache_control
+
+ if control.empty?
+ headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
+ else
+ extras = control[:extras]
+ max_age = control[:max_age]
- public_cache, max_age, must_revalidate, extras =
- cache_control.values_at(:public, :max_age, :must_revalidate, :extras)
+ options = []
+ options << "max-age=#{max_age}" if max_age
+ options << (control[:public] ? "public" : "private")
+ options << "must-revalidate" if control[:must_revalidate]
+ options.concat(extras) if extras
- options = []
- options << "max-age=#{max_age}" if max_age
- options << (public_cache ? "public" : "private")
- options << "must-revalidate" if must_revalidate
- options.concat(extras) if extras
+ headers["Cache-Control"] = options.join(", ")
+ end
- headers["Cache-Control"] = options.join(", ")
end
end
end
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 64f08c447d..83175ab4cf 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -184,6 +184,7 @@ module ActionView
def initialize(view_context, options, block)
partial = options[:partial]
+ @memo = {}
@view = view_context
@options = options
@locals = options[:locals] || {}
@@ -207,9 +208,7 @@ module ActionView
end
def render_collection
- # Even if no template is rendered, this will ensure that the MIME type
- # for the empty response is the same as the provided template
- @options[:_template] = default_template = find_template
+ @options[:_template] = template = find_template
return nil if collection.blank?
@@ -217,15 +216,46 @@ module ActionView
spacer = find_template(@options[:spacer_template]).render(@view, @locals)
end
- segments = []
+ result = template ? collection_with_template(template) : collection_without_template
+ result.join(spacer)
+ end
+
+ def collection_with_template(template)
+ options = @options
+
+ segments, locals, as = [], @locals, options[:as] || :object
+
+ variable_name = template.variable_name
+ counter_name = template.counter_name
+ locals[counter_name] = -1
+
+ collection.each do |object|
+ locals[counter_name] += 1
+ locals[variable_name] = object
+ locals[as] = object if as
+
+ segments << template.render(@view, locals)
+ end
+ segments
+ end
+
+ def collection_without_template
+ options = @options
+
+ segments, locals, as = [], @locals, options[:as] || :object
+ index, template = -1, nil
+
+ collection.each do |object|
+ template = find_template(partial_path(object))
+ locals[template.counter_name] = (index += 1)
+ locals[template.variable_name] = object
+ locals[as] = object if as
- collection.each_with_index do |object, index|
- template = default_template || find_template(partial_path(object))
- @locals[template.counter_name] = index
- segments << render_template(template, object)
+ segments << template.render(@view, locals)
end
- segments.join(spacer)
+ @options[:_template] = template
+ segments
end
def render_template(template, object = @object)
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index ebfc6cc8ce..10f664736f 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -41,8 +41,10 @@ module ActionView
end
def handler_glob
- e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join
- "{#{e}}"
+ @handler_glob ||= begin
+ e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join
+ "{#{e}}"
+ end
end
def formats_glob
@@ -60,6 +62,10 @@ module ActionView
class FileSystemResolver < Resolver
+ def self.cached_glob
+ @@cached_glob ||= {}
+ end
+
def initialize(path, options = {})
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
super(options)
@@ -105,20 +111,22 @@ module ActionView
# :api: plugin
def details_to_glob(name, details, prefix, partial, root)
- path = ""
- path << "#{prefix}/" unless prefix.empty?
- path << (partial ? "_#{name}" : name)
-
- extensions = ""
- [:locales, :formats].each do |k|
- extensions << if exts = details[k]
- '{' + exts.map {|e| ".#{e},"}.join + '}'
- else
- k == :formats ? formats_glob : ''
+ self.class.cached_glob[[name, prefix, partial, details, root]] ||= begin
+ path = ""
+ path << "#{prefix}/" unless prefix.empty?
+ path << (partial ? "_#{name}" : name)
+
+ extensions = ""
+ [:locales, :formats].each do |k|
+ extensions << if exts = details[k]
+ '{' + exts.map {|e| ".#{e},"}.join + '}'
+ else
+ k == :formats ? formats_glob : ''
+ end
end
- end
- "#{root}#{path}#{extensions}#{handler_glob}"
+ "#{root}#{path}#{extensions}#{handler_glob}"
+ end
end
# TODO: fix me
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index e51744d095..c2ccd1d3a5 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -9,14 +9,16 @@ module ActionView
end
attr_internal :rendered
- alias_method :_render_template_without_template_tracking, :_render_single_template
- def _render_single_template(template, local_assigns, &block)
- if template.respond_to?(:identifier) && template.present?
- @_rendered[:partials][template] += 1 if template.partial?
- @_rendered[:template] ||= []
- @_rendered[:template] << template
- end
- _render_template_without_template_tracking(template, local_assigns, &block)
+ end
+
+ class Template
+ alias_method :render_without_tracking, :render
+ def render(view, locals, &blk)
+ rendered = view.rendered
+ rendered[:partials][self] += 1 if partial?
+ rendered[:template] ||= []
+ rendered[:template] << self
+ render_without_tracking(view, locals, &blk)
end
end
@@ -68,9 +70,8 @@ module ActionView
def initialize
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
-
+
@params = {}
- send(:initialize_current_url)
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index b062a71442..eb4e2fb585 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -6,6 +6,8 @@ $:.unshift(File.dirname(__FILE__) + '/lib')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers')
+ENV['TMPDIR'] = File.join(File.dirname(__FILE__), 'tmp')
+
ENV['new_base'] = "true"
$stderr.puts "Running old tests on new_base"
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 346fa09414..f1c93e6b1e 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -536,7 +536,6 @@ class FragmentCachingTest < ActionController::TestCase
@controller.params = @params
@controller.request = @request
@controller.response = @response
- @controller.send(:initialize_current_url)
@controller.send(:initialize_template_class, @response)
@controller.send(:assign_shortcuts, @request, @response)
end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 8319b5c573..2e2dba5aae 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -497,8 +497,12 @@ class RespondWithController < ActionController::Base
respond_with(Customer.new("david", 13))
end
+ def using_resource_with_collection
+ respond_with([Customer.new("david", 13), Customer.new("jamis", 9)])
+ end
+
def using_resource_with_parent
- respond_with([Quiz::Store.new("developer?", 11), Customer.new("david", 13)])
+ respond_with(Quiz::Store.new("developer?", 11), Customer.new("david", 13))
end
def using_resource_with_status_and_location
@@ -506,7 +510,7 @@ class RespondWithController < ActionController::Base
end
def using_resource_with_responder
- responder = proc { |c, r, o| c.render :text => "Resource name is #{r.name}" }
+ responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" }
respond_with(Customer.new("david", 13), :responder => responder)
end
@@ -592,7 +596,7 @@ class RespondWithControllerTest < ActionController::TestCase
@request.accept = "application/xml"
get :using_resource
assert_equal "application/xml", @response.content_type
- assert_equal "XML", @response.body
+ assert_equal "<name>david</name>", @response.body
@request.accept = "application/json"
assert_raise ActionView::MissingTemplate do
@@ -622,7 +626,7 @@ class RespondWithControllerTest < ActionController::TestCase
post :using_resource
assert_equal "application/xml", @response.content_type
assert_equal 201, @response.status
- assert_equal "XML", @response.body
+ assert_equal "<name>david</name>", @response.body
assert_equal "http://www.example.com/customers/13", @response.location
errors = { :name => :invalid }
@@ -689,7 +693,7 @@ class RespondWithControllerTest < ActionController::TestCase
get :using_resource_with_parent
assert_equal "application/xml", @response.content_type
assert_equal 200, @response.status
- assert_equal "XML", @response.body
+ assert_equal "<name>david</name>", @response.body
end
def test_using_resource_with_parent_for_post
@@ -698,7 +702,7 @@ class RespondWithControllerTest < ActionController::TestCase
post :using_resource_with_parent
assert_equal "application/xml", @response.content_type
assert_equal 201, @response.status
- assert_equal "XML", @response.body
+ assert_equal "<name>david</name>", @response.body
assert_equal "http://www.example.com/quiz_stores/11/customers/13", @response.location
errors = { :name => :invalid }
@@ -710,6 +714,15 @@ class RespondWithControllerTest < ActionController::TestCase
assert_nil @response.location
end
+ def test_using_resource_with_collection
+ @request.accept = "application/xml"
+ get :using_resource_with_collection
+ assert_equal "application/xml", @response.content_type
+ assert_equal 200, @response.status
+ assert_match /<name>david<\/name>/, @response.body
+ assert_match /<name>jamis<\/name>/, @response.body
+ end
+
def test_clear_respond_to
@controller = InheritedRespondWithController.new
@request.accept = "text/html"
@@ -722,7 +735,7 @@ class RespondWithControllerTest < ActionController::TestCase
@request.accept = "*/*"
get :index
assert_equal "application/xml", @response.content_type
- assert_equal "XML", @response.body
+ assert_equal "<name>david</name>", @response.body
end
def test_no_double_render_is_raised
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index f3500fca34..b626063df4 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -427,7 +427,7 @@ class RequestTest < ActiveSupport::TestCase
request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8'
request.expects(:parameters).at_least_once.returns({})
- assert_equal with_set(Mime::XML, Mime::HTML), request.formats
+ assert_equal with_set(Mime::XML, Mime::HTML, Mime::ALL), request.formats
end
with_accept_header false do
@@ -460,7 +460,7 @@ protected
end
def with_set(*args)
- args + Mime::SET
+ args
end
def with_accept_header(value)
diff --git a/actionpack/test/fixtures/test/_customer_with_var.erb b/actionpack/test/fixtures/test/_customer_with_var.erb
index 3379246b7e..c28824936b 100644
--- a/actionpack/test/fixtures/test/_customer_with_var.erb
+++ b/actionpack/test/fixtures/test/_customer_with_var.erb
@@ -1 +1 @@
-<%= customer.name %> <%= object.name %> <%= customer_with_var.name %> \ No newline at end of file
+<%= customer.name %> <%= customer.name %> <%= customer_with_var.name %> \ No newline at end of file
diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index 0faf8f3f9a..18eff7516b 100644
--- a/actionpack/test/lib/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
@@ -10,12 +10,16 @@ class Customer < Struct.new(:name, :id)
id.to_s
end
- def to_xml
- "XML"
+ def to_xml(options={})
+ if options[:builder]
+ options[:builder].name name
+ else
+ "<name>#{name}</name>"
+ end
end
- def to_js
- "JS"
+ def to_js(options={})
+ "name: #{name.inspect}"
end
def errors
diff --git a/actionpack/test/tmp/.gitignore b/actionpack/test/tmp/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/tmp/.gitignore
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index b24a929ff5..244f3a546e 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -35,7 +35,7 @@ module ActiveModel
autoload :Naming, 'active_model/naming'
autoload :Observer, 'active_model/observing'
autoload :Observing, 'active_model/observing'
- autoload :Serializer, 'active_model/serializer'
+ autoload :Serialization, 'active_model/serialization'
autoload :StateMachine, 'active_model/state_machine'
autoload :TestCase, 'active_model/test_case'
autoload :Validations, 'active_model/validations'
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
new file mode 100644
index 0000000000..4c0073f687
--- /dev/null
+++ b/activemodel/lib/active_model/serialization.rb
@@ -0,0 +1,30 @@
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/hash/slice'
+
+module ActiveModel
+ module Serialization
+ def serializable_hash(options = nil)
+ options ||= {}
+
+ options[:only] = Array.wrap(options[:only]).map { |n| n.to_s }
+ options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
+
+ attribute_names = attributes.keys.sort
+ if options[:only].any?
+ attribute_names &= options[:only]
+ elsif options[:except].any?
+ attribute_names -= options[:except]
+ end
+
+ method_names = Array.wrap(options[:methods]).inject([]) do |methods, name|
+ methods << name if respond_to?(name.to_s)
+ methods
+ end
+
+ (attribute_names + method_names).inject({}) { |hash, name|
+ hash[name] = send(name)
+ hash
+ }
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb
deleted file mode 100644
index 5b603bdbd7..0000000000
--- a/activemodel/lib/active_model/serializer.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/hash/slice'
-
-module ActiveModel
- class Serializer
- attr_reader :options
-
- def initialize(serializable, options = nil)
- @serializable = serializable
- @options = options ? options.dup : {}
-
- @options[:only] = Array.wrap(@options[:only]).map { |n| n.to_s }
- @options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
- end
-
- def serialize
- raise NotImplemented
- end
-
- def to_s(&block)
- serialize(&block)
- end
-
- # To replicate the behavior in ActiveRecord#attributes,
- # <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
- # for a N level model but is set for the N+1 level models,
- # then because <tt>:except</tt> is set to a default value, the second
- # level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
- # <tt>:only</tt> is set, always delete <tt>:except</tt>.
- def serializable_attribute_names
- attribute_names = @serializable.attributes.keys.sort
-
- if options[:only].any?
- attribute_names &= options[:only]
- elsif options[:except].any?
- attribute_names -= options[:except]
- end
-
- attribute_names
- end
-
- def serializable_method_names
- Array.wrap(options[:methods]).inject([]) do |methods, name|
- methods << name if @serializable.respond_to?(name.to_s)
- methods
- end
- end
-
- def serializable_names
- serializable_attribute_names + serializable_method_names
- end
-
- def serializable_hash
- serializable_names.inject({}) { |hash, name|
- hash[name] = @serializable.send(name)
- hash
- }
- end
- end
-end
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index e94512fd64..ee6d48bfc6 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -5,6 +5,7 @@ module ActiveModel
module Serializers
module JSON
extend ActiveSupport::Concern
+ include ActiveModel::Serialization
included do
extend ActiveModel::Naming
@@ -12,19 +13,6 @@ module ActiveModel
cattr_accessor :include_root_in_json, :instance_writer => false
end
- class Serializer < ActiveModel::Serializer
- def serializable_hash
- model = super
- @serializable.include_root_in_json ?
- { @serializable.class.model_name.element => model } :
- model
- end
-
- def serialize
- ActiveSupport::JSON.encode(serializable_hash)
- end
- end
-
# Returns a JSON string representing the model. Some configuration is
# available through +options+.
#
@@ -92,7 +80,9 @@ module ActiveModel
# {"comments": [{"body": "Don't think too hard"}],
# "title": "So I was thinking"}]}
def encode_json(encoder)
- Serializer.new(self, encoder.options).to_s
+ hash = serializable_hash(encoder.options)
+ hash = { self.class.model_name.element => hash } if include_root_in_json
+ ActiveSupport::JSON.encode(hash)
end
def as_json(options = nil)
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 4508a39347..86149f1e5f 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -5,8 +5,9 @@ module ActiveModel
module Serializers
module Xml
extend ActiveSupport::Concern
+ include ActiveModel::Serialization
- class Serializer < ActiveModel::Serializer #:nodoc:
+ class Serializer #:nodoc:
class Attribute #:nodoc:
attr_reader :name, :value, :type
@@ -74,32 +75,32 @@ module ActiveModel
end
end
- def builder
- @builder ||= begin
- require 'builder' unless defined? ::Builder
- options[:indent] ||= 2
- builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
+ attr_reader :options
- unless options[:skip_instruct]
- builder.instruct!
- options[:skip_instruct] = true
- end
+ def initialize(serializable, options = nil)
+ @serializable = serializable
+ @options = options ? options.dup : {}
- builder
- end
+ @options[:only] = Array.wrap(@options[:only]).map { |n| n.to_s }
+ @options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
end
- def root
- root = (options[:root] || @serializable.class.model_name.singular).to_s
- reformat_name(root)
- end
-
- def dasherize?
- !options.has_key?(:dasherize) || options[:dasherize]
- end
+ # To replicate the behavior in ActiveRecord#attributes,
+ # <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
+ # for a N level model but is set for the N+1 level models,
+ # then because <tt>:except</tt> is set to a default value, the second
+ # level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
+ # <tt>:only</tt> is set, always delete <tt>:except</tt>.
+ def serializable_attribute_names
+ attribute_names = @serializable.attributes.keys.sort
+
+ if options[:only].any?
+ attribute_names &= options[:only]
+ elsif options[:except].any?
+ attribute_names -= options[:except]
+ end
- def camelize?
- options.has_key?(:camelize) && options[:camelize]
+ attribute_names
end
def serializable_attributes
@@ -134,6 +135,34 @@ module ActiveModel
end
private
+ def builder
+ @builder ||= begin
+ require 'builder' unless defined? ::Builder
+ options[:indent] ||= 2
+ builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
+
+ unless options[:skip_instruct]
+ builder.instruct!
+ options[:skip_instruct] = true
+ end
+
+ builder
+ end
+ end
+
+ def root
+ root = (options[:root] || @serializable.class.model_name.singular).to_s
+ reformat_name(root)
+ end
+
+ def dasherize?
+ !options.has_key?(:dasherize) || options[:dasherize]
+ end
+
+ def camelize?
+ options.has_key?(:camelize) && options[:camelize]
+ end
+
def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
@@ -163,8 +192,7 @@ module ActiveModel
end
def to_xml(options = {}, &block)
- serializer = Serializer.new(self, options)
- block_given? ? serializer.to_s(&block) : serializer.to_s
+ Serializer.new(self, options).serialize(&block)
end
def from_xml(xml)
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index 94f1e8f1fd..b49471f7ab 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -1,60 +1,58 @@
module ActiveRecord #:nodoc:
module Serialization
- module RecordSerializer #:nodoc:
- def initialize(*args)
- super
- options[:except] |= Array.wrap(@serializable.class.inheritance_column)
+ extend ActiveSupport::Concern
+ include ActiveModel::Serializers::JSON
+
+ def serializable_hash(options = nil)
+ options ||= {}
+
+ options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
+ options[:except] |= Array.wrap(self.class.inheritance_column)
+
+ hash = super(options)
+
+ serializable_add_includes(options) do |association, records, opts|
+ hash[association] = records.is_a?(Enumerable) ?
+ records.map { |r| r.serializable_hash(opts) } :
+ records.serializable_hash(opts)
end
+ hash
+ end
+
+ private
# Add associations specified via the <tt>:includes</tt> option.
# Expects a block that takes as arguments:
# +association+ - name of the association
# +records+ - the association record(s) to be serialized
# +opts+ - options for the association records
- def add_includes(&block)
- if include_associations = options.delete(:include)
- base_only_or_except = { :except => options[:except],
- :only => options[:only] }
-
- include_has_options = include_associations.is_a?(Hash)
- associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
-
- for association in associations
- records = case @serializable.class.reflect_on_association(association).macro
- when :has_many, :has_and_belongs_to_many
- @serializable.send(association).to_a
- when :has_one, :belongs_to
- @serializable.send(association)
- end
-
- unless records.nil?
- association_options = include_has_options ? include_associations[association] : base_only_or_except
- opts = options.merge(association_options)
- yield(association, records, opts)
- end
- end
+ def serializable_add_includes(options = {})
+ return unless include_associations = options.delete(:include)
- options[:include] = include_associations
- end
- end
+ base_only_or_except = { :except => options[:except],
+ :only => options[:only] }
+
+ include_has_options = include_associations.is_a?(Hash)
+ associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
- def serializable_hash
- hash = super
+ for association in associations
+ records = case self.class.reflect_on_association(association).macro
+ when :has_many, :has_and_belongs_to_many
+ send(association).to_a
+ when :has_one, :belongs_to
+ send(association)
+ end
- add_includes do |association, records, opts|
- hash[association] =
- if records.is_a?(Enumerable)
- records.collect { |r| self.class.new(r, opts).serializable_hash }
- else
- self.class.new(records, opts).serializable_hash
- end
+ unless records.nil?
+ association_options = include_has_options ? include_associations[association] : base_only_or_except
+ opts = options.merge(association_options)
+ yield(association, records, opts)
+ end
end
- hash
+ options[:include] = include_associations
end
- end
end
end
require 'active_record/serializers/xml_serializer'
-require 'active_record/serializers/json_serializer'
diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb
deleted file mode 100644
index 63bf42c09d..0000000000
--- a/activerecord/lib/active_record/serializers/json_serializer.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module ActiveRecord #:nodoc:
- module Serialization
- extend ActiveSupport::Concern
- include ActiveModel::Serializers::JSON
-
- class JSONSerializer < ActiveModel::Serializers::JSON::Serializer
- include Serialization::RecordSerializer
- end
-
- def encode_json(encoder)
- JSONSerializer.new(self, encoder.options).to_s
- end
- end
-end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index 4e172bd2b6..b19920741e 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -2,6 +2,8 @@ require 'active_support/core_ext/hash/conversions'
module ActiveRecord #:nodoc:
module Serialization
+ include ActiveModel::Serializers::Xml
+
# Builds an XML document to represent the model. Some configuration is
# available through +options+. However more complicated cases should
# override ActiveRecord::Base#to_xml.
@@ -169,18 +171,15 @@ module ActiveRecord #:nodoc:
# end
# end
def to_xml(options = {}, &block)
- serializer = XmlSerializer.new(self, options)
- block_given? ? serializer.to_s(&block) : serializer.to_s
- end
-
- def from_xml(xml)
- self.attributes = Hash.from_xml(xml).values.first
- self
+ XmlSerializer.new(self, options).serialize(&block)
end
end
class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
- include Serialization::RecordSerializer
+ def initialize(*args)
+ super
+ options[:except] |= Array.wrap(@serializable.class.inheritance_column)
+ end
def serializable_attributes
serializable_attribute_names.collect { |name| Attribute.new(name, @serializable) }
@@ -235,7 +234,9 @@ module ActiveRecord #:nodoc:
builder.tag!(*args) do
add_attributes
procs = options.delete(:procs)
- add_includes { |association, records, opts| add_associations(association, records, opts) }
+ @serializable.send(:serializable_add_includes, options) { |association, records, opts|
+ add_associations(association, records, opts)
+ }
options[:procs] = procs
add_procs
yield builder if block_given?
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 448db538ab..e28df8efa5 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -62,19 +62,27 @@ module ActiveSupport
end
end
+ RAILS_CACHE_ID = ENV["RAILS_CACHE_ID"]
+ RAILS_APP_VERION = ENV["RAILS_APP_VERION"]
+ EXPANDED_CACHE = RAILS_CACHE_ID || RAILS_APP_VERION
+
def self.expand_cache_key(key, namespace = nil)
expanded_cache_key = namespace ? "#{namespace}/" : ""
- if ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
- expanded_cache_key << "#{ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]}/"
+ if EXPANDED_CACHE
+ expanded_cache_key << "#{RAILS_CACHE_ID || RAILS_APP_VERION}/"
end
- expanded_cache_key << case
- when key.respond_to?(:cache_key)
+ expanded_cache_key <<
+ if key.respond_to?(:cache_key)
key.cache_key
- when key.is_a?(Array)
- key.collect { |element| expand_cache_key(element) }.to_param
- when key
+ elsif key.is_a?(Array)
+ if key.size > 1
+ key.collect { |element| expand_cache_key(element) }.to_param
+ else
+ key.first.to_param
+ end
+ elsif key
key.to_param
end.to_s
diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
index 74ce85a1c2..1602a609eb 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -46,11 +46,12 @@ class Class
end # end
" unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false
EOS
+ self.send("#{sym}=", yield) if block_given?
end
end
- def cattr_accessor(*syms)
+ def cattr_accessor(*syms, &blk)
cattr_reader(*syms)
- cattr_writer(*syms)
+ cattr_writer(*syms, &blk)
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
index fd029544c3..6c67df7f50 100644
--- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
@@ -26,13 +26,14 @@ class Class
end
end
- def superclass_delegating_writer(*names)
+ def superclass_delegating_writer(*names, &block)
names.each do |name|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{name}=(value) # def self.property=(value)
@#{name} = value # @property = value
end # end
EOS
+ self.send("#{name}=", yield) if block_given?
end
end
@@ -42,8 +43,8 @@ class Class
# delegate to their superclass unless they have been given a
# specific value. This stops the strange situation where values
# set after class definition don't get applied to subclasses.
- def superclass_delegating_accessor(*names)
+ def superclass_delegating_accessor(*names, &block)
superclass_delegating_reader(*names)
- superclass_delegating_writer(*names)
+ superclass_delegating_writer(*names, &block)
end
end
diff --git a/ci/ci_build.rb b/ci/ci_build.rb
index 0b9bd3d278..7d81fa843a 100755
--- a/ci/ci_build.rb
+++ b/ci/ci_build.rb
@@ -28,14 +28,14 @@ cd "#{root_dir}/activerecord" do
puts
puts "[CruiseControl] Building ActiveRecord with MySQL"
puts
- build_results[:activerecord_mysql] = system 'rake test_mysql'
+ build_results[:activerecord_mysql] = system 'rake mysql:rebuild_databases && rake test_mysql'
end
cd "#{root_dir}/activerecord" do
puts
puts "[CruiseControl] Building ActiveRecord with PostgreSQL"
puts
- build_results[:activerecord_postgresql8] = system 'rake test_postgresql'
+ build_results[:activerecord_postgresql8] = system 'rake postgresql:rebuild_databases && rake test_postgresql'
end
cd "#{root_dir}/activerecord" do
diff --git a/railties/lib/generators.rb b/railties/lib/generators.rb
index c97c61507a..a2f462ae81 100644
--- a/railties/lib/generators.rb
+++ b/railties/lib/generators.rb
@@ -76,39 +76,37 @@ module Rails
}
def self.aliases #:nodoc:
- @@aliases ||= DEFAULT_ALIASES.dup
+ @aliases ||= DEFAULT_ALIASES.dup
end
def self.options #:nodoc:
- @@options ||= DEFAULT_OPTIONS.dup
+ @options ||= DEFAULT_OPTIONS.dup
end
- # Get paths only from loaded rubygems. In other words, to use rspec
- # generators, you first have to ensure that rspec gem was already loaded.
+ # We have two scenarios here: when rubygems is loaded and when bundler is
+ # being used. If rubygems is loaded, we get all generators paths from loaded
+ # specs. Otherwise we just have to look into vendor/gems/gems.
#
- def self.rubygems_generators_paths
+ def self.gems_generators_paths
paths = []
- return paths unless defined?(Gem)
- Gem.loaded_specs.each do |name, spec|
- generator_path = File.join(spec.full_gem_path, "lib/generators")
- paths << generator_path if File.exist?(generator_path)
+ if defined?(Gem) && Gem.respond_to?(:loaded_specs)
+ Gem.loaded_specs.each do |name, spec|
+ generator_path = File.join(spec.full_gem_path, "lib/generators")
+ paths << generator_path if File.exist?(generator_path)
+ end
+ elsif defined?(RAILS_ROOT)
+ paths += Dir[File.join(RAILS_ROOT, "vendor", "gems", "gems", "*", "lib", "generators")]
end
paths
end
- # If RAILS_ROOT is defined, add vendor/gems, vendor/plugins and lib/generators
- # paths.
+ # Load paths from plugin.
#
- def self.rails_root_generators_paths
- paths = []
- if defined?(RAILS_ROOT)
- paths += Dir[File.join(RAILS_ROOT, "vendor", "gems", "gems", "*", "lib", "generators")]
- paths += Dir[File.join(RAILS_ROOT, "vendor", "plugins", "*", "lib", "generators")]
- paths << File.join(RAILS_ROOT, "lib", "generators")
- end
- paths
+ def self.plugins_generators_paths
+ return [] unless defined?(RAILS_ROOT)
+ Dir[File.join(RAILS_ROOT, "vendor", "plugins", "*", "lib", "generators")]
end
# Hold configured generators fallbacks. If a plugin developer wants a
@@ -125,7 +123,7 @@ module Rails
# Rails::Generators.fallbacks[:shoulda] = :test_unit
#
def self.fallbacks
- @@fallbacks ||= {}
+ @fallbacks ||= {}
end
# Remove the color from output.
@@ -143,14 +141,17 @@ module Rails
# 5) rubygems generators
# 6) builtin generators
#
- # TODO Remove hardcoded paths for all, except (1).
+ # TODO Remove hardcoded paths for all, except (6).
#
def self.load_paths
- @@load_paths ||= begin
- paths = self.rails_root_generators_paths
+ @load_paths ||= begin
+ paths = []
+ paths << File.join(RAILS_ROOT, "lib", "generators") if defined?(RAILS_ROOT)
paths << File.join(Thor::Util.user_home, ".rails", "generators")
- paths += self.rubygems_generators_paths
+ paths += self.plugins_generators_paths
+ paths += self.gems_generators_paths
paths << File.expand_path(File.join(File.dirname(__FILE__), "generators"))
+ paths.uniq!
paths
end
end
@@ -279,39 +280,18 @@ module Rails
end
# Receives namespaces in an array and tries to find matching generators
- # in the load path. Each path is traversed into directory lookups. For
- # example:
- #
- # rails:generators:model
- #
- # Becomes:
- #
- # generators/rails/model/model_generator.rb
- # generators/rails/model_generator.rb
- # generators/model_generator.rb
+ # in the load path.
#
def self.lookup(attempts) #:nodoc:
- attempts.each do |attempt|
- generators_path = ['.']
-
- paths = attempt.gsub(':generators:', ':').split(':')
- name = "#{paths.last}_generator.rb"
-
- until paths.empty?
- generators_path.unshift File.join(*paths)
- paths.pop
- end
-
- generators_path.uniq!
- generators_path = "{#{generators_path.join(',')}}"
-
- self.load_paths.each do |path|
- Dir[File.join(path, generators_path, name)].each do |file|
- begin
- require file
- rescue Exception => e
- warn "[WARNING] Could not load generator at #{file.inspect}. Error: #{e.message}"
- end
+ attempts = attempts.map { |a| "#{a.split(":").last}_generator" }.uniq
+ attempts = "{#{attempts.join(',')}}.rb"
+
+ self.load_paths.each do |path|
+ Dir[File.join(path, '**', attempts)].each do |file|
+ begin
+ require file
+ rescue Exception => e
+ warn "[WARNING] Could not load generator at #{file.inspect}. Error: #{e.message}"
end
end
end
diff --git a/railties/lib/generators/active_record.rb b/railties/lib/generators/active_record.rb
index 924b70881a..ff3093f356 100644
--- a/railties/lib/generators/active_record.rb
+++ b/railties/lib/generators/active_record.rb
@@ -1,7 +1,6 @@
require 'generators/named_base'
require 'generators/migration'
require 'generators/active_model'
-require 'active_record'
module ActiveRecord
module Generators
diff --git a/railties/test/fixtures/vendor/another_gem_path/xspec/lib/generators/xspec_generator.rb b/railties/test/fixtures/vendor/another_gem_path/xspec/lib/generators/xspec_generator.rb
new file mode 100644
index 0000000000..cd477eb4c9
--- /dev/null
+++ b/railties/test/fixtures/vendor/another_gem_path/xspec/lib/generators/xspec_generator.rb
@@ -0,0 +1,2 @@
+class XspecGenerator < Rails::Generators::NamedBase
+end
diff --git a/railties/test/fixtures/vendor/gems/gems/mspec/lib/generators/mspec_generator.rb b/railties/test/fixtures/vendor/plugins/mspec/lib/generators/mspec_generator.rb
index 191bdbf2fc..191bdbf2fc 100644
--- a/railties/test/fixtures/vendor/gems/gems/mspec/lib/generators/mspec_generator.rb
+++ b/railties/test/fixtures/vendor/plugins/mspec/lib/generators/mspec_generator.rb
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index 9444a9ed4b..a258574dce 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -8,9 +8,12 @@ else
RAILS_ROOT = fixtures
end
-$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../activerecord/lib"
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib"
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../activerecord/lib"
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../actionpack/lib"
require 'generators'
+require 'activerecord'
+require 'action_dispatch'
CURRENT_PATH = File.expand_path(Dir.pwd)
Rails::Generators.no_color!
@@ -19,7 +22,7 @@ class GeneratorsTestCase < Test::Unit::TestCase
include FileUtils
def destination_root
- @destination_root ||= File.expand_path(File.join(File.dirname(__FILE__),
+ @destination_root ||= File.expand_path(File.join(File.dirname(__FILE__),
'..', 'fixtures', 'tmp'))
end
diff --git a/railties/test/generators/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb
index 57bd755a9a..293b903b87 100644
--- a/railties/test/generators/session_migration_generator_test.rb
+++ b/railties/test/generators/session_migration_generator_test.rb
@@ -2,16 +2,6 @@ require 'abstract_unit'
require 'generators/generators_test_helper'
require 'generators/rails/session_migration/session_migration_generator'
-module ActiveRecord
- module SessionStore
- class Session
- class << self
- attr_accessor :table_name
- end
- end
- end
-end
-
class SessionMigrationGeneratorTest < GeneratorsTestCase
def test_session_migration_with_default_name
@@ -31,7 +21,10 @@ class SessionMigrationGeneratorTest < GeneratorsTestCase
assert_match /class AddSessionsTable < ActiveRecord::Migration/, migration
assert_match /create_table :custom_table_name/, migration
end
+ ensure
+ ActiveRecord::SessionStore::Session.table_name = "sessions"
end
+
protected
def run_generator(args=[])
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 89d52dd170..4cc0b33521 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -4,6 +4,11 @@ require 'generators/test_unit/model/model_generator'
require 'mocha'
class GeneratorsTest < GeneratorsTestCase
+ def setup
+ Rails::Generators.instance_variable_set(:@load_paths, nil)
+ Gem.stubs(:respond_to?).with(:loaded_specs).returns(false)
+ end
+
def test_invoke_when_generator_is_not_found
output = capture(:stdout){ Rails::Generators.invoke :unknown }
assert_equal "Could not find generator unknown.\n", output
@@ -70,6 +75,20 @@ class GeneratorsTest < GeneratorsTestCase
assert_equal "mspec", klass.namespace
end
+ def test_find_by_namespace_lookup_with_gem_specification
+ assert_nil Rails::Generators.find_by_namespace(:xspec)
+ Rails::Generators.instance_variable_set(:@load_paths, nil)
+
+ spec = Gem::Specification.new
+ spec.expects(:full_gem_path).returns(File.join(RAILS_ROOT, 'vendor', 'another_gem_path', 'xspec'))
+ Gem.expects(:respond_to?).with(:loaded_specs).returns(true)
+ Gem.expects(:loaded_specs).returns(:spec => spec)
+
+ klass = Rails::Generators.find_by_namespace(:xspec)
+ assert klass
+ assert_equal "xspec", klass.namespace
+ end
+
def test_builtin_generators
assert Rails::Generators.builtin.include? %w(rails model)
end