Permalink
Browse files

Catchup Backport to 2.0 of the following Changesets:

[8489]
[8491]
[8496]
[8502]
[8503]
[8504]
[8505]
[8506]
[8508]
[8516]
[8521]
[8522]
[8525]
[8526]
[8527]
[8528]
[8529]
[8531]
[8532]
[8533]
[8534]
[8535]
[8536]
[8539]
[8541]
[8543]
[8554]
[8556]
[8558]
[8560]
[8561]
[8562]
[8576]
[8577]
[8581]
[8583]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8585 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent fa7e937 commit 083b0b7f3f430b9377c08f603eb9063d80c4595a @NZKoz NZKoz committed Jan 7, 2008
Showing with 620 additions and 206 deletions.
  1. +5 −0 actionmailer/CHANGELOG
  2. +14 −1 actionmailer/lib/action_mailer/test_case.rb
  3. +18 −0 actionmailer/test/test_helper_test.rb
  4. +17 −0 actionpack/CHANGELOG
  5. +1 −1 actionpack/Rakefile
  6. +5 −1 actionpack/lib/action_controller/assertions/response_assertions.rb
  7. +2 −2 actionpack/lib/action_controller/base.rb
  8. +5 −3 actionpack/lib/action_controller/filters.rb
  9. +1 −1 actionpack/lib/action_controller/routing.rb
  10. +73 −57 actionpack/lib/action_controller/verification.rb
  11. +12 −4 actionpack/lib/action_view/base.rb
  12. +18 −14 actionpack/lib/action_view/helpers/atom_feed_helper.rb
  13. +1 −1 actionpack/lib/action_view/helpers/text_helper.rb
  14. +18 −0 actionpack/test/controller/action_pack_assertions_test.rb
  15. +4 −1 actionpack/test/controller/base_test.rb
  16. +4 −7 actionpack/test/controller/filters_test.rb
  17. +3 −3 actionpack/test/controller/html-scanner/sanitizer_test.rb
  18. +25 −2 actionpack/test/controller/new_render_test.rb
  19. +19 −0 actionpack/test/controller/render_test.rb
  20. +3 −0 actionpack/test/fixtures/layouts/block_with_layout.erb
  21. +3 −0 actionpack/test/fixtures/layouts/partial_with_layout.erb
  22. +52 −2 actionpack/test/template/atom_feed_helper_test.rb
  23. +1 −1 actionpack/test/template/date_helper_test.rb
  24. +1 −0 actionpack/test/template/text_helper_test.rb
  25. +8 −0 activerecord/CHANGELOG
  26. +1 −1 activerecord/Rakefile
  27. +23 −2 activerecord/lib/active_record/associations.rb
  28. +21 −15 activerecord/lib/active_record/base.rb
  29. +24 −15 activerecord/lib/active_record/fixtures.rb
  30. +35 −5 activerecord/test/associations_test.rb
  31. +25 −0 activerecord/test/base_test.rb
  32. +8 −0 activeresource/CHANGELOG
  33. +0 −1 activeresource/Rakefile
  34. +1 −1 activeresource/lib/active_resource.rb
  35. +19 −10 activeresource/lib/active_resource/base.rb
  36. +3 −3 activeresource/lib/active_resource/custom_methods.rb
  37. +35 −0 activeresource/test/base/load_test.rb
  38. +41 −0 activeresource/test/format_test.rb
  39. +6 −0 activesupport/CHANGELOG
  40. +1 −1 activesupport/Rakefile
  41. +7 −2 activesupport/lib/active_support/core_ext/enumerable.rb
  42. +5 −7 activesupport/lib/active_support/core_ext/hash/keys.rb
  43. +1 −1 activesupport/lib/active_support/inflector.rb
  44. +8 −5 activesupport/lib/active_support/json/decoding.rb
  45. +4 −1 activesupport/test/inflector_test_cases.rb
  46. +2 −0 railties/CHANGELOG
  47. +19 −19 railties/lib/rails_generator/commands.rb
  48. +2 −2 railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
  49. +13 −14 railties/lib/tasks/databases.rake
  50. +3 −0 railties/lib/tasks/framework.rake
View
5 actionmailer/CHANGELOG
@@ -1,3 +1,8 @@
+*SVN*
+
+* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
+
+
*2.0.2* (December 16th, 2007)
* Included in Rails 2.0.2
View
15 actionmailer/lib/action_mailer/test_case.rb
@@ -33,7 +33,7 @@ def determine_default_mailer(name)
end
end
- def setup
+ def setup_with_mailer
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
@@ -42,6 +42,19 @@ def setup
@expected.set_content_type "text", "plain", { "charset" => charset }
@expected.mime_version = '1.0'
end
+ alias_method :setup, :setup_with_mailer
+
+ def self.method_added(method)
+ if method.to_s == 'setup'
+ unless method_defined?(:setup_without_mailer)
+ alias_method :setup_without_mailer, :setup
+ define_method(:setup) do
+ setup_with_mailer
+ setup_without_mailer
+ end
+ end
+ end
+ end
private
def charset
View
18 actionmailer/test/test_helper_test.rb
@@ -115,3 +115,21 @@ def test_assert_no_emails_failure
assert_match /0 .* but 1/, error.message
end
end
+
+class AnotherTestHelperMailerTest < ActionMailer::TestCase
+
+ tests TestHelperMailer
+
+ def setup
+ # Should not override ActionMailer setup methods
+ @test_var = "a value"
+ end
+
+ def test_should_still_setup_mailer
+ assert @expected.is_a?(TMail::Mail)
+ end
+
+ def test_should_run_overridden_setup_method
+ assert @test_var
+ end
+end
View
17 actionpack/CHANGELOG
@@ -1,5 +1,22 @@
*SVN*
+* Support render :text => nil. #6684 [tjennings, PotatoSalad, Cheah Chu Yeow]
+
+* assert_response failures include the exception message. #10688 [Seth Rasmussen]
+
+* Fixed rendering of partials with layout when done from site layout #9209 [antramm]
+
+* Fix atom_feed_helper to comply with the atom spec. Closes #10672 [xaviershay]
+
+ * The tags created do not contain a date (http://feedvalidator.org/docs/error/InvalidTAG.html)
+ * IDs are not guaranteed unique
+ * A default self link was not provided, contrary to the documentation
+ * NOTE: This changes tags for existing atom entries, but at least they validate now.
+
+* Correct indentation in tests. Closes #10671 [l.guidi]
+
+* Fix that auto_link looks for ='s in url paths (Amazon urls have them). Closes #10640 [bgreenlee]
+
* Ensure that test case setup is run even if overridden. #10382 [Josh Peek]
* Fix HTML Sanitizer to allow trailing spaces in CSS style attributes. Closes #10566 [wesley.moxam]
View
2 actionpack/Rakefile
@@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require 'rake/contrib/rubyforgepublisher'
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
@@ -144,6 +143,7 @@ end
desc "Publish the release files to RubyForge."
task :release => [ :package ] do
require 'rubyforge'
+ require 'rake/contrib/rubyforgepublisher'
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
View
6 actionpack/lib/action_controller/assertions/response_assertions.rb
@@ -33,7 +33,11 @@ def assert_response(type, message = nil)
elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
assert_block("") { true } # to count the assertion
else
- assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
+ if @response.error?
+ assert_block(build_message(message, "Expected response to be a <?>, but was <?>\n<?>", type, @response.response_code, @response.template.instance_variable_get(:@exception).message)) { false }
+ else
+ assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
+ end
end
end
end
View
4 actionpack/lib/action_controller/base.rb
@@ -850,8 +850,8 @@ def render(options = nil, &block) #:doc:
response.headers["Location"] = url_for(location)
end
- if text = options[:text]
- render_for_text(text, options[:status])
+ if options.has_key?(:text)
+ render_for_text(options[:text], options[:status])
else
if file = options[:file]
View
8 actionpack/lib/action_controller/filters.rb
@@ -583,10 +583,12 @@ def class_for_filter(filter, filter_type) #:nodoc:
when filter.respond_to?(:call)
if filter.is_a?(Method)
MethodFilter
- elsif filter.arity == 1
- ProcFilter
else
- ProcWithCallFilter
+ case filter.arity
+ when 1; ProcFilter
+ when 2; ProcWithCallFilter
+ else raise ArgumentError, 'Filter blocks must take one or two arguments.'
+ end
end
when filter.respond_to?(:filter)
ClassFilter
View
2 actionpack/lib/action_controller/routing.rb
@@ -1216,7 +1216,7 @@ def #{selector}(*args)
opts = if args.empty? || Hash === args.first
args.first || {}
else
- options = args.last.is_a?(Hash) ? args.pop : {}
+ options = args.extract_options!
args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)|
h[k] = v
h
View
130 actionpack/lib/action_controller/verification.rb
@@ -43,72 +43,88 @@ module ClassMethods
# the user is redirected to a different action. The +options+ parameter
# is a hash consisting of the following key/value pairs:
#
- # * <tt>:params</tt> - a single key or an array of keys that must
- # be in the <tt>params</tt> hash in order for the action(s) to be safely
- # called.
- # * <tt>:session</tt> - a single key or an array of keys that must
- # be in the <tt>session</tt> in order for the action(s) to be safely called.
- # * <tt>:flash</tt> - a single key or an array of keys that must
- # be in the flash in order for the action(s) to be safely called.
- # * <tt>:method</tt> - a single key or an array of keys--any one of which
- # must match the current request method in order for the action(s) to
- # be safely called. (The key should be a symbol: <tt>:get</tt> or
- # <tt>:post</tt>, for example.)
- # * <tt>:xhr</tt> - true/false option to ensure that the request is coming
- # from an Ajax call or not.
- # * <tt>:add_flash</tt> - a hash of name/value pairs that should be merged
- # into the session's flash if the prerequisites cannot be satisfied.
- # * <tt>:add_headers</tt> - a hash of name/value pairs that should be
- # merged into the response's headers hash if the prerequisites cannot
- # be satisfied.
- # * <tt>:redirect_to</tt> - the redirection parameters to be used when
- # redirecting if the prerequisites cannot be satisfied. You can
- # redirect either to named route or to the action in some controller.
- # * <tt>:render</tt> - the render parameters to be used when
- # the prerequisites cannot be satisfied.
- # * <tt>:only</tt> - only apply this verification to the actions specified
- # in the associated array (may also be a single value).
- # * <tt>:except</tt> - do not apply this verification to the actions
- # specified in the associated array (may also be a single value).
+ # <tt>:params</tt>::
+ # a single key or an array of keys that must be in the <tt>params</tt>
+ # hash in order for the action(s) to be safely called.
+ # <tt>:session</tt>::
+ # a single key or an array of keys that must be in the <tt>session</tt>
+ # in order for the action(s) to be safely called.
+ # <tt>:flash</tt>::
+ # a single key or an array of keys that must be in the flash in order
+ # for the action(s) to be safely called.
+ # <tt>:method</tt>::
+ # a single key or an array of keys--any one of which must match the
+ # current request method in order for the action(s) to be safely called.
+ # (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
+ # example.)
+ # <tt>:xhr</tt>::
+ # true/false option to ensure that the request is coming from an Ajax
+ # call or not.
+ # <tt>:add_flash</tt>::
+ # a hash of name/value pairs that should be merged into the session's
+ # flash if the prerequisites cannot be satisfied.
+ # <tt>:add_headers</tt>::
+ # a hash of name/value pairs that should be merged into the response's
+ # headers hash if the prerequisites cannot be satisfied.
+ # <tt>:redirect_to</tt>::
+ # the redirection parameters to be used when redirecting if the
+ # prerequisites cannot be satisfied. You can redirect either to named
+ # route or to the action in some controller.
+ # <tt>:render</tt>::
+ # the render parameters to be used when the prerequisites cannot be satisfied.
+ # <tt>:only</tt>::
+ # only apply this verification to the actions specified in the associated
+ # array (may also be a single value).
+ # <tt>:except</tt>::
+ # do not apply this verification to the actions specified in the associated
+ # array (may also be a single value).
def verify(options={})
- filter_opts = { :only => options[:only], :except => options[:except] }
- before_filter(filter_opts) do |c|
+ before_filter :only => options[:only], :except => options[:except] do |c|
c.send! :verify_action, options
end
end
end
+ private
+
def verify_action(options) #:nodoc:
- prereqs_invalid =
- [*options[:params] ].find { |v| params[v].nil? } ||
- [*options[:session]].find { |v| session[v].nil? } ||
- [*options[:flash] ].find { |v| flash[v].nil? }
-
- if !prereqs_invalid && options[:method]
- prereqs_invalid ||=
- [*options[:method]].all? { |v| request.method != v.to_sym }
- end
-
- prereqs_invalid ||= (request.xhr? != options[:xhr]) unless options[:xhr].nil?
-
- if prereqs_invalid
- flash.update(options[:add_flash]) if options[:add_flash]
+ if prereqs_invalid?(options)
+ flash.update(options[:add_flash]) if options[:add_flash]
response.headers.update(options[:add_headers]) if options[:add_headers]
-
- unless performed?
- case
- when options[:render]
- render(options[:render])
- when options[:redirect_to]
- options[:redirect_to] = self.send!(options[:redirect_to]) if options[:redirect_to].is_a?(Symbol)
- redirect_to(options[:redirect_to])
- else
- head(:bad_request)
- end
- end
+ apply_remaining_actions(options) unless performed?
+ end
+ end
+
+ def prereqs_invalid?(options) # :nodoc:
+ verify_presence_of_keys_in_hash_flash_or_params(options) ||
+ verify_method(options) ||
+ verify_request_xhr_status(options)
+ end
+
+ def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
+ [*options[:params] ].find { |v| params[v].nil? } ||
+ [*options[:session]].find { |v| session[v].nil? } ||
+ [*options[:flash] ].find { |v| flash[v].nil? }
+ end
+
+ def verify_method(options) # :nodoc:
+ [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method]
+ end
+
+ def verify_request_xhr_status(options) # :nodoc:
+ request.xhr? != options[:xhr] unless options[:xhr].nil?
+ end
+
+ def apply_redirect_to(redirect_to_option) # :nodoc:
+ redirect_to_option.is_a?(Symbol) ? self.send!(redirect_to_option) : redirect_to_option
+ end
+
+ def apply_remaining_actions(options) # :nodoc:
+ case
+ when options[:render] ; render(options[:render])
+ when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to]))
+ else head(:bad_request)
end
end
-
- private :verify_action
end
end
View
16 actionpack/lib/action_view/base.rb
@@ -338,11 +338,13 @@ def render(options = {}, old_local_assigns = {}, &block) #:nodoc:
path, partial_name = partial_pieces(options.delete(:layout))
if block_given?
- @content_for_layout = capture(&block)
- concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding)
+ wrap_content_for_layout capture(&block) do
+ concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding)
+ end
else
- @content_for_layout = render(options)
- render(options.merge(:partial => "#{path}/#{partial_name}"))
+ wrap_content_for_layout render(options) do
+ render(options.merge(:partial => "#{path}/#{partial_name}"))
+ end
end
elsif options[:file]
render_file(options[:file], options[:use_full_path], options[:locals])
@@ -441,6 +443,12 @@ def append_view_path(path)
end
private
+ def wrap_content_for_layout(content)
+ original_content_for_layout = @content_for_layout
+ @content_for_layout = content
+ returning(yield) { @content_for_layout = original_content_for_layout }
+ end
+
def find_full_template_path(template_path, extension)
file_name = "#{template_path}.#{extension}"
base_path = find_base_path_for(file_name)
View
32 actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -26,7 +26,7 @@ module AtomFeedHelper
# end
#
# app/views/posts/index.atom.builder:
- # atom_feed do |feed|
+ # atom_feed(:tag_uri => "2008") do |feed|
# feed.title("My great blog!")
# feed.updated((@posts.first.created_at))
#
@@ -44,31 +44,35 @@ module AtomFeedHelper
#
# The options are for atom_feed are:
#
+ # * <tt>:schema_date</tt>: Required. The date at which the tag scheme for the feed was first used. A good default is the year you created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information.
# * <tt>:language</tt>: Defaults to "en-US".
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
#
# atom_feed yields a AtomFeedBuilder instance.
def atom_feed(options = {}, &block)
+ if options[:schema_date].blank?
+ logger.warn("You must provide the :schema_date option to atom_feed for your feed to be valid. A good default is the year you first created this feed.") unless logger.nil?
+ else
+ options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
+ end
+
xml = options[:xml] || eval("xml", block.binding)
xml.instruct!
xml.feed "xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom' do
- xml.id("tag:#{request.host}:#{request.request_uri.split(".")[0].gsub("/", "")}")
+ xml.id("tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
-
- if options[:url]
- xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
- end
-
- yield AtomFeedBuilder.new(xml, self)
+ xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
+
+ yield AtomFeedBuilder.new(xml, self, options)
end
end
class AtomFeedBuilder
- def initialize(xml, view)
- @xml, @view = xml, view
+ def initialize(xml, view, feed_options = {})
+ @xml, @view, @feed_options = xml, view, feed_options
end
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
@@ -85,7 +89,7 @@ def updated(date_or_time = nil)
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
def entry(record, options = {})
@xml.entry do
- @xml.id("tag:#{@view.request.host_with_port}:#{record.class}#{record.id}")
+ @xml.id("tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
@xml.published((options[:published] || record.created_at).xmlschema)
@@ -102,10 +106,10 @@ def entry(record, options = {})
end
private
- def method_missing(method, *arguments)
- @xml.__send__(method, *arguments)
+ def method_missing(method, *arguments, &block)
+ @xml.__send__(method, *arguments, &block)
end
end
end
end
-end
+end
View
2 actionpack/lib/action_view/helpers/text_helper.rb
@@ -436,7 +436,7 @@ def set_cycle(name, cycle_object)
[-\w]+ # subdomain or domain
(?:\.[-\w]+)* # remaining subdomains or domain
(?::\d+)? # port
- (?:/(?:(?:[~\w\+@%-]|(?:[,.;:][^\s$]))+)?)* # path
+ (?:/(?:(?:[~\w\+@%=-]|(?:[,.;:][^\s$]))+)?)* # path
(?:\?[\w\+@%&=.;-]+)? # query string
(?:\#[\w\-]*)? # trailing anchor
)
View
18 actionpack/test/controller/action_pack_assertions_test.rb
@@ -124,6 +124,15 @@ def full_messages; ['...stuff...']; end
def rescue_action(e) raise; end
end
+# Used to test that assert_response includes the exception message
+# in the failure message when an action raises and assert_response
+# is expecting something other than an error.
+class AssertResponseWithUnexpectedErrorController < ActionController::Base
+ def index
+ raise 'FAIL'
+ end
+end
+
module Admin
class InnerModuleController < ActionController::Base
def index
@@ -465,6 +474,15 @@ def test_assert_valid_failing
rescue Test::Unit::AssertionFailedError => e
end
end
+
+ def test_assert_response_uses_exception_message
+ @controller = AssertResponseWithUnexpectedErrorController.new
+ get :index
+ assert_response :success
+ flunk 'Expected non-success response'
+ rescue Test::Unit::AssertionFailedError => e
+ assert e.message.include?('FAIL')
+ end
end
class ActionPackHeaderTest < Test::Unit::TestCase
View
5 actionpack/test/controller/base_test.rb
@@ -87,7 +87,10 @@ def test_action_methods
# Mocha adds some public instance methods to Object that would be
# considered actions, so explicitly hide_action them.
def hide_mocha_methods_from_controller(controller)
- mocha_methods = [:expects, :metaclass, :mocha, :mocha_inspect, :reset_mocha, :stubba_object, :stubba_method, :stubs, :verify, :__metaclass__, :__is_a__]
+ mocha_methods = [
+ :expects, :mocha, :mocha_inspect, :reset_mocha, :stubba_object,
+ :stubba_method, :stubs, :verify, :__metaclass__, :__is_a__, :to_matcher,
+ ]
controller.class.send!(:hide_action, *mocha_methods)
end
end
View
11 actionpack/test/controller/filters_test.rb
@@ -696,10 +696,6 @@ class ControllerWithProcFilter < PostsController
end
end
-class ControllerWithWrongFilterType < PostsController
- around_filter lambda { yield }, :only => :no_raise
-end
-
class ControllerWithNestedFilters < ControllerWithSymbolAsFilter
around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both
end
@@ -746,14 +742,15 @@ def test_filters_registering
assert_equal 1, ControllerWithFilterClass.filter_chain.size
assert_equal 1, ControllerWithFilterInstance.filter_chain.size
assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
- assert_equal 1, ControllerWithWrongFilterType.filter_chain.size
assert_equal 6, ControllerWithNestedFilters.filter_chain.size
assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
end
def test_wrong_filter_type
- assert_raise(ActionController::ActionControllerError) do
- test_process(ControllerWithWrongFilterType,'no_raise')
+ assert_raise ArgumentError do
+ Class.new PostsController do
+ around_filter lambda { yield }
+ end
end
end
View
6 actionpack/test/controller/html-scanner/sanitizer_test.rb
@@ -204,9 +204,9 @@ def test_should_sanitize_illegal_style_properties
end
def test_should_sanitize_with_trailing_space
- raw = "display:block; "
- expected = "display: block;"
- assert_equal expected, sanitize_css(raw)
+ raw = "display:block; "
+ expected = "display: block;"
+ assert_equal expected, sanitize_css(raw)
end
def test_should_sanitize_xul_style_attributes
View
27 actionpack/test/controller/new_render_test.rb
@@ -361,10 +361,18 @@ def render_call_to_partial_with_layout
render :action => "calling_partial_with_layout"
end
+ def render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
+ render :action => "calling_partial_with_layout"
+ end
+
def render_using_layout_around_block
render :action => "using_layout_around_block"
end
+ def render_using_layout_around_block_in_main_layout_and_within_content_for_layout
+ render :action => "using_layout_around_block"
+ end
+
def rescue_action(e) raise end
private
@@ -387,6 +395,10 @@ def determine_layout
"layouts/builder"
when "action_talk_to_layout", "layout_overriding_layout"
"layouts/talk_from_action"
+ when "render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout"
+ "layouts/partial_with_layout"
+ when "render_using_layout_around_block_in_main_layout_and_within_content_for_layout"
+ "layouts/block_with_layout"
end
end
end
@@ -824,9 +836,20 @@ def test_render_call_to_partial_with_layout
get :render_call_to_partial_with_layout
assert_equal "Before (David)\nInside from partial (David)\nAfter", @response.body
end
-
+
+ def test_render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
+ get :render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
+ assert_equal "Before (Anthony)\nInside from partial (Anthony)\nAfter\nBefore (David)\nInside from partial (David)\nAfter\nBefore (Ramm)\nInside from partial (Ramm)\nAfter", @response.body
+ end
+
def test_using_layout_around_block
- get :using_layout_around_block
+ get :render_using_layout_around_block
assert_equal "Before (David)\nInside from block\nAfter", @response.body
end
+
+ def test_using_layout_around_block_in_main_layout_and_within_content_for_layout
+ get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout
+ assert_equal "Before (Anthony)\nInside from first block in layout\nAfter\nBefore (David)\nInside from block\nAfter\nBefore (Ramm)\nInside from second block in layout\nAfter\n", @response.body
+ end
+
end
View
19 actionpack/test/controller/render_test.rb
@@ -57,6 +57,14 @@ def render_custom_code
render :text => "hello world", :status => 404
end
+ def render_text_with_nil
+ render :text => nil
+ end
+
+ def render_text_with_false
+ render :text => false
+ end
+
def render_nothing_with_appendix
render :text => "appended"
end
@@ -263,6 +271,17 @@ def test_render_custom_code
assert_equal 'hello world', @response.body
end
+ def test_render_text_with_nil
+ get :render_text_with_nil
+ assert_response 200
+ assert_equal '', @response.body
+ end
+
+ def test_render_text_with_false
+ get :render_text_with_false
+ assert_equal 'false', @response.body
+ end
+
def test_render_nothing_with_appendix
get :render_nothing_with_appendix
assert_response 200
View
3 actionpack/test/fixtures/layouts/block_with_layout.erb
@@ -0,0 +1,3 @@
+<% render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% end %>
+<%= yield %>
+<% render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>
View
3 actionpack/test/fixtures/layouts/partial_with_layout.erb
@@ -0,0 +1,3 @@
+<%= render( :layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => {:name => 'Anthony' } ) %>
+<%= yield %>
+<%= render( :layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => {:name => 'Ramm' } ) %>
View
54 actionpack/test/template/atom_feed_helper_test.rb
@@ -5,7 +5,7 @@
class ScrollsController < ActionController::Base
FEEDS = {}
FEEDS["defaults"] = <<-EOT
- atom_feed do |feed|
+ atom_feed(:schema_date => '2008') do |feed|
feed.title("My great blog!")
feed.updated((@scrolls.first.created_at))
@@ -38,7 +38,23 @@ class ScrollsController < ActionController::Base
end
end
EOT
+ FEEDS["xml_block"] = <<-EOT
+ atom_feed do |feed|
+ feed.title("My great blog!")
+ feed.updated((@scrolls.first.created_at))
+ feed.author do |author|
+ author.name("DHH")
+ end
+
+ for scroll in @scrolls
+ feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
+ entry.title(scroll.title)
+ entry.content(scroll.body, :type => 'html')
+ end
+ end
+ end
+ EOT
def index
@scrolls = [
Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)),
@@ -47,6 +63,12 @@ def index
render :inline => FEEDS[params[:id]], :type => :builder
end
+
+ protected
+
+ def rescue_action(e)
+ raise(e)
+ end
end
class AtomFeedTest < Test::Unit::TestCase
@@ -88,6 +110,34 @@ def test_entry_with_prefilled_options_should_use_those_instead_of_querying_the_r
end
end
+ def test_self_url_should_default_to_current_request_url
+ with_restful_routing(:scrolls) do
+ get :index, :id => "defaults"
+ assert_select "link[rel=self][href=http://www.nextangle.com/scrolls?id=defaults]"
+ end
+ end
+
+ def test_feed_id_should_be_a_valid_tag
+ with_restful_routing(:scrolls) do
+ get :index, :id => "defaults"
+ assert_select "id", :text => "tag:www.nextangle.com,2008:/scrolls?id=defaults"
+ end
+ end
+
+ def test_entry_id_should_be_a_valid_tag
+ with_restful_routing(:scrolls) do
+ get :index, :id => "defaults"
+ assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/1"
+ assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/2"
+ end
+ end
+
+ def test_feed_should_allow_nested_xml_blocks
+ with_restful_routing(:scrolls) do
+ get :index, :id => "xml_block"
+ assert_select "author name", :text => "DHH"
+ end
+ end
private
def with_restful_routing(resources)
@@ -98,4 +148,4 @@ def with_restful_routing(resources)
yield
end
end
-end
+end
View
2 actionpack/test/template/date_helper_test.rb
@@ -1395,7 +1395,7 @@ def test_include_blank_overrides_default_option
expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %(<option value=""></option>\n)
- 2002.upto(2012) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
+ (Time.now.year - 5).upto(Time.now.year + 5) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
expected << %(<option value=""></option>\n)
View
1 actionpack/test/template/text_helper_test.rb
@@ -175,6 +175,7 @@ def test_auto_link_parsing
http://www.rubyonrails.com/~minam/contact;new?with=query&string=params
http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007
http://www.mail-archive.com/rails@lists.rubyonrails.org/
+ http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1
)
urls.each do |url|
View
8 activerecord/CHANGELOG
@@ -1,5 +1,13 @@
*SVN*
+* update_all ignores scoped :order and :limit, so post.comments.update_all doesn't try to include the comment order in the update statement. #10686 [Brendan Ribera]
+
+* Added by parameter to increment, decrement, and their bang varieties so you can do player1.increment!(:points, 5) #10542 [Sam]
+
+* Optimize ActiveRecord::Base#exists? to use #select_all instead of #find. Closes #10605 [jamesh, fcheung, protocool]
+
+* Don't unnecessarily load has_many associations in after_update callbacks. Closes #6822 [stopdropandrew, canadaduane]
+
* Eager belongs_to :include infers the foreign key from the association name rather than the class name. #10517 [Jonathan Viney]
* SQLite: fix rename_ and remove_column for columns with unique indexes. #10576 [Brandon Keepers]
View
2 activerecord/Rakefile
@@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require 'rake/contrib/rubyforgepublisher'
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
@@ -239,6 +238,7 @@ end
desc "Publish the release files to RubyForge."
task :release => [ :package ] do
require 'rubyforge'
+ require 'rake/contrib/rubyforgepublisher'
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
View
25 activerecord/lib/active_record/associations.rb
@@ -130,7 +130,7 @@ def clear_association_cache #:nodoc:
# #others<< | X | X | X
# #others.push | X | X | X
# #others.concat | X | X | X
- # #others.build(attributes={}) | X | X | X
+ # #others.build(attributes={}) | X | X |
# #others.create(attributes={}) | X | X |
# #others.create!(attributes={}) | X | X | X
# #others.size | X | X | X
@@ -487,6 +487,22 @@ def clear_association_cache #:nodoc:
# When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
# before the actual model exists.
#
+ # Eager loading is not possible with polymorphic associations. Given
+ #
+ # class Address < ActiveRecord::Base
+ # belongs_to :addressable, :polymorphic => true
+ # end
+ #
+ # a call that tries to eager load the addressable model
+ #
+ # Address.find(:all, :include => :addressable) # INVALID
+ #
+ # will raise <tt>ActiveRecord::EagerLoadPolymorphicError</tt>. The reason is that the parent model's type
+ # is a column value so its corresponding table name cannot be put in the FROM/JOIN clauses of that early query.
+ #
+ # It does work the other way around though: if the <tt>User</tt> model is <tt>addressable</tt> you can eager load
+ # their addresses with <tt>:include</tt> just fine, every piece needed to construct the query is known beforehand.
+ #
# == Table Aliasing
#
# ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
@@ -783,6 +799,7 @@ def has_one(association_id, options = {})
# a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
# Note: Specifying a counter_cache will add it to that model's list of readonly attributes using #attr_readonly.
# * <tt>:include</tt> - specify second-order associations that should be eager loaded when this object is loaded.
+ # Not allowed if the association is polymorphic.
# * <tt>:polymorphic</tt> - specify this association is a polymorphic association by passing +true+.
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
# to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end).
@@ -1079,8 +1096,10 @@ def add_multiple_associated_save_callbacks(association_name)
if association.respond_to?(:loaded?)
if new_record?
association
- else
+ elsif association.loaded?
association.select { |record| record.new_record? }
+ else
+ association.target.select { |record| record.new_record? }
end.each do |record|
errors.add "#{association_name}" unless record.valid?
end
@@ -1097,6 +1116,8 @@ def add_multiple_associated_save_callbacks(association_name)
association
elsif association.respond_to?(:loaded?) && association.loaded?
association.select { |record| record.new_record? }
+ elsif association.respond_to?(:loaded?) && !association.loaded?
+ association.target.select { |record| record.new_record? }
else
[]
end
View
36 activerecord/lib/active_record/base.rb
@@ -548,8 +548,14 @@ def find_by_sql(sql)
# Person.exists?(:name => "David")
# Person.exists?(['name LIKE ?', "%#{query}%"])
def exists?(id_or_conditions)
- !find(:first, :select => "#{quoted_table_name}.#{primary_key}",
- :conditions => expand_id_conditions(id_or_conditions)).nil?
+ connection.select_all(
+ construct_finder_sql(
+ :select => "#{quoted_table_name}.#{primary_key}",
+ :conditions => expand_id_conditions(id_or_conditions),
+ :limit => 1
+ ),
+ "#{name} Exists"
+ ).size > 0
end
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -674,8 +680,8 @@ def update_all(updates, conditions = nil, options = {})
sql = "UPDATE #{table_name} SET #{sanitize_sql_for_assignment(updates)} "
scope = scope(:find)
add_conditions!(sql, conditions, scope)
- add_order!(sql, options[:order], scope)
- add_limit!(sql, options, scope)
+ add_order!(sql, options[:order], nil)
+ add_limit!(sql, options, nil)
connection.update(sql, "#{name} Update")
end
@@ -1487,7 +1493,7 @@ def method_missing(method_id, *arguments)
self.class_eval %{
def self.#{method_id}(*args)
- options = args.last.is_a?(Hash) ? args.pop : {}
+ options = args.extract_options!
attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
finder_options = { :conditions => attributes }
validate_find_options(options)
@@ -2046,28 +2052,28 @@ def update_attributes!(attributes)
save!
end
- # Initializes the +attribute+ to zero if nil and adds one. Only makes sense for number-based attributes. Returns self.
- def increment(attribute)
+ # Initializes the +attribute+ to zero if nil and adds the value passed as +by+ (default is one). Only makes sense for number-based attributes. Returns self.
+ def increment(attribute, by = 1)
self[attribute] ||= 0
- self[attribute] += 1
+ self[attribute] += by
self
end
# Increments the +attribute+ and saves the record.
- def increment!(attribute)
- increment(attribute).update_attribute(attribute, self[attribute])
+ def increment!(attribute, by = 1)
+ increment(attribute, by).update_attribute(attribute, self[attribute])
end
- # Initializes the +attribute+ to zero if nil and subtracts one. Only makes sense for number-based attributes. Returns self.
- def decrement(attribute)
+ # Initializes the +attribute+ to zero if nil and subtracts the value passed as +by+ (default is one). Only makes sense for number-based attributes. Returns self.
+ def decrement(attribute, by = 1)
self[attribute] ||= 0
- self[attribute] -= 1
+ self[attribute] -= by
self
end
# Decrements the +attribute+ and saves the record.
- def decrement!(attribute)
- decrement(attribute).update_attribute(attribute, self[attribute])
+ def decrement!(attribute, by = 1)
+ decrement(attribute, by).update_attribute(attribute, self[attribute])
end
# Turns an +attribute+ that's currently true into false and vice versa. Returns self.
View
39 activerecord/lib/active_record/fixtures.rb
@@ -644,8 +644,16 @@ def read_fixture_files; end
end
def model_class
- @model_class ||= @class_name.is_a?(Class) ?
- @class_name : @class_name.constantize rescue nil
+ unless defined?(@model_class)
+ @model_class =
+ if @class_name.nil? || @class_name.is_a?(Class)
+ @class_name
+ else
+ @class_name.constantize rescue nil
+ end
+ end
+
+ @model_class
end
def primary_key_name
@@ -681,7 +689,7 @@ def read_fixture_files
Dir.entries(@fixture_path).each do |file|
path = File.join(@fixture_path, file)
if File.file?(path) and file !~ @file_filter
- self[file] = Fixture.new(path, @class_name)
+ self[file] = Fixture.new(path, model_class)
end
end
end
@@ -709,7 +717,7 @@ def read_yaml_fixture_files
raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
end
- self[name] = Fixture.new(data, @class_name)
+ self[name] = Fixture.new(data, model_class)
end
end
end
@@ -722,7 +730,7 @@ def read_csv_fixture_files
reader.each do |row|
data = {}
row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
- self["#{Inflector::underscore(@class_name)}_#{i+=1}"]= Fixture.new(data, @class_name)
+ self["#{Inflector::underscore(@class_name)}_#{i+=1}"] = Fixture.new(data, model_class)
end
end
@@ -758,9 +766,9 @@ class FixtureError < StandardError #:nodoc:
class FormatError < FixtureError #:nodoc:
end
- attr_reader :class_name
+ attr_reader :model_class
- def initialize(fixture, class_name)
+ def initialize(fixture, model_class)
case fixture
when Hash, YAML::Omap
@fixture = fixture
@@ -770,7 +778,11 @@ def initialize(fixture, class_name)
raise ArgumentError, "Bad fixture argument #{fixture.inspect} during creation of #{class_name} fixture"
end
- @class_name = class_name
+ @model_class = model_class.is_a?(Class) ? model_class : model_class.constantize rescue nil
+ end
+
+ def class_name
+ @model_class.name if @model_class
end
def each
@@ -791,21 +803,18 @@ def key_list
end
def value_list
- klass = @class_name.constantize rescue nil
-
list = @fixture.inject([]) do |fixtures, (key, value)|
- col = klass.columns_hash[key] if klass.respond_to?(:ancestors) && klass.ancestors.include?(ActiveRecord::Base)
+ col = model_class.columns_hash[key] if model_class.respond_to?(:ancestors) && model_class.ancestors.include?(ActiveRecord::Base)
fixtures << ActiveRecord::Base.connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
end
list * ', '
end
def find
- klass = @class_name.is_a?(Class) ? @class_name : Object.const_get(@class_name) rescue nil
- if klass
- klass.find(self[klass.primary_key])
+ if model_class
+ model_class.find(self[model_class.primary_key])
else
- raise FixtureClassNotFound, "The class #{@class_name.inspect} was not found."
+ raise FixtureClassNotFound, "No class attached to find."
end
end
View
40 activerecord/test/associations_test.rb
@@ -72,23 +72,23 @@ def test_storing_in_pstore
end
end
end
-
+
class AssociationProxyTest < Test::Unit::TestCase
fixtures :authors, :posts, :categorizations, :categories, :developers, :projects, :developers_projects
-
+
def test_proxy_accessors
welcome = posts(:welcome)
assert_equal welcome, welcome.author.proxy_owner
assert_equal welcome.class.reflect_on_association(:author), welcome.author.proxy_reflection
welcome.author.class # force load target
assert_equal welcome.author, welcome.author.proxy_target
-
+
david = authors(:david)
assert_equal david, david.posts.proxy_owner
assert_equal david.class.reflect_on_association(:posts), david.posts.proxy_reflection
david.posts.first # force load target
assert_equal david.posts, david.posts.proxy_target
-
+
assert_equal david, david.posts_with_extension.testing_proxy_owner
assert_equal david.class.reflect_on_association(:posts_with_extension), david.posts_with_extension.testing_proxy_reflection
david.posts_with_extension.first # force load target
@@ -98,10 +98,28 @@ def test_proxy_accessors
def test_push_does_not_load_target
david = authors(:david)
+ david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
+ assert !david.posts.loaded?
+ assert david.posts.include?(post)
+ end
+
+ def test_push_has_many_through_does_not_load_target
+ david = authors(:david)
+
david.categories << categories(:technology)
assert !david.categories.loaded?
assert david.categories.include?(categories(:technology))
end
+
+ def test_push_followed_by_save_does_not_load_target
+ david = authors(:david)
+
+ david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
+ assert !david.posts.loaded?
+ david.save
+ assert !david.posts.loaded?
+ assert david.posts.include?(post)
+ end
def test_push_does_not_lose_additions_to_new_record
josh = Author.new(:name => "Josh")
@@ -772,7 +790,13 @@ def test_build_many
assert companies(:first_firm).save
assert_equal 3, companies(:first_firm).clients_of_firm(true).size
end
-
+
+ def test_build_followed_by_save_does_not_load_target
+ new_client = companies(:first_firm).clients_of_firm.build("name" => "Another Client")
+ assert companies(:first_firm).save
+ assert !companies(:first_firm).clients_of_firm.loaded?
+ end
+
def test_build_without_loading_association
first_topic = topics(:first)
Reply.column_names
@@ -825,6 +849,12 @@ def test_create_many
assert_equal 3, companies(:first_firm).clients_of_firm(true).size
end
+ def test_create_followed_by_save_does_not_load_target
+ new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
+ assert companies(:first_firm).save
+ assert !companies(:first_firm).clients_of_firm.loaded?
+ end
+
def test_find_or_initialize
the_client = companies(:first_firm).clients.find_or_initialize_by_name("Yet another client")
assert_equal companies(:first_firm).id, the_client.firm_id
View
25 activerecord/test/base_test.rb
@@ -596,6 +596,13 @@ def test_update_all_with_order_and_limit
end
end
+ def test_update_all_ignores_order_limit_from_association
+ author = Author.find(1)
+ assert_nothing_raised do
+ assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all("body = 'bulk update!'")
+ end
+ end
+
def test_update_many
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
updated = Topic.update(topic_data.keys, topic_data.values)
@@ -1275,6 +1282,15 @@ def test_increment_nil_attribute
assert_equal 1, topics(:first).parent_id
end
+ def test_increment_attribute_by
+ assert_equal 50, accounts(:signals37).credit_limit
+ accounts(:signals37).increment! :credit_limit, 5
+ assert_equal 55, accounts(:signals37, :reload).credit_limit
+
+ accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3)
+ assert_equal 59, accounts(:signals37, :reload).credit_limit
+ end
+
def test_decrement_attribute
assert_equal 50, accounts(:signals37).credit_limit
@@ -1285,6 +1301,15 @@ def test_decrement_attribute
assert_equal 47, accounts(:signals37, :reload).credit_limit
end
+ def test_decrement_attribute_by
+ assert_equal 50, accounts(:signals37).credit_limit
+ accounts(:signals37).decrement! :credit_limit, 5
+ assert_equal 45, accounts(:signals37, :reload).credit_limit
+
+ accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3)
+ assert_equal 41, accounts(:signals37, :reload).credit_limit
+ end
+
def test_toggle_attribute
assert !topics(:first).approved?
topics(:first).toggle!(:approved)
View
8 activeresource/CHANGELOG
@@ -1,5 +1,13 @@
*SVN*
+* Fix small documentation typo. Closes #10670 [l.guidi]
+
+* find_or_create_resource_for handles module nesting. #10646 [xavier]
+
+* Allow setting ActiveResource::Base#format before #site. [rick]
+
+* Support agnostic formats when calling custom methods. Closes #10635 [joerichsen]
+
* Document custom methods. #10589 [Cheah Chu Yeow]
* Ruby 1.9 compatibility. [Jeremy Kemper]
View
1 activeresource/Rakefile
@@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
-require 'rake/contrib/rubyforgepublisher'
require File.join(File.dirname(__FILE__), 'lib', 'active_resource', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
View
2 activeresource/lib/active_resource.rb
@@ -27,7 +27,7 @@
unless defined?(ActiveSupport)
begin
$:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
- require 'active_support'
+ require 'active_support'
rescue LoadError
require 'rubygems'
gem 'activesupport'
View
29 activeresource/lib/active_resource/base.rb
@@ -47,7 +47,7 @@ module ActiveResource
#
# Since simple CRUD/lifecycle methods can't accomplish every task, Active Resource also supports
# defining your own custom REST methods. To invoke them, Active Resource provides the <tt>get</tt>,
- # <tt>post</tt>, <tt>post</tt> and <tt>put</tt> methods where you can specify a custom REST method
+ # <tt>post</tt>, <tt>put</tt> and <tt>delete</tt> methods where you can specify a custom REST method
# name to invoke.
#
# # POST to the custom 'register' REST method, i.e. POST /people/new/register.xml.
@@ -192,7 +192,7 @@ def format=(mime_type_reference_or_format)
ActiveResource::Formats[mime_type_reference_or_format] : mime_type_reference_or_format
write_inheritable_attribute("format", format)
- connection.format = format
+ connection.format = format if site
end
# Returns the current format, default is ActiveResource::Formats::XmlFormat
@@ -845,17 +845,26 @@ def find_or_create_resource_for_collection(name)
find_or_create_resource_for(name.to_s.singularize)
end
+ # Tries to find a resource in a non empty list of nested modules
+ # Raises a NameError if it was not found in any of the given nested modules
+ def find_resource_in_modules(resource_name, module_names)
+ receiver = Object
+ namespaces = module_names[0, module_names.size-1].map do |module_name|
+ receiver = receiver.const_get(module_name)
+ end
+ if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(resource_name) }
+ return namespace.const_get(resource_name)
+ else
+ raise NameError
+ end
+ end
+
# Tries to find a resource for a given name; if it fails, then the resource is created
def find_or_create_resource_for(name)
resource_name = name.to_s.camelize
-
- # FIXME: Make it generic enough to support any depth of module nesting
- if (ancestors = self.class.name.split("::")).size > 1
- begin
- ancestors.first.constantize.const_get(resource_name)
- rescue NameError
- self.class.const_get(resource_name)
- end
+ ancestors = self.class.name.split("::")
+ if ancestors.size > 1
+ find_resource_in_modules(resource_name, ancestors)
else
self.class.const_get(resource_name)
end
View
6 activeresource/lib/active_resource/custom_methods.rb
@@ -80,7 +80,7 @@ def delete(custom_method_name, options = {})
module ClassMethods
def custom_method_collection_url(method_name, options = {})
prefix_options, query_options = split_options(options)
- "#{prefix(prefix_options)}#{collection_name}/#{method_name}.xml#{query_string(query_options)}"
+ "#{prefix(prefix_options)}#{collection_name}/#{method_name}.#{format.extension}#{query_string(query_options)}"
end
end
@@ -108,11 +108,11 @@ def delete(method_name, options = {})
private
def custom_method_element_url(method_name, options = {})
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.xml#{self.class.send!(:query_string, options)}"
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.send!(:query_string, options)}"
end
def custom_method_new_element_url(method_name, options = {})
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.xml#{self.class.send!(:query_string, options)}"
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.send!(:query_string, options)}"
end
end
end
View
35 activeresource/test/base/load_test.rb
@@ -10,6 +10,29 @@ class Note < ActiveResource::Base
class Comment < ActiveResource::Base
self.site = "http://37s.sunrise.i:3000"
end
+
+ module Deeply
+ module Nested
+
+ class Note < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+
+ class Comment < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+
+ module TestDifferentLevels
+
+ class Note < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+
+ end
+
+ end
+ end
+
end
@@ -108,4 +131,16 @@ def test_nested_collections_within_the_same_namespace
n = Highrise::Note.new(:comments => [{ :name => "1" }])
assert_kind_of Highrise::Comment, n.comments.first
end
+
+ def test_nested_collections_within_deeply_nested_namespace
+ n = Highrise::Deeply::Nested::Note.new(:comments => [{ :name => "1" }])
+ assert_kind_of Highrise::Deeply::Nested::Comment, n.comments.first
+ end
+
+ def test_nested_collections_in_different_levels_of_namespaces
+ n = Highrise::Deeply::Nested::TestDifferentLevels::Note.new(:comments => [{ :name => "1" }])
+ assert_kind_of Highrise::Deeply::Nested::Comment, n.comments.first
+ end
+
+
end
View
41 activeresource/test/format_test.rb
@@ -29,6 +29,47 @@ def test_formats_on_collection
end
end
+ def test_formats_on_custom_collection_method
+ for format in [ :json, :xml ]
+ using_format(Person, format) do
+ ActiveResource::HttpMock.respond_to.get "/people/retrieve.#{format}?name=David", {}, ActiveResource::Formats[format].encode([@david])
+ remote_programmers = Person.get(:retrieve, :name => 'David')
+ assert_equal 1, remote_programmers.size
+ assert_equal @david[:id], remote_programmers[0]['id']
+ assert_equal @david[:name], remote_programmers[0]['name']
+ end
+ end
+ end
+
+ def test_formats_on_custom_element_method
+ for format in [ :json, :xml ]
+ using_format(Person, format) do
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/2.#{format}", {}, ActiveResource::Formats[format].encode(@david)
+ mock.get "/people/2/shallow.#{format}", {}, ActiveResource::Formats[format].encode(@david)
+ end
+ remote_programmer = Person.find(2).get(:shallow)
+ assert_equal @david[:id], remote_programmer['id']
+ assert_equal @david[:name], remote_programmer['name']
+ end
+ end
+
+ for format in [ :json, :xml ]
+ ryan = ActiveResource::Formats[format].encode({ :name => 'Ryan' })
+ using_format(Person, format) do
+ ActiveResource::HttpMock.respond_to.post "/people/new/register.#{format}", {}, ryan, 201, 'Location' => "/people/5.#{format}"
+ remote_ryan = Person.new(:name => 'Ryan')
+ assert_equal ActiveResource::Response.new(ryan, 201, {'Location' => "/people/5.#{format}"}), remote_ryan.post(:register)
+ end
+ end
+ end
+
+ def test_setting_format_before_site
+ resource = Class.new(ActiveResource::Base)
+ resource.format = :json
+ resource.site = 'http://37s.sunrise.i:3000'
+ assert_equal ActiveResource::Formats[:json], resource.connection.format
+ end
private
def using_format(klass, mime_type_reference)
View
6 activesupport/CHANGELOG
@@ -1,5 +1,11 @@
*SVN*
+* Fixed String#titleize to work for strings with 's too #10571 [trek]
+
+* Changed the implementation of Enumerable#group_by to use a double array approach instead of a hash such that the insert order is honored [DHH/Marcel]
+
+* remove multiple enumerations from ActiveSupport::JSON#convert_json_to_yaml when dealing with date/time values. [rick]
+
* Hash#symbolize_keys skips keys that can't be symbolized. #10500 [Brad Greenlee]
* Ruby 1.9 compatibility. #1689, #10466, #10468, #10554 [Cheah Chu Yeow, Pratik Naik, Jeremy Kemper, Dirkjan Bussink]
View
2 activesupport/Rakefile
@@ -1,7 +1,6 @@
require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/gempackagetask'
-require 'rake/contrib/rubyforgepublisher'
require File.join(File.dirname(__FILE__), 'lib', 'active_support', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
@@ -76,6 +75,7 @@ end
desc "Publish the release files to RubyForge."
task :release => [ :package ] do
require 'rubyforge'
+ require 'rake/contrib/rubyforgepublisher'
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
View
9 activesupport/lib/active_support/core_ext/enumerable.rb
@@ -15,8 +15,13 @@ module Enumerable
# "2006-02-24 -> Transcript, Transcript"
# "2006-02-23 -> Transcript"
def group_by
- inject({}) do |groups, element|
- (groups[yield(element)] ||= []) << element
+ inject([]) do |groups, element|
+ value = yield(element)
+ if (last_group = groups.last) && last_group.first == value
+ last_group.last << element
+ else
+ groups << [value, [element]]
+ end
groups
end
end if RUBY_VERSION < '1.9'
View
12 activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -13,10 +13,7 @@ def stringify_keys
# Destructively convert all keys to strings.
def stringify_keys!
keys.each do |key|
- unless key.class.to_s == "String" # weird hack to make the tests run when string_ext_test.rb is also running
- self[key.to_s] = self[key]
- delete(key)
- end
+ self[key.to_s] = delete(key)
end
self
end
@@ -38,11 +35,12 @@ def symbolize_keys!
alias_method :to_options!, :symbolize_keys!
# Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
- # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbol
+ # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
# as keys, this will fail.
- # examples:
+ #
+ # ==== Examples:
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
- # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): years, name"
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
def assert_valid_keys(*valid_keys)
unknown_keys = keys - [valid_keys].flatten
View
2 activesupport/lib/active_support/inflector.rb
@@ -162,7 +162,7 @@ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
# "man from the boondocks".titleize #=> "Man From The Boondocks"
# "x-men: the last stand".titleize #=> "X Men: The Last Stand"
def titleize(word)
- humanize(underscore(word)).gsub(/\b([a-z])/) { $1.capitalize }
+ humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
end
# The reverse of +camelize+. Makes an underscored form from the expression in the string.
View
13 activesupport/lib/active_support/json/decoding.rb
@@ -45,11 +45,14 @@ def convert_json_to_yaml(json) #:nodoc:
if marks.empty?
json.gsub(/\\\//, '/')
else
- # FIXME: multiple slow enumerations
- output = ([0] + marks.map(&:succ)).
- zip(marks + [json.length]).
- map { |left, right| json[left..right] }.
- join(" ")
+ left_pos = [-1].push(*marks)
+ right_pos = marks << json.length
+ output = []
+ left_pos.each_with_index do |left, i|
+ output << json[left.succ..right_pos[i]]
+ end
+ output = output * " "
+
times.each { |i| output[i-1] = ' ' }
output.gsub!(/\\\//, '/')
output
View
5 activesupport/test/inflector_test_cases.rb
@@ -155,7 +155,10 @@ module InflectorTestCases
'Action Web Service' => 'Action Web Service',
'Action web service' => 'Action Web Service',
'actionwebservice' => 'Actionwebservice',
- 'Actionwebservice' => 'Actionwebservice'
+ 'Actionwebservice' => 'Actionwebservice',
+ "david's code" => "David's Code",
+ "David's code" => "David's Code",
+ "david's Code" => "David's Code"
}
OrdinalNumbers = {
View
2 railties/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Added that rails:update is run when you do rails:freeze:edge to ensure you also get the latest JS and config files #10565 [jeff]
+
* SQLite: db:drop:all doesn't fail silently if the database is already open. #10577 [Cheah Chu Yeow, mrichman]
* Ruby 1.9 compatibility. #1689, #10546 [Cheah Chu Yeow, frederico]
View
38 railties/lib/rails_generator/commands.rb
@@ -310,26 +310,26 @@ def directory(relative_path)
logger.exists relative_path
else
logger.create relative_path
- unless options[:pretend]
- FileUtils.mkdir_p(path)
+ unless options[:pretend]
+ FileUtils.mkdir_p(path)
- # Subversion doesn't do path adds, so we need to add
- # each directory individually.
- # So stack up the directory tree and add the paths to
- # subversion in order without recursion.
- if options[:svn]
- stack=[relative_path]
- until File.dirname(stack.last) == stack.last # dirname('.') == '.'
- stack.push File.dirname(stack.last)
- end
- stack.reverse_each do |rel_path|
- svn_path = destination_path(rel_path)
- system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn'))
- end
- end
- end
- end
- end
+ # Subversion doesn't do path adds, so we need to add
+ # each directory individually.
+ # So stack up the directory tree and add the paths to
+ # subversion in order without recursion.
+ if options[:svn]
+ stack=[relative_path]
+ until File.dirname(stack.last) == stack.last # dirname('.') == '.'
+ stack.push File.dirname(stack.last)
+ end
+ stack.reverse_each do |rel_path|
+ svn_path = destination_path(rel_path)
+ system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn'))
+ end
+ end
+ end
+ end
+ end
# Display a README.
def readme(*relative_sources)
View
4 railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
@@ -54,8 +54,6 @@ def manifest
m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb"))
m.template('style.css', 'public/stylesheets/scaffold.css')
- m.dependency 'model', [name] + @args, :collision => :skip
-
m.template(
'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
)
@@ -64,6 +62,8 @@ def manifest
m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
m.route_resources controller_file_name
+
+ m.dependency 'model', [name] + @args, :collision => :skip
end
end
View
27 railties/lib/tasks/databases.rake
@@ -16,11 +16,7 @@ namespace :db do
# <<: *defaults
next unless config['database']
# Only connect to local databases
- if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank?
- create_database(config)
- else
- p "This task only creates local databases. #{config['database']} is on a remote host."
- end
+ local_database?(config) { create_database(config) }
end
end
end
@@ -65,11 +61,7 @@ namespace :db do
# Skip entries that don't have a database key
next unless config['database']
# Only connect to local databases
- if config['host'] == 'localhost' || config['host'].blank?
- drop_database(config)
- else
- p "This task only drops local databases. #{config['database']} is on a remote host."
- end
+ local_database?(config) { drop_database(config) }
end
end
end
@@ -79,6 +71,15 @@ namespace :db do
drop_database(ActiveRecord::Base.configurations[RAILS_ENV || 'development'])
end
+ def local_database?(config, &block)
+ if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank?
+ yield
+ else
+ puts "This task only modifies local databases. #{config['database']} is on a remote host."
+ end
+ end
+
+
desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x. Turn off output with VERBOSE=false."
task :migrate => :environment do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
@@ -331,9 +332,7 @@ namespace :db do
desc "Clear the sessions table"
task :clear => :environment do
- session_table = 'session'
- session_table = Inflector.pluralize(session_table) if ActiveRecord::Base.pluralize_table_names
- ActiveRecord::Base.connection.execute "DELETE FROM #{session_table}"
+ ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}"
end
end
end
@@ -360,4 +359,4 @@ end
def firebird_db_string(config)
FireRuby::Database.db_string_for(config.symbolize_keys)
-end
+end
View
3 railties/lib/tasks/framework.rake
@@ -69,6 +69,9 @@ namespace :rails do
for framework in %w(railties actionpack activerecord actionmailer activesupport activeresource)
system "svn export #{rails_svn}/#{framework} vendor/rails/#{framework}" + (ENV['REVISION'] ? " -r #{ENV['REVISION']}" : "")
end
+
+ puts "Updating current scripts, javascripts, and configuration settings"
+ Rake::Task["rails:update"].invoke
end
end

0 comments on commit 083b0b7

Please sign in to comment.