diff options
119 files changed, 1992 insertions, 1289 deletions
@@ -10,7 +10,8 @@ end gem "coffee-script" gem "sass" -gem "uglifier", :git => 'git://github.com/lautis/uglifier.git' +gem "uglifier", :git => "git://github.com/lautis/uglifier.git" +gem "rack", :git => "git://github.com/rack/rack.git" gem "rake", ">= 0.8.7" gem "mocha", ">= 0.9.8" diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc index af9bf40f9e..2806531dfa 100644 --- a/actionmailer/README.rdoc +++ b/actionmailer/README.rdoc @@ -102,7 +102,7 @@ Example: ) if email.has_attachments? - for attachment in email.attachments + email.attachments.each do |attachment| page.attachments.create({ :file => attachment, :description => email.subject }) diff --git a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb index 88b074cef5..aaa1f79732 100644 --- a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb +++ b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb @@ -1,7 +1,7 @@ <% module_namespacing do -%> class <%= class_name %> < ActionMailer::Base default <%= key_value :from, '"from@example.com"' %> -<% for action in actions -%> +<% actions.each do |action| -%> # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc index 5db4cff66b..5919b5c6d4 100644 --- a/actionpack/README.rdoc +++ b/actionpack/README.rdoc @@ -33,7 +33,7 @@ A short rundown of some of the major features: * Actions grouped in controller as methods instead of separate command objects and can therefore share helper methods - CustomersController < ActionController::Base + class CustomersController < ActionController::Base def show @customer = find_customer end @@ -58,7 +58,7 @@ A short rundown of some of the major features: * ERB templates (static content mixed with dynamic output from ruby) - <% for post in @posts %> + <% @posts.each do |post| %> Title: <%= post.title %> <% end %> @@ -81,7 +81,7 @@ A short rundown of some of the major features: xml.language "en-us" xml.ttl "40" - for item in @recent_items + @recent_items.each do |item| xml.item do xml.title(item_title(item)) xml.description(item_description(item)) @@ -293,7 +293,7 @@ And the templates look like this: </body></html> weblog/index.html.erb: - <% for post in @posts %> + <% @posts.each do |post| %> <p><%= link_to(post.title, :action => "show", :id => post.id) %></p> <% end %> diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 606abac83a..0b4e7027ed 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -24,8 +24,8 @@ Gem::Specification.new do |s| s.add_dependency('i18n', '~> 0.6.0beta1') s.add_dependency('rack', '~> 1.3.0.beta') s.add_dependency('rack-test', '~> 0.6.0') - s.add_dependency('rack-mount', '~> 0.8.0') - s.add_dependency('sprockets', '~> 2.0.0.beta.2') + s.add_dependency('rack-mount', '~> 0.8.1') + s.add_dependency('sprockets', '~> 2.0.0.beta.3') s.add_dependency('tzinfo', '~> 0.3.27') s.add_dependency('erubis', '~> 2.7.0') end diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index aa7c1e09c2..5500f88930 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/module/anonymous' require 'action_dispatch/http/mime_types' module ActionController @@ -36,11 +37,11 @@ module ActionController # {"name" => "Konata", "user" => {"name" => "Konata"}} # # You can also specify the key in which the parameters should be wrapped to, - # and also the list of attributes it should wrap by using either +:only+ or - # +:except+ options like this: + # and also the list of attributes it should wrap by using either +:include+ or + # +:exclude+ options like this: # # class UsersController < ApplicationController - # wrap_parameters :person, :only => [:username, :password] + # wrap_parameters :person, :include => [:username, :password] # end # # If you're going to pass the parameters to an +ActiveModel+ object (such as @@ -52,7 +53,7 @@ module ActionController # wrap_parameters Person # end # - # You still could pass +:only+ and +:except+ to set the list of attributes + # You still could pass +:include+ and +:exclude+ to set the list of attributes # you want to wrap. # # By default, if you don't specify the key in which the parameters would be @@ -72,7 +73,7 @@ module ActionController included do class_attribute :_wrapper_options - self._wrapper_options = {:format => []} + self._wrapper_options = { :format => [] } end module ClassMethods @@ -90,7 +91,7 @@ module ActionController # # wraps parameters by determine the wrapper key from Person class # (+person+, in this case) and the list of attribute names # - # wrap_parameters :only => [:username, :title] + # wrap_parameters :include => [:username, :title] # # wraps only +:username+ and +:title+ attributes from parameters. # # wrap_parameters false @@ -99,9 +100,9 @@ module ActionController # ==== Options # * <tt>:format</tt> - The list of formats in which the parameters wrapper # will be enabled. - # * <tt>:only</tt> - The list of attribute names which parameters wrapper + # * <tt>:include</tt> - The list of attribute names which parameters wrapper # will wrap into a nested hash. - # * <tt>:except</tt> - The list of attribute names which parameters wrapper + # * <tt>:exclude</tt> - The list of attribute names which parameters wrapper # will exclude from a nested hash. def wrap_parameters(name_or_model_or_options, options = {}) model = nil @@ -140,6 +141,8 @@ module ActionController # This method also does namespace lookup. Foo::Bar::UsersController will # try to find Foo::Bar::User, Foo::User and finally User. def _default_wrap_model #:nodoc: + return nil if self.anonymous? + model_name = self.name.sub(/Controller$/, '').singularize begin @@ -161,22 +164,22 @@ module ActionController def _set_wrapper_defaults(options, model=nil) options = options.dup - unless options[:only] || options[:except] + unless options[:include] || options[:exclude] model ||= _default_wrap_model if model.respond_to?(:attribute_names) && model.attribute_names.present? - options[:only] = model.attribute_names + options[:include] = model.attribute_names end end - unless options[:name] + unless options[:name] || self.anonymous? model ||= _default_wrap_model options[:name] = model ? model.to_s.demodulize.underscore : controller_name.singularize end - options[:only] = Array.wrap(options[:only]).collect(&:to_s) if options[:only] - options[:except] = Array.wrap(options[:except]).collect(&:to_s) if options[:except] - options[:format] = Array.wrap(options[:format]) + options[:include] = Array.wrap(options[:include]).collect(&:to_s) if options[:include] + options[:exclude] = Array.wrap(options[:exclude]).collect(&:to_s) if options[:exclude] + options[:format] = Array.wrap(options[:format]) self._wrapper_options = options end @@ -213,11 +216,11 @@ module ActionController # Returns the list of parameters which will be selected for wrapped. def _wrap_parameters(parameters) - value = if only = _wrapper_options[:only] - parameters.slice(*only) + value = if include_only = _wrapper_options[:include] + parameters.slice(*include_only) else - except = _wrapper_options[:except] || [] - parameters.except(*(except + EXCLUDE_PARAMETERS)) + exclude = _wrapper_options[:exclude] || [] + parameters.except(*(exclude + EXCLUDE_PARAMETERS)) end { _wrapper_key => value } @@ -226,7 +229,7 @@ module ActionController # Checks if we should perform parameters wrapping. def _wrapper_enabled? ref = request.content_mime_type.try(:ref) - _wrapper_formats.include?(ref) && !request.request_parameters[_wrapper_key] + _wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key] end end end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 0085f542aa..89ff5ba174 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -2,6 +2,7 @@ require 'rack/session/abstract/id' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/class/attribute' +require 'active_support/core_ext/module/anonymous' module ActionController module TemplateAssertions @@ -413,7 +414,11 @@ module ActionController @request.env['REQUEST_METHOD'] = http_method parameters ||= {} - @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters) + controller_class_name = @controller.class.anonymous? ? + "anonymous_controller" : + @controller.class.name.underscore.sub(/_controller$/, '') + + @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters) @request.session = ActionController::TestSession.new(session) if session @request.session["flash"] = @request.flash.update(flash || {}) diff --git a/actionpack/lib/action_dispatch/http/rack_cache.rb b/actionpack/lib/action_dispatch/http/rack_cache.rb index b5c1435903..cc8edee300 100644 --- a/actionpack/lib/action_dispatch/http/rack_cache.rb +++ b/actionpack/lib/action_dispatch/http/rack_cache.rb @@ -14,11 +14,15 @@ module ActionDispatch end def read(key) - @store.read(key) || [] + if data = @store.read(key) + Marshal.load(data) + else + [] + end end def write(key, value) - @store.write(key, value) + @store.write(key, Marshal.dump(value)) end ::Rack::Cache::MetaStore::RAILS = self diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 24ebb8fed7..0057f64dd3 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -167,8 +167,8 @@ module ActionDispatch handle_options(options) - @set_cookies[key] = options - @delete_cookies.delete(key) + @set_cookies[key.to_s] = options + @delete_cookies.delete(key.to_s) value end @@ -181,7 +181,7 @@ module ActionDispatch handle_options(options) value = @cookies.delete(key.to_s) - @delete_cookies[key] = options + @delete_cookies[key.to_s] = options value end diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb index e7aeb45fb3..13fe693c32 100644 --- a/actionpack/lib/action_dispatch/testing/performance_test.rb +++ b/actionpack/lib/action_dispatch/testing/performance_test.rb @@ -1,17 +1,10 @@ require 'active_support/testing/performance' -begin - module ActionDispatch - # An integration test that runs a code profiler on your test methods. - # Profiling output for combinations of each test method, measurement, and - # output format are written to your tmp/performance directory. - # - # By default, process_time is measured and both flat and graph_html output - # formats are written, so you'll have two output files per test method. - class PerformanceTest < ActionDispatch::IntegrationTest - include ActiveSupport::Testing::Performance - end +module ActionDispatch + # An integration test that runs a code profiler on your test methods. + # Profiling output for combinations of each test method, measurement, and + # output format are written to your tmp/performance directory. + class PerformanceTest < ActionDispatch::IntegrationTest + include ActiveSupport::Testing::Performance end -rescue NameError - $stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks." end diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index 889ea8a763..a087688a2c 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -34,7 +34,7 @@ module ActionView # feed.title("My great blog!") # feed.updated(@posts.first.created_at) # - # for post in @posts + # @posts.each do |post| # feed.entry(post) do |entry| # entry.title(post.title) # entry.content(post.body, :type => 'html') @@ -66,7 +66,7 @@ module ActionView # feed.updated((@posts.first.created_at)) # feed.tag!(openSearch:totalResults, 10) # - # for post in @posts + # @posts.each do |post| # feed.entry(post) do |entry| # entry.title(post.title) # entry.content(post.body, :type => 'html') diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index c75b7d4de0..8cee3babe2 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -34,15 +34,7 @@ module Sprockets app.assets = asset_environment(app) ActiveSupport.on_load(:action_view) do - if app.assets.respond_to?(:context_class) - context = app.assets.context_class - - # TODO: Remove this condition when Sprockets 2.0.beta.3 is released - else - context = app.assets.context - end - - context.instance_eval do + app.assets.context_class.instance_eval do include ::ActionView::Helpers::SprocketsHelper end end @@ -99,7 +91,7 @@ module Sprockets compressor when :yui require 'yui/compressor' - YUI::JavaScriptCompressor.new(:munge => true) + YUI::CssCompressor.new else sym end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 5d7a51e902..80c4fa2ee5 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -4,7 +4,7 @@ require "action_controller/log_subscriber" module Another class LogSubscribersController < ActionController::Base - wrap_parameters :person, :only => :name, :format => :json + wrap_parameters :person, :include => :name, :format => :json def show render :nothing => true @@ -34,11 +34,11 @@ module Another cache_page("Super soaker", "/index.html") render :nothing => true end - + def with_exception raise Exception end - + end end diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index ae4ad8eb9c..7bef1e8d5d 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -2,7 +2,21 @@ require 'abstract_unit' module Admin; class User; end; end +module ParamsWrapperTestHelp + def with_default_wrapper_options(&block) + @controller.class._wrapper_options = {:format => [:json]} + @controller.class.inherited(@controller.class) + yield + end + + def assert_parameters(expected) + assert_equal expected, self.class.controller_class.last_parameters + end +end + class ParamsWrapperTest < ActionController::TestCase + include ParamsWrapperTestHelp + class UsersController < ActionController::Base class << self attr_accessor :last_parameters @@ -51,9 +65,9 @@ class ParamsWrapperTest < ActionController::TestCase end end - def test_specify_only_option + def test_specify_include_option with_default_wrapper_options do - UsersController.wrap_parameters :only => :username + UsersController.wrap_parameters :include => :username @request.env['CONTENT_TYPE'] = 'application/json' post :parse, { 'username' => 'sikachu', 'title' => 'Developer' } @@ -61,9 +75,9 @@ class ParamsWrapperTest < ActionController::TestCase end end - def test_specify_except_option + def test_specify_exclude_option with_default_wrapper_options do - UsersController.wrap_parameters :except => :title + UsersController.wrap_parameters :exclude => :title @request.env['CONTENT_TYPE'] = 'application/json' post :parse, { 'username' => 'sikachu', 'title' => 'Developer' } @@ -71,9 +85,9 @@ class ParamsWrapperTest < ActionController::TestCase end end - def test_specify_both_wrapper_name_and_only_option + def test_specify_both_wrapper_name_and_include_option with_default_wrapper_options do - UsersController.wrap_parameters :person, :only => :username + UsersController.wrap_parameters :person, :include => :username @request.env['CONTENT_TYPE'] = 'application/json' post :parse, { 'username' => 'sikachu', 'title' => 'Developer' } @@ -166,20 +180,11 @@ class ParamsWrapperTest < ActionController::TestCase assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu', 'title' => 'Developer' }}) end end - - private - def with_default_wrapper_options(&block) - @controller.class._wrapper_options = {:format => [:json]} - @controller.class.inherited(@controller.class) - yield - end - - def assert_parameters(expected) - assert_equal expected, UsersController.last_parameters - end end class NamespacedParamsWrapperTest < ActionController::TestCase + include ParamsWrapperTestHelp + module Admin module Users class UsersController < ActionController::Base; @@ -247,14 +252,36 @@ class NamespacedParamsWrapperTest < ActionController::TestCase end end - private - def with_default_wrapper_options(&block) - @controller.class._wrapper_options = {:format => [:json]} - @controller.class.inherited(@controller.class) - yield +end + +class AnonymousControllerParamsWrapperTest < ActionController::TestCase + include ParamsWrapperTestHelp + + tests(Class.new(ActionController::Base) do + class << self + attr_accessor :last_parameters end - def assert_parameters(expected) - assert_equal expected, Admin::Users::UsersController.last_parameters + def parse + self.class.last_parameters = request.params.except(:controller, :action) + head :ok + end + end) + + def test_does_not_implicitly_wrap_params + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, { 'username' => 'sikachu' } + assert_parameters({ 'username' => 'sikachu' }) end + end + + def test_does_wrap_params_if_name_provided + with_default_wrapper_options do + @controller.class.wrap_parameters(:name => "guest") + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, { 'username' => 'sikachu' } + assert_parameters({ 'username' => 'sikachu', 'guest' => { 'username' => 'sikachu' }}) + end + end end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index ebc16694db..e42c39f527 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -121,7 +121,7 @@ class CookiesTest < ActionController::TestCase end def string_key - cookies['user_name'] = "david" + cookies['user_name'] = "dhh" head :ok end @@ -417,14 +417,18 @@ class CookiesTest < ActionController::TestCase assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT" end + def test_cookies_hash_is_indifferent_access - [:symbol_key, :string_key].each do |cookie_key| - get cookie_key + get :symbol_key assert_equal "david", cookies[:user_name] assert_equal "david", cookies['user_name'] - end + get :string_key + assert_equal "dhh", cookies[:user_name] + assert_equal "dhh", cookies['user_name'] end + + def test_setting_request_cookies_is_indifferent_access @request.cookies.clear @request.cookies[:user_name] = "andrew" diff --git a/actionpack/test/dispatch/rack_cache_test.rb b/actionpack/test/dispatch/rack_cache_test.rb new file mode 100644 index 0000000000..79d8a64d29 --- /dev/null +++ b/actionpack/test/dispatch/rack_cache_test.rb @@ -0,0 +1,21 @@ +require 'abstract_unit' +require 'action_dispatch/http/rack_cache' + +class RackCacheMetaStoreTest < ActiveSupport::TestCase + class ReadWriteHash < ::Hash + alias :read :[] + alias :write :[]= + end + + setup do + @store = ActionDispatch::RailsMetaStore.new(ReadWriteHash.new) + end + + test "stuff is deep duped" do + @store.write(:foo, { :bar => :original }) + hash = @store.read(:foo) + hash[:bar] = :changed + hash = @store.read(:foo) + assert_equal :original, hash[:bar] + end +end diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 36102bbc4f..81d7444cf8 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -16,7 +16,7 @@ class ScrollsController < ActionController::Base feed.title("My great blog!") feed.updated((@scrolls.first.created_at)) - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll) do |entry| entry.title(scroll.title) entry.content(scroll.body, :type => 'html') @@ -33,7 +33,7 @@ class ScrollsController < ActionController::Base feed.title("My great blog!") feed.updated((@scrolls.first.created_at)) - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param.to_s, :updated => Time.utc(2007, 1, scroll.id)) do |entry| entry.title(scroll.title) entry.content(scroll.body, :type => 'html') @@ -54,7 +54,7 @@ class ScrollsController < ActionController::Base author.name("DHH") end - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param.to_s, :updated => Time.utc(2007, 1, scroll.id)) do |entry| entry.title(scroll.title) entry.content(scroll.body, :type => 'html') @@ -68,7 +68,7 @@ class ScrollsController < ActionController::Base feed.title("My great blog!") feed.updated((@scrolls.first.created_at)) - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll) do |entry| entry.title(scroll.title) entry.content(scroll.body, :type => 'html') @@ -86,7 +86,7 @@ class ScrollsController < ActionController::Base feed.title("My great blog!") feed.updated((@scrolls.first.created_at)) - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll, :id => "tag:test.rubyonrails.org,2008:"+scroll.id.to_s) do |entry| entry.title(scroll.title) entry.content(scroll.body, :type => 'html') @@ -105,7 +105,7 @@ class ScrollsController < ActionController::Base feed.title("My great blog!") feed.updated((@scrolls.first.created_at)) - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll) do |entry| entry.title(scroll.title) entry.content(scroll.body, :type => 'html') @@ -123,7 +123,7 @@ class ScrollsController < ActionController::Base feed.title("My great blog!") feed.updated((@scrolls.first.created_at)) - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll) do |entry| entry.title(scroll.title) entry.content(scroll.body, :type => 'html') @@ -140,7 +140,7 @@ class ScrollsController < ActionController::Base feed.title("My great blog!") feed.updated((@scrolls.first.created_at)) - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll) do |entry| entry.title(scroll.title) entry.summary(:type => 'xhtml') do |xhtml| @@ -165,7 +165,7 @@ class ScrollsController < ActionController::Base feed.title("My great blog!") feed.updated((@scrolls.first.created_at)) - for scroll in @scrolls + @scrolls.each do |scroll| feed.entry(scroll) do |entry| entry.title(scroll.title) entry.content(scroll.body, :type => 'html') diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 4682ae07ef..b94ad4bb9b 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -71,9 +71,7 @@ module ActiveModel # Notify list of observers of a change. def notify_observers(*arg) - for observer in observer_instances - observer.update(*arg) - end + observer_instances.each { |observer| observer.update(*arg) } end # Total number of observers. diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index 19639b1363..eb3975f86b 100644 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -25,7 +25,7 @@ module ActiveModel def decorations decorations = {} decorations[:encoding] = 'base64' if type == :binary - decorations[:type] = type unless type == :string + decorations[:type] = (type == :string) ? nil : type decorations[:nil] = true if value.nil? decorations end diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb index 8f5c196850..f978191d22 100644 --- a/activemodel/test/cases/serializers/xml_serialization_test.rb +++ b/activemodel/test/cases/serializers/xml_serialization_test.rb @@ -92,7 +92,7 @@ class XmlSerializationTest < ActiveModel::TestCase test "should serialize string" do assert_match %r{<name>aaron stack</name>}, @contact.to_xml end - + test "should serialize nil" do assert_match %r{<pseudonyms nil=\"true\"></pseudonyms>}, @contact.to_xml(:methods => :pseudonyms) end @@ -132,4 +132,10 @@ class XmlSerializationTest < ActiveModel::TestCase xml = @contact.to_xml(:procs => [ proc ]) assert_match %r{<name-reverse>kcats noraa</name-reverse>}, xml end + + test "should serialize string correctly when type passed" do + xml = @contact.to_xml :type => 'Contact' + assert_match %r{<contact type="Contact">}, xml + assert_match %r{<name>aaron stack</name>}, xml + end end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 2e144745cd..502a7e43de 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,15 @@ *Rails 3.1.0 (unreleased)* +* Add block setting of attributes to singular associations: + + class User < ActiveRecord::Base + has_one :account + end + + user.build_account{ |a| a.credit_limit => 100.0 } + + The block is called after the instance has been initialized. [Andrew White] + * Add ActiveRecord::Base.attribute_names to return a list of attribute names. This will return an empty array if the model is abstract or table does not exists. [Prem Sichanugrist] * CSV Fixtures are deprecated and support will be removed in Rails 3.2.0 diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index 3a89446a83..6b4c85bb93 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -84,7 +84,7 @@ A short rundown of some of the major features: class CommentObserver < ActiveRecord::Observer def after_create(comment) # is called just after Comment#save - CommentMailer.new_comment_email("david@loudthinking.com", comment) + CommentMailer.new_comment_email("david@loudthinking.com", comment).deliver end end diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 3a5035305b..335127f38e 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -22,6 +22,6 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) - s.add_dependency('arel', '~> 2.1.0') + s.add_dependency('arel', '~> 2.1.1') s.add_dependency('tzinfo', '~> 0.3.27') end diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb index 62d48d3a2c..638a2ec72a 100644 --- a/activerecord/lib/active_record/associations/builder/singular_association.rb +++ b/activerecord/lib/active_record/associations/builder/singular_association.rb @@ -29,16 +29,16 @@ module ActiveRecord::Associations::Builder def define_constructors name = self.name - model.redefine_method("build_#{name}") do |*params| - association(name).build(*params) + model.redefine_method("build_#{name}") do |*params, &block| + association(name).build(*params, &block) end - model.redefine_method("create_#{name}") do |*params| - association(name).create(*params) + model.redefine_method("create_#{name}") do |*params, &block| + association(name).create(*params, &block) end - model.redefine_method("create_#{name}!") do |*params| - association(name).create!(*params) + model.redefine_method("create_#{name}!") do |*params, &block| + association(name).create!(*params, &block) end end end diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 525ac65722..902ad8cb64 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -423,9 +423,13 @@ module ActiveRecord raise NotImplementedError end + def create_scope + scoped.scope_for_create.stringify_keys + end + def build_record(attributes, options) - record = reflection.build_association - record.assign_attributes(scoped.scope_for_create, :without_protection => true) + record = reflection.build_association(attributes, options) + record.assign_attributes(create_scope.except(*record.changed), :without_protection => true) record.assign_attributes(attributes, options) record end diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb index 877ddf3ee1..ce1f2a5543 100644 --- a/activerecord/lib/active_record/associations/singular_association.rb +++ b/activerecord/lib/active_record/associations/singular_association.rb @@ -17,27 +17,28 @@ module ActiveRecord replace(record) end - def create(attributes = {}, options = {}) - build(attributes, options).tap { |record| record.save } + def create(attributes = {}, options = {}, &block) + build(attributes, options, &block).tap { |record| record.save } end - def create!(attributes = {}, options = {}) - build(attributes, options).tap { |record| record.save! } + def create!(attributes = {}, options = {}, &block) + build(attributes, options, &block).tap { |record| record.save! } end def build(attributes = {}, options = {}) - record = reflection.build_association - record.assign_attributes( - scoped.scope_for_create.except(klass.primary_key), - :without_protection => true - ) - record.assign_attributes(attributes, options) + record = reflection.build_association(attributes, options) + record.assign_attributes(create_scope.except(*record.changed), :without_protection => true) + yield(record) if block_given? set_new_record(record) record end private + def create_scope + scoped.scope_for_create.stringify_keys.except(klass.primary_key) + end + def find_target scoped.first.tap { |record| set_inverse_instance(record) } end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index f4beeceb61..0a460bc086 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -701,11 +701,11 @@ module ActiveRecord schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') result = query(<<-SQL, name) SELECT distinct i.relname, d.indisunique, d.indkey, t.oid - FROM pg_class t, pg_class i, pg_index d + FROM pg_class t + INNER JOIN pg_index d ON t.oid = d.indrelid + INNER JOIN pg_class i ON d.indexrelid = i.oid WHERE i.relkind = 'i' - AND d.indexrelid = i.oid AND d.indisprimary = 'f' - AND t.oid = d.indrelid AND t.relname = '#{table_name}' AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) ) ORDER BY i.relname @@ -820,19 +820,13 @@ module ActiveRecord # given table's primary key. result = exec_query(<<-end_sql, 'SCHEMA').rows.first SELECT attr.attname, seq.relname - FROM pg_class seq, - pg_attribute attr, - pg_depend dep, - pg_namespace name, - pg_constraint cons - WHERE seq.oid = dep.objid - AND seq.relkind = 'S' - AND attr.attrelid = dep.refobjid - AND attr.attnum = dep.refobjsubid - AND attr.attrelid = cons.conrelid - AND attr.attnum = cons.conkey[1] - AND cons.contype = 'p' - AND dep.refobjid = '#{quote_table_name(table)}'::regclass + FROM pg_class seq + INNER JOIN pg_depend dep ON seq.oid = dep.objid + INNER JOIN pg_attribute attr ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid + INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] + WHERE seq.relkind = 'S' + AND cons.contype = 'p' + AND dep.refobjid = '#{quote_table_name(table)}'::regclass end_sql # [primary_key, sequence] @@ -845,16 +839,11 @@ module ActiveRecord def primary_key(table) row = exec_query(<<-end_sql, 'SCHEMA', [[nil, table]]).rows.first SELECT DISTINCT(attr.attname) - FROM pg_attribute attr, - pg_depend dep, - pg_namespace name, - pg_constraint cons - WHERE attr.attrelid = dep.refobjid - AND attr.attnum = dep.refobjsubid - AND attr.attrelid = cons.conrelid - AND attr.attnum = cons.conkey[1] - AND cons.contype = 'p' - AND dep.refobjid = $1::regclass + FROM pg_attribute attr + INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid + INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] + WHERE cons.contype = 'p' + AND dep.refobjid = $1::regclass end_sql row && row.first diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index cdedcde0eb..3afa257a76 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -3,16 +3,17 @@ module ActiveRecord # == What is Optimistic Locking # # Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of - # conflicts with the data. It does this by checking whether another process has made changes to a record since - # it was opened, an ActiveRecord::StaleObjectError is thrown if that has occurred and the update is ignored. + # conflicts with the data. It does this by checking whether another process has made changes to a record since + # it was opened, an <tt>ActiveRecord::StaleObjectError</tt> exception is thrown if that has occurred + # and the update is ignored. # - # Check out ActiveRecord::Locking::Pessimistic for an alternative. + # Check out <tt>ActiveRecord::Locking::Pessimistic</tt> for an alternative. # # == Usage # - # Active Records support optimistic locking if the field <tt>lock_version</tt> is present. Each update to the - # record increments the lock_version column and the locking facilities ensure that records instantiated twice - # will let the last one saved raise a StaleObjectError if the first was also updated. Example: + # Active Records support optimistic locking if the field +lock_version+ is present. Each update to the + # record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice + # will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example: # # p1 = Person.find(1) # p2 = Person.find(1) @@ -36,10 +37,10 @@ module ActiveRecord # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, # or otherwise apply the business logic needed to resolve the conflict. # - # You must ensure that your database schema defaults the lock_version column to 0. + # You must ensure that your database schema defaults the +lock_version+ column to 0. # # This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>. - # To override the name of the lock_version column, invoke the <tt>set_locking_column</tt> method. + # To override the name of the +lock_version+ column, invoke the <tt>set_locking_column</tt> method. # This method uses the same syntax as <tt>set_table_name</tt> module Optimistic extend ActiveSupport::Concern @@ -68,9 +69,9 @@ module ActiveRecord result = super # If the locking column has no default value set, - # start the lock version at zero. Note we can't use - # locking_enabled? at this point as @attributes may - # not have been initialized yet + # start the lock version at zero. Note we can't use + # <tt>locking_enabled?</tt> at this point as + # <tt>@attributes</tt> may not have been initialized yet. if lock_optimistically && result.include?(self.class.locking_column) result[self.class.locking_column] ||= 0 @@ -137,10 +138,9 @@ module ActiveRecord module ClassMethods DEFAULT_LOCKING_COLUMN = 'lock_version' - # Is optimistic locking enabled for this table? Returns true if the - # +lock_optimistically+ flag is set to true (which it is, by default) - # and the table includes the +locking_column+ column (defaults to - # +lock_version+). + # Returns true if the +lock_optimistically+ flag is set to true + # (which it is, by default) and the table includes the + # +locking_column+ column (defaults to +lock_version+). def locking_enabled? lock_optimistically && columns_hash[locking_column] end diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb index 862cf8f72a..4c4c1bf5a1 100644 --- a/activerecord/lib/active_record/locking/pessimistic.rb +++ b/activerecord/lib/active_record/locking/pessimistic.rb @@ -3,7 +3,7 @@ module ActiveRecord # Locking::Pessimistic provides support for row-level locking using # SELECT ... FOR UPDATE and other lock types. # - # Pass <tt>:lock => true</tt> to ActiveRecord::Base.find to obtain an exclusive + # Pass <tt>:lock => true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive # lock on the selected rows: # # select * from accounts where id=1 for update # Account.find(1, :lock => true) @@ -21,7 +21,7 @@ module ActiveRecord # yuko.save! # end # - # You can also use ActiveRecord::Base#lock! method to lock one record by id. + # You can also use <tt>ActiveRecord::Base#lock!</tt> method to lock one record by id. # This may be better if you don't need to lock every row. Example: # # Account.transaction do diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index 9e3b3429e4..fb3fd34665 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/module/attr_internal' +require 'active_record/log_subscriber' module ActiveRecord module Railties diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index ddcc36c841..b993bf6e90 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -626,4 +626,25 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal "Bob", firm.name end + + def test_build_with_block + client = Client.create(:name => 'Client Company') + + firm = client.build_firm{ |f| f.name = 'Agency Company' } + assert_equal 'Agency Company', firm.name + end + + def test_create_with_block + client = Client.create(:name => 'Client Company') + + firm = client.create_firm{ |f| f.name = 'Agency Company' } + assert_equal 'Agency Company', firm.name + end + + def test_create_bang_with_block + client = Client.create(:name => 'Client Company') + + firm = client.create_firm!{ |f| f.name = 'Agency Company' } + assert_equal 'Agency Company', firm.name + end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 0e33fa9c8e..522ac56d82 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1455,4 +1455,17 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal "RED!", car.bulbs.to_a.first.color end + + def test_new_is_called_with_attributes_and_options + car = Car.create(:name => 'honda') + + bulb = car.bulbs.build + assert_equal Bulb, bulb.class + + bulb = car.bulbs.build(:bulb_type => :custom) + assert_equal Bulb, bulb.class + + bulb = car.bulbs.build({ :bulb_type => :custom }, :as => :admin) + assert_equal CustomBulb, bulb.class + end end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 356a4a7a09..f3bf5baa95 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -410,4 +410,41 @@ class HasOneAssociationsTest < ActiveRecord::TestCase bulb = car.create_frickinawesome_bulb(:frickinawesome => false) assert_equal true, bulb.frickinawesome? end + + def test_new_is_called_with_attributes_and_options + car = Car.create(:name => 'honda') + + bulb = car.build_bulb + assert_equal Bulb, bulb.class + + bulb = car.build_bulb + assert_equal Bulb, bulb.class + + bulb = car.build_bulb(:bulb_type => :custom) + assert_equal Bulb, bulb.class + + bulb = car.build_bulb({ :bulb_type => :custom }, :as => :admin) + assert_equal CustomBulb, bulb.class + end + + def test_build_with_block + car = Car.create(:name => 'honda') + + bulb = car.build_bulb{ |b| b.color = 'Red' } + assert_equal 'RED!', bulb.color + end + + def test_create_with_block + car = Car.create(:name => 'honda') + + bulb = car.create_bulb{ |b| b.color = 'Red' } + assert_equal 'RED!', bulb.color + end + + def test_create_bang_with_block + car = Car.create(:name => 'honda') + + bulb = car.create_bulb!{ |b| b.color = 'Red' } + assert_equal 'RED!', bulb.color + end end diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index 55d9a328a7..e5e9ca6131 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -10,7 +10,7 @@ require 'models/tagging' require 'models/tag' class InnerJoinAssociationTest < ActiveRecord::TestCase - fixtures :authors, :posts, :comments, :categories, :categories_posts, :categorizations, + fixtures :authors, :essays, :posts, :comments, :categories, :categories_posts, :categorizations, :taggings, :tags def test_construct_finder_sql_applies_aliases_tables_on_association_conditions diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 086654f4ce..8e23ab78be 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -15,6 +15,7 @@ require 'models/book' require 'models/citation' require 'models/aircraft' require 'models/engine' +require 'models/car' class AssociationsJoinModelTest < ActiveRecord::TestCase self.use_transactional_fixtures = false unless supports_savepoints? diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb index 7d90963720..0dcc8d5970 100644 --- a/activerecord/test/models/bulb.rb +++ b/activerecord/test/models/bulb.rb @@ -15,4 +15,17 @@ class Bulb < ActiveRecord::Base self[:color] = color.upcase + "!" end + def self.new(attributes = {}, options = {}, &block) + bulb_type = (attributes || {}).delete(:bulb_type) + + if options && options[:as] == :admin && bulb_type.present? + bulb_class = "#{bulb_type.to_s.camelize}Bulb".constantize + bulb_class.new(attributes, options, &block) + else + super + end + end end + +class CustomBulb < Bulb +end
\ No newline at end of file diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index a4e79f3d77..25f9242b98 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,11 +1,15 @@ *Rails 3.1.0 (unreleased)* -* No changes +* The default format has been changed to JSON for all requests. If you want to continue to use XML you will need to set `self.format = :xml` in the class. eg. + +class User < ActiveResource::Base + self.format = :xml +end *Rails 3.0.7 (April 18, 2011)* -*No changes. +* No changes. *Rails 3.0.6 (April 5, 2011) @@ -95,14 +99,14 @@ * Ruby 1.9 compatibility. [Jeremy Kemper] -*2.0.2* (December 16th, 2007) +*2.0.2 (December 16th, 2007)* * Added more specific exceptions for 400, 401, and 403 (all descending from ClientError so existing rescues will work) #10326 [trek] * Correct empty response handling. #10445 [seangeo] -*2.0.1* (December 7th, 2007) +*2.0.1 (December 7th, 2007)* * Don't cache net/http object so that ActiveResource is more thread-safe. Closes #10142 [kou] diff --git a/activeresource/examples/simple.rb b/activeresource/examples/simple.rb deleted file mode 100644 index 6d2c6e3b1b..0000000000 --- a/activeresource/examples/simple.rb +++ /dev/null @@ -1,15 +0,0 @@ -$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" -require 'active_resource' -require 'active_support/core_ext/hash/conversions' - -ActiveSupport::XmlMini.backend = ENV['XMLMINI'] || 'REXML' -ActiveResource::HttpMock.respond_to do |mock| - mock.get '/people/1.xml', {}, { :id => 1, :name => 'bob' }.to_xml(:root => 'person') -end - -class Person < ActiveResource::Base - self.site = 'http://localhost/' -end - -bob = Person.find(1) -puts bob.inspect diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 7f2a844723..65d285249b 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -81,19 +81,19 @@ module ActiveResource # <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. + # # POST to the custom 'register' REST method, i.e. POST /people/new/register.json. # Person.new(:name => 'Ryan').post(:register) # # => { :id => 1, :name => 'Ryan', :position => 'Clerk' } # - # # PUT an update by invoking the 'promote' REST method, i.e. PUT /people/1/promote.xml?position=Manager. + # # PUT an update by invoking the 'promote' REST method, i.e. PUT /people/1/promote.json?position=Manager. # Person.find(1).put(:promote, :position => 'Manager') # # => { :id => 1, :name => 'Ryan', :position => 'Manager' } # - # # GET all the positions available, i.e. GET /people/positions.xml. + # # GET all the positions available, i.e. GET /people/positions.json. # Person.get(:positions) # # => [{:name => 'Manager'}, {:name => 'Clerk'}] # - # # DELETE to 'fire' a person, i.e. DELETE /people/1/fire.xml. + # # DELETE to 'fire' a person, i.e. DELETE /people/1/fire.json. # Person.find(1).delete(:fire) # # For more information on using custom REST methods, see the @@ -164,7 +164,7 @@ module ActiveResource # response code will be returned from the server which will raise an ActiveResource::ResourceNotFound # exception. # - # # GET http://api.people.com:3000/people/999.xml + # # GET http://api.people.com:3000/people/999.json # ryan = Person.find(999) # 404, raises ActiveResource::ResourceNotFound # # @@ -218,7 +218,7 @@ module ActiveResource # ryan.save # => false # # # When - # # PUT http://api.people.com:3000/people/1.xml + # # PUT http://api.people.com:3000/people/1.json # # or # # PUT http://api.people.com:3000/people/1.json # # is requested with invalid values, the response is: @@ -489,7 +489,7 @@ module ActiveResource # Person.format = ActiveResource::Formats::XmlFormat # Person.find(1) # => GET /people/1.xml # - # Default format is <tt>:xml</tt>. + # Default format is <tt>:json</tt>. def format=(mime_type_reference_or_format) format = mime_type_reference_or_format.is_a?(Symbol) ? ActiveResource::Formats[mime_type_reference_or_format] : mime_type_reference_or_format @@ -498,9 +498,9 @@ module ActiveResource connection.format = format if site end - # Returns the current format, default is ActiveResource::Formats::XmlFormat. + # Returns the current format, default is ActiveResource::Formats::JsonFormat. def format - self._format || ActiveResource::Formats::XmlFormat + self._format || ActiveResource::Formats::JsonFormat end # Sets the number of seconds after which requests to the REST API should time out. @@ -570,7 +570,7 @@ module ActiveResource attr_accessor_with_default(:primary_key, 'id') #:nodoc: - # Gets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.xml</tt>) + # Gets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.json</tt>) # This method is regenerated at runtime based on what the \prefix is set to. def prefix(options={}) default = site.path @@ -587,7 +587,7 @@ module ActiveResource prefix_source end - # Sets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.xml</tt>). + # Sets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.json</tt>). # Default value is <tt>site.path</tt>. def prefix=(value = '/') # Replace :placeholders with '#{embedded options[:lookups]}' @@ -618,21 +618,21 @@ module ActiveResource # # ==== Options # +prefix_options+ - A \hash to add a \prefix to the request for nested URLs (e.g., <tt>:account_id => 19</tt> - # would yield a URL like <tt>/accounts/19/purchases.xml</tt>). + # would yield a URL like <tt>/accounts/19/purchases.json</tt>). # +query_options+ - A \hash to add items to the query string for the request. # # ==== Examples # Post.element_path(1) - # # => /posts/1.xml + # # => /posts/1.json # # Comment.element_path(1, :post_id => 5) - # # => /posts/5/comments/1.xml + # # => /posts/5/comments/1.json # # Comment.element_path(1, :post_id => 5, :active => 1) - # # => /posts/5/comments/1.xml?active=1 + # # => /posts/5/comments/1.json?active=1 # # Comment.element_path(1, {:post_id => 5}, {:active => 1}) - # # => /posts/5/comments/1.xml?active=1 + # # => /posts/5/comments/1.json?active=1 # def element_path(id, prefix_options = {}, query_options = nil) check_prefix_options(prefix_options) @@ -645,14 +645,14 @@ module ActiveResource # # ==== Options # * +prefix_options+ - A hash to add a prefix to the request for nested URLs (e.g., <tt>:account_id => 19</tt> - # would yield a URL like <tt>/accounts/19/purchases/new.xml</tt>). + # would yield a URL like <tt>/accounts/19/purchases/new.json</tt>). # # ==== Examples # Post.new_element_path - # # => /posts/new.xml + # # => /posts/new.json # # Comment.collection_path(:post_id => 5) - # # => /posts/5/comments/new.xml + # # => /posts/5/comments/new.json def new_element_path(prefix_options = {}) "#{prefix(prefix_options)}#{collection_name}/new.#{format.extension}" end @@ -662,21 +662,21 @@ module ActiveResource # # ==== Options # * +prefix_options+ - A hash to add a prefix to the request for nested URLs (e.g., <tt>:account_id => 19</tt> - # would yield a URL like <tt>/accounts/19/purchases.xml</tt>). + # would yield a URL like <tt>/accounts/19/purchases.json</tt>). # * +query_options+ - A hash to add items to the query string for the request. # # ==== Examples # Post.collection_path - # # => /posts.xml + # # => /posts.json # # Comment.collection_path(:post_id => 5) - # # => /posts/5/comments.xml + # # => /posts/5/comments.json # # Comment.collection_path(:post_id => 5, :active => 1) - # # => /posts/5/comments.xml?active=1 + # # => /posts/5/comments.json?active=1 # # Comment.collection_path({:post_id => 5}, {:active => 1}) - # # => /posts/5/comments.xml?active=1 + # # => /posts/5/comments.json?active=1 # def collection_path(prefix_options = {}, query_options = nil) check_prefix_options(prefix_options) @@ -745,34 +745,34 @@ module ActiveResource # # ==== Examples # Person.find(1) - # # => GET /people/1.xml + # # => GET /people/1.json # # Person.find(:all) - # # => GET /people.xml + # # => GET /people.json # # Person.find(:all, :params => { :title => "CEO" }) - # # => GET /people.xml?title=CEO + # # => GET /people.json?title=CEO # # Person.find(:first, :from => :managers) - # # => GET /people/managers.xml + # # => GET /people/managers.json # # Person.find(:last, :from => :managers) - # # => GET /people/managers.xml + # # => GET /people/managers.json # - # Person.find(:all, :from => "/companies/1/people.xml") - # # => GET /companies/1/people.xml + # Person.find(:all, :from => "/companies/1/people.json") + # # => GET /companies/1/people.json # # Person.find(:one, :from => :leader) - # # => GET /people/leader.xml + # # => GET /people/leader.json # # Person.find(:all, :from => :developers, :params => { :language => 'ruby' }) - # # => GET /people/developers.xml?language=ruby + # # => GET /people/developers.json?language=ruby # - # Person.find(:one, :from => "/companies/1/manager.xml") - # # => GET /companies/1/manager.xml + # Person.find(:one, :from => "/companies/1/manager.json") + # # => GET /companies/1/manager.json # # StreetAddress.find(1, :params => { :person_id => 1 }) - # # => GET /people/1/street_addresses/1.xml + # # => GET /people/1/street_addresses/1.json # # == Failure or missing data # A failure to find the requested object raises a ResourceNotFound @@ -833,7 +833,7 @@ module ActiveResource # my_event = Event.find(:first) # let's assume this is event with ID 7 # Event.delete(my_event.id) # sends DELETE /events/7 # - # # Let's assume a request to events/5/cancel.xml + # # Let's assume a request to events/5/cancel.json # Event.delete(params[:id]) # sends DELETE /events/5 def delete(id, options = {}) connection.delete(element_path(id, options)) @@ -1121,7 +1121,7 @@ module ActiveResource # Saves (+POST+) or \updates (+PUT+) a resource. Delegates to +create+ if the object is \new, # +update+ if it exists. If the response to the \save includes a body, it will be assumed that this body - # is XML for the final object as it looked after the \save (which would include attributes like +created_at+ + # is Json for the final object as it looked after the \save (which would include attributes like +created_at+ # that weren't part of the original submit). # # ==== Examples @@ -1232,9 +1232,16 @@ module ActiveResource # your_supplier = Supplier.new # your_supplier.load(my_attrs) # your_supplier.save - def load(attributes) + def load(attributes, remove_root = false) raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash) @prefix_options, attributes = split_options(attributes) + + if attributes.keys.size == 1 + remove_root = self.class.element_name == attributes.keys.first.to_s + end + + attributes = Formats.remove_root(attributes) if remove_root + attributes.each do |key, value| @attributes[key.to_s] = case value @@ -1285,7 +1292,7 @@ module ActiveResource # resource's attributes, the full body of the request will still be sent # in the save request to the remote service. def update_attributes(attributes) - load(attributes) && save + load(attributes, false) && save end # For checking <tt>respond_to?</tt> without searching the attributes (which is faster). @@ -1339,7 +1346,7 @@ module ActiveResource def load_attributes_from_response(response) if !response['Content-Length'].blank? && response['Content-Length'] != "0" && !response.body.nil? && response.body.strip.size > 0 - load(self.class.format.decode(response.body)) + load(self.class.format.decode(response.body), true) @persisted = true end end diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index 765575d866..d923204dde 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -30,7 +30,7 @@ module ActiveResource # The +site+ parameter is required and will set the +site+ # attribute to the URI for the remote resource service. - def initialize(site, format = ActiveResource::Formats::XmlFormat) + def initialize(site, format = ActiveResource::Formats::JsonFormat) raise ArgumentError, 'Missing site URI' unless site @user = @password = nil self.site = site diff --git a/activeresource/lib/active_resource/custom_methods.rb b/activeresource/lib/active_resource/custom_methods.rb index 9879f8cded..c1931b2758 100644 --- a/activeresource/lib/active_resource/custom_methods.rb +++ b/activeresource/lib/active_resource/custom_methods.rb @@ -11,10 +11,10 @@ module ActiveResource # # This route set creates routes for the following HTTP requests: # - # POST /people/new/register.xml # PeopleController.register - # PUT /people/1/promote.xml # PeopleController.promote with :id => 1 - # DELETE /people/1/deactivate.xml # PeopleController.deactivate with :id => 1 - # GET /people/active.xml # PeopleController.active + # POST /people/new/register.json # PeopleController.register + # PUT /people/1/promote.json # PeopleController.promote with :id => 1 + # DELETE /people/1/deactivate.json # PeopleController.deactivate with :id => 1 + # GET /people/active.json # PeopleController.active # # Using this module, Active Resource can use these custom REST methods just like the # standard methods. @@ -23,13 +23,13 @@ module ActiveResource # self.site = "http://37s.sunrise.i:3000" # end # - # Person.new(:name => 'Ryan).post(:register) # POST /people/new/register.xml + # Person.new(:name => 'Ryan).post(:register) # POST /people/new/register.json # # => { :id => 1, :name => 'Ryan' } # - # Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.xml - # Person.find(1).delete(:deactivate) # DELETE /people/1/deactivate.xml + # Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.json + # Person.find(1).delete(:deactivate) # DELETE /people/1/deactivate.json # - # Person.get(:active) # GET /people/active.xml + # Person.get(:active) # GET /people/active.json # # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}] # module CustomMethods @@ -41,10 +41,10 @@ module ActiveResource # Invokes a GET to a given custom REST method. For example: # - # Person.get(:active) # GET /people/active.xml + # Person.get(:active) # GET /people/active.json # # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}] # - # Person.get(:active, :awesome => true) # GET /people/active.xml?awesome=true + # Person.get(:active, :awesome => true) # GET /people/active.json?awesome=true # # => [{:id => 1, :name => 'Ryan'}] # # Note: the objects returned from this method are not automatically converted @@ -54,7 +54,9 @@ module ActiveResource # # Person.find(:all, :from => :active) def get(custom_method_name, options = {}) - format.decode(connection.get(custom_method_collection_url(custom_method_name, options), headers).body) + hashified = format.decode(connection.get(custom_method_collection_url(custom_method_name, options), headers).body) + derooted = Formats.remove_root(hashified) + derooted.is_a?(Array) ? derooted.map { |e| Formats.remove_root(e) } : derooted end def post(custom_method_name, options = {}, body = '') diff --git a/activeresource/lib/active_resource/formats.rb b/activeresource/lib/active_resource/formats.rb index 53b75b34e7..f7ad689cc5 100644 --- a/activeresource/lib/active_resource/formats.rb +++ b/activeresource/lib/active_resource/formats.rb @@ -10,5 +10,13 @@ module ActiveResource def self.[](mime_type_reference) ActiveResource::Formats.const_get(ActiveSupport::Inflector.camelize(mime_type_reference.to_s) + "Format") end + + def self.remove_root(data) + if data.is_a?(Hash) && data.keys.size == 1 + data.values.first + else + data + end + end end end diff --git a/activeresource/lib/active_resource/formats/json_format.rb b/activeresource/lib/active_resource/formats/json_format.rb index 9980634921..827d1cc23a 100644 --- a/activeresource/lib/active_resource/formats/json_format.rb +++ b/activeresource/lib/active_resource/formats/json_format.rb @@ -18,7 +18,7 @@ module ActiveResource end def decode(json) - ActiveSupport::JSON.decode(json) + Formats.remove_root(ActiveSupport::JSON.decode(json)) end end end diff --git a/activeresource/lib/active_resource/formats/xml_format.rb b/activeresource/lib/active_resource/formats/xml_format.rb index 3b2575cfa1..49cb9aa1ac 100644 --- a/activeresource/lib/active_resource/formats/xml_format.rb +++ b/activeresource/lib/active_resource/formats/xml_format.rb @@ -18,19 +18,8 @@ module ActiveResource end def decode(xml) - from_xml_data(Hash.from_xml(xml)) + Formats.remove_root(Hash.from_xml(xml)) end - - private - # Manipulate from_xml Hash, because xml_simple is not exactly what we - # want for Active Resource. - def from_xml_data(data) - if data.is_a?(Hash) && data.keys.size == 1 - data.values.first - else - data - end - end end end end diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb index 3bfd536b29..e90580be4f 100644 --- a/activeresource/lib/active_resource/http_mock.rb +++ b/activeresource/lib/active_resource/http_mock.rb @@ -20,10 +20,10 @@ module ActiveResource # * <tt>path</tt> - A string, starting with a "/", defining the URI that is expected to be # called. # * <tt>request_headers</tt> - Headers that are expected along with the request. This argument uses a - # hash format, such as <tt>{ "Content-Type" => "application/xml" }</tt>. This mock will only trigger + # hash format, such as <tt>{ "Content-Type" => "application/json" }</tt>. This mock will only trigger # if your tests sends a request with identical headers. # * <tt>body</tt> - The data to be returned. This should be a string of Active Resource parseable content, - # such as XML. + # such as Json. # * <tt>status</tt> - The HTTP response code, as an integer, to return with the response. # * <tt>response_headers</tt> - Headers to be returned with the response. Uses the same hash format as # <tt>request_headers</tt> listed above. @@ -35,12 +35,12 @@ module ActiveResource # # ==== Example # def setup - # @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person") + # @matz = { :person => { :id => 1, :name => "Matz" } }.to_json # ActiveResource::HttpMock.respond_to do |mock| - # mock.post "/people.xml", {}, @matz, 201, "Location" => "/people/1.xml" - # mock.get "/people/1.xml", {}, @matz - # mock.put "/people/1.xml", {}, nil, 204 - # mock.delete "/people/1.xml", {}, nil, 200 + # mock.post "/people.json", {}, @matz, 201, "Location" => "/people/1.json" + # mock.get "/people/1.json", {}, @matz + # mock.put "/people/1.json", {}, nil, 204 + # mock.delete "/people/1.json", {}, nil, 200 # end # end # @@ -85,9 +85,9 @@ module ActiveResource # # ==== Example # def setup - # @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person") + # @matz = { :person => { :id => 1, :name => "Matz" } }.to_json # ActiveResource::HttpMock.respond_to do |mock| - # mock.get "/people/1.xml", {}, @matz + # mock.get "/people/1.json", {}, @matz # end # end # @@ -95,7 +95,7 @@ module ActiveResource # person = Person.find(1) # Call the remote service # # # This request object has the same HTTP method and path as declared by the mock - # expected_request = ActiveResource::Request.new(:get, "/people/1.xml") + # expected_request = ActiveResource::Request.new(:get, "/people/1.json") # # # Assert that the mock received, and responded to, the expected request from the model # assert ActiveResource::HttpMock.requests.include?(expected_request) @@ -117,12 +117,12 @@ module ActiveResource # # === Example # - # @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person") + # @matz = { :person => { :id => 1, :name => "Matz" } }.to_json # ActiveResource::HttpMock.respond_to do |mock| - # mock.post "/people.xml", {}, @matz, 201, "Location" => "/people/1.xml" - # mock.get "/people/1.xml", {}, @matz - # mock.put "/people/1.xml", {}, nil, 204 - # mock.delete "/people/1.xml", {}, nil, 200 + # mock.post "/people.json", {}, @matz, 201, "Location" => "/people/1.json" + # mock.get "/people/1.json", {}, @matz + # mock.put "/people/1.json", {}, nil, 204 + # mock.delete "/people/1.json", {}, nil, 200 # end # # Alternatively, accepts a hash of <tt>{Request => Response}</tt> pairs allowing you to generate @@ -135,11 +135,11 @@ module ActiveResource # # Request.new(:#{method}, path, nil, request_headers) # - # @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person") + # @matz = { :person => { :id => 1, :name => "Matz" } }.to_json # - # create_matz = ActiveResource::Request.new(:post, '/people.xml', @matz, {}) - # created_response = ActiveResource::Response.new("", 201, {"Location" => "/people/1.xml"}) - # get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil) + # create_matz = ActiveResource::Request.new(:post, '/people.json', @matz, {}) + # created_response = ActiveResource::Response.new("", 201, {"Location" => "/people/1.json"}) + # get_matz = ActiveResource::Request.new(:get, '/people/1.json', nil) # ok_response = ActiveResource::Response.new("", 200, {}) # # pairs = {create_matz => created_response, get_matz => ok_response} @@ -154,12 +154,12 @@ module ActiveResource # === Example # # ActiveResource::HttpMock.respond_to do |mock| - # mock.send(:get, "/people/1", {}, "XML1") + # mock.send(:get, "/people/1", {}, "JSON1") # end # ActiveResource::HttpMock.responses.length #=> 1 # # ActiveResource::HttpMock.respond_to(false) do |mock| - # mock.send(:get, "/people/2", {}, "XML2") + # mock.send(:get, "/people/2", {}, "JSON2") # end # ActiveResource::HttpMock.responses.length #=> 2 # @@ -169,11 +169,11 @@ module ActiveResource # === Example # # ActiveResource::HttpMock.respond_to do |mock| - # mock.send(:get, "/people/1", {}, "XML1") + # mock.send(:get, "/people/1", {}, "JSON1") # end # ActiveResource::HttpMock.responses.length #=> 1 # - # get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil) + # get_matz = ActiveResource::Request.new(:get, '/people/1.json', nil) # ok_response = ActiveResource::Response.new("", 200, {}) # # pairs = {get_matz => ok_response} diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb index 195f93f2a6..948dd94a1d 100644 --- a/activeresource/test/abstract_unit.rb +++ b/activeresource/test/abstract_unit.rb @@ -20,16 +20,19 @@ rescue LoadError end def setup_response - @default_request_headers = { 'Content-Type' => 'application/xml' } - @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') - @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person') - @greg = { :id => 3, :name => 'Greg' }.to_xml(:root => 'person') - @addy = { :id => 1, :street => '12345 Street', :country => 'Australia' }.to_xml(:root => 'address') - @rick = { :name => "Rick", :age => 25 }.to_xml(:root => "person") - @joe = { 'person' => { :id => 6, :name => 'Joe' }}.to_json - @people = [{ :id => 1, :name => 'Matz' }, { :id => 2, :name => 'David' }].to_xml(:root => 'people') - @people_david = [{ :id => 2, :name => 'David' }].to_xml(:root => 'people') - @addresses = [{ :id => 1, :street => '12345 Street', :country => 'Australia' }].to_xml(:root => 'addresses') + matz_hash = { 'person' => { :id => 1, :name => 'Matz' } } + + @default_request_headers = { 'Content-Type' => 'application/json' } + @matz = matz_hash.to_json + @matz_xml = matz_hash.to_xml + @david = { :person => { :id => 2, :name => 'David' } }.to_json + @greg = { :person => { :id => 3, :name => 'Greg' } }.to_json + @addy = { :address => { :id => 1, :street => '12345 Street', :country => 'Australia' } }.to_json + @rick = { :person => { :name => "Rick", :age => 25 } }.to_json + @joe = { :person => { :id => 6, :name => 'Joe', :likes_hats => true }}.to_json + @people = { :people => [ { :person => { :id => 1, :name => 'Matz' } }, { :person => { :id => 2, :name => 'David' } }] }.to_json + @people_david = { :people => [ { :person => { :id => 2, :name => 'David' } }] }.to_json + @addresses = { :addresses => [{ :address => { :id => 1, :street => '12345 Street', :country => 'Australia' } }] }.to_json # - deep nested resource - # - Luis (Customer) @@ -48,19 +51,38 @@ def setup_response # - Natacha (Customer::Friend::Brother::Child) # - Milena (Customer::Friend::Brother) # - @luis = {:id => 1, :name => 'Luis', - :friends => [{:name => 'JK', - :brothers => [{:name => 'Mateo', - :children => [{:name => 'Edith'},{:name => 'Martha'}]}, - {:name => 'Felipe', - :children => [{:name => 'Bryan'},{:name => 'Luke'}]}]}, - {:name => 'Eduardo', - :brothers => [{:name => 'Sebas', - :children => [{:name => 'Andres'},{:name => 'Jorge'}]}, - {:name => 'Elsa', - :children => [{:name => 'Natacha'}]}, - {:name => 'Milena', - :children => []}]}]}.to_xml(:root => 'customer') + @luis = { + :customer => { + :id => 1, + :name => 'Luis', + :friends => [{ + :name => 'JK', + :brothers => [ + { + :name => 'Mateo', + :children => [{ :name => 'Edith' },{ :name => 'Martha' }] + }, { + :name => 'Felipe', + :children => [{ :name => 'Bryan' },{ :name => 'Luke' }] + } + ] + }, { + :name => 'Eduardo', + :brothers => [ + { + :name => 'Sebas', + :children => [{ :name => 'Andres' },{ :name => 'Jorge' }] + }, { + :name => 'Elsa', + :children => [{ :name => 'Natacha' }] + }, { + :name => 'Milena', + :children => [] + } + ] + }] + } + }.to_json # - resource with yaml array of strings; for ARs using serialize :bar, Array @marty = <<-eof.strip <?xml version=\"1.0\" encoding=\"UTF-8\"?> @@ -75,49 +97,52 @@ def setup_response </person> eof - @startup_sound = { - :name => "Mac Startup Sound", :author => { :name => "Jim Reekes" } - }.to_xml(:root => 'sound') + @startup_sound = { + :sound => { + :name => "Mac Startup Sound", :author => { :name => "Jim Reekes" } + } + }.to_json ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, @matz + mock.get "/people/1.json", {}, @matz + mock.get "/people/1.xml", {}, @matz_xml mock.get "/people/2.xml", {}, @david mock.get "/people/5.xml", {}, @marty - mock.get "/people/Greg.xml", {}, @greg + mock.get "/people/Greg.json", {}, @greg mock.get "/people/6.json", {}, @joe - mock.get "/people/4.xml", {'key' => 'value'}, nil, 404 - mock.put "/people/1.xml", {}, nil, 204 - mock.delete "/people/1.xml", {}, nil, 200 + mock.get "/people/4.json", { 'key' => 'value' }, nil, 404 + mock.put "/people/1.json", {}, nil, 204 + mock.delete "/people/1.json", {}, nil, 200 mock.delete "/people/2.xml", {}, nil, 400 - mock.get "/people/99.xml", {}, nil, 404 - mock.post "/people.xml", {}, @rick, 201, 'Location' => '/people/5.xml' - mock.get "/people.xml", {}, @people - mock.get "/people/1/addresses.xml", {}, @addresses - mock.get "/people/1/addresses/1.xml", {}, @addy + mock.get "/people/99.json", {}, nil, 404 + mock.post "/people.json", {}, @rick, 201, 'Location' => '/people/5.xml' + mock.get "/people.json", {}, @people + mock.get "/people/1/addresses.json", {}, @addresses + mock.get "/people/1/addresses/1.json", {}, @addy mock.get "/people/1/addresses/2.xml", {}, nil, 404 - mock.get "/people/2/addresses.xml", {}, nil, 404 + mock.get "/people/2/addresses.json", {}, nil, 404 mock.get "/people/2/addresses/1.xml", {}, nil, 404 - mock.get "/people/Greg/addresses/1.xml", {}, @addy - mock.put "/people/1/addresses/1.xml", {}, nil, 204 - mock.delete "/people/1/addresses/1.xml", {}, nil, 200 - mock.post "/people/1/addresses.xml", {}, nil, 201, 'Location' => '/people/1/addresses/5' - mock.get "/people/1/addresses/99.xml", {}, nil, 404 + mock.get "/people/Greg/addresses/1.json", {}, @addy + mock.put "/people/1/addresses/1.json", {}, nil, 204 + mock.delete "/people/1/addresses/1.json", {}, nil, 200 + mock.post "/people/1/addresses.json", {}, nil, 201, 'Location' => '/people/1/addresses/5' + mock.get "/people/1/addresses/99.json", {}, nil, 404 mock.get "/people//addresses.xml", {}, nil, 404 mock.get "/people//addresses/1.xml", {}, nil, 404 mock.put "/people//addresses/1.xml", {}, nil, 404 mock.delete "/people//addresses/1.xml", {}, nil, 404 mock.post "/people//addresses.xml", {}, nil, 404 - mock.head "/people/1.xml", {}, nil, 200 - mock.head "/people/Greg.xml", {}, nil, 200 - mock.head "/people/99.xml", {}, nil, 404 - mock.head "/people/1/addresses/1.xml", {}, nil, 200 - mock.head "/people/1/addresses/2.xml", {}, nil, 404 - mock.head "/people/2/addresses/1.xml", {}, nil, 404 - mock.head "/people/Greg/addresses/1.xml", {}, nil, 200 + mock.head "/people/1.json", {}, nil, 200 + mock.head "/people/Greg.json", {}, nil, 200 + mock.head "/people/99.json", {}, nil, 404 + mock.head "/people/1/addresses/1.json", {}, nil, 200 + mock.head "/people/1/addresses/2.json", {}, nil, 404 + mock.head "/people/2/addresses/1.json", {}, nil, 404 + mock.head "/people/Greg/addresses/1.json", {}, nil, 200 # customer - mock.get "/customers/1.xml", {}, @luis + mock.get "/customers/1.json", {}, @luis # sound - mock.get "/sounds/1.xml", {}, @startup_sound + mock.get "/sounds/1.json", {}, @startup_sound end Person.user = nil diff --git a/activeresource/test/cases/authorization_test.rb b/activeresource/test/cases/authorization_test.rb index a6797643e1..69ef9a2821 100644 --- a/activeresource/test/cases/authorization_test.rb +++ b/activeresource/test/cases/authorization_test.rb @@ -5,36 +5,36 @@ class AuthorizationTest < Test::Unit::TestCase def setup @conn = ActiveResource::Connection.new('http://localhost') - @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') - @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person') + @matz = { :person => { :id => 1, :name => 'Matz' } }.to_json + @david = { :person => { :id => 2, :name => 'David' } }.to_json @authenticated_conn = ActiveResource::Connection.new("http://david:test123@localhost") @basic_authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' } @nonce = "MTI0OTUxMzc4NzpjYWI3NDM3NDNmY2JmODU4ZjQ2ZjcwNGZkMTJiMjE0NA==" ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/2.xml", @basic_authorization_request_header, @david - mock.get "/people/1.xml", @basic_authorization_request_header, nil, 401, { 'WWW-Authenticate' => 'i_should_be_ignored' } - mock.put "/people/2.xml", @basic_authorization_request_header, nil, 204 - mock.delete "/people/2.xml", @basic_authorization_request_header, nil, 200 - mock.post "/people/2/addresses.xml", @basic_authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5' - mock.head "/people/2.xml", @basic_authorization_request_header, nil, 200 + mock.get "/people/2.json", @basic_authorization_request_header, @david + mock.get "/people/1.json", @basic_authorization_request_header, nil, 401, { 'WWW-Authenticate' => 'i_should_be_ignored' } + mock.put "/people/2.json", @basic_authorization_request_header, nil, 204 + mock.delete "/people/2.json", @basic_authorization_request_header, nil, 200 + mock.post "/people/2/addresses.json", @basic_authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5' + mock.head "/people/2.json", @basic_authorization_request_header, nil, 200 - mock.get "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "a10c9bd131c9d4d7755b8f4706fd04af") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } - mock.get "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "912c7a643f18cda562b8d9662c47b6f5") }, @david, 200 - mock.get "/people/1.xml", { 'Authorization' => request_digest_auth_header("/people/1.xml", "d76e675c0ecfa2bb1abe01491b068a06") }, @matz, 200 + mock.get "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "fad396f6a34aeba28e28b9b96ddbb671") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } + mock.get "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "c064d5ba8891a25290c76c8c7d31fb7b") }, @david, 200 + mock.get "/people/1.json", { 'Authorization' => request_digest_auth_header("/people/1.json", "f9c0b594257bb8422af4abd429c5bb70") }, @matz, 200 - mock.put "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "7de8a265a5be3c4c2d3a246562ecd6bd") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } - mock.put "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "3fb3b33d9d0b869cc75815aa11faacd9") }, nil, 204 + mock.put "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "50a685d814f94665b9d160fbbaa3958a") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } + mock.put "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "5a75cde841122d8e0f20f8fd1f98a743") }, nil, 204 - mock.delete "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "07dfc32769a34ea3510d3a77d64ca495") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } - mock.delete "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "5d438610de7ec163b29096c9afcbb254") }, nil, 200 + mock.delete "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "846f799107eab5ca4285b909ee299a33") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } + mock.delete "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "9f5b155224edbbb69fd99d8ce094681e") }, nil, 200 - mock.post "/people/2/addresses.xml", { 'Authorization' => blank_digest_auth_header("/people/2/addresses.xml", "966dab13620421f928d051f2b9d7b9af") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } - mock.post "/people/2/addresses.xml", { 'Authorization' => request_digest_auth_header("/people/2/addresses.xml", "ed540d032c63f8ee34959116c090ec45") }, nil, 201, 'Location' => '/people/1/addresses/5' + mock.post "/people/2/addresses.json", { 'Authorization' => blank_digest_auth_header("/people/2/addresses.json", "6984d405ff3d9ed07bbf747dcf16afb0") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } + mock.post "/people/2/addresses.json", { 'Authorization' => request_digest_auth_header("/people/2/addresses.json", "4bda6a28dbf930b5af9244073623bd04") }, nil, 201, 'Location' => '/people/1/addresses/5' - mock.head "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "2854eeb92cce2aed29350ea0ce7ba1e2") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } - mock.head "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "07cd4d247e9c130f92ba2501a080b328") }, nil, 200 + mock.head "/people/2.json", { 'Authorization' => blank_digest_auth_header("/people/2.json", "15e5ed84ba5c4cfcd5c98a36c2e4f421") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header } + mock.head "/people/2.json", { 'Authorization' => request_digest_auth_header("/people/2.json", "d4c6d2bcc8717abb2e2ccb8c49ee6a91") }, nil, 200 end # Make client nonce deterministic @@ -48,7 +48,7 @@ class AuthorizationTest < Test::Unit::TestCase end def test_authorization_header - authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) + authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization'] authorization = authorization_header["Authorization"].to_s.split @@ -58,7 +58,7 @@ class AuthorizationTest < Test::Unit::TestCase def test_authorization_header_with_username_but_no_password @conn = ActiveResource::Connection.new("http://david:@localhost") - authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) + authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) authorization = authorization_header["Authorization"].to_s.split assert_equal "Basic", authorization[0] @@ -67,7 +67,7 @@ class AuthorizationTest < Test::Unit::TestCase def test_authorization_header_with_password_but_no_username @conn = ActiveResource::Connection.new("http://:test123@localhost") - authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) + authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) authorization = authorization_header["Authorization"].to_s.split assert_equal "Basic", authorization[0] @@ -76,7 +76,7 @@ class AuthorizationTest < Test::Unit::TestCase def test_authorization_header_with_decoded_credentials_from_url @conn = ActiveResource::Connection.new("http://my%40email.com:%31%32%33@localhost") - authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) + authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) authorization = authorization_header["Authorization"].to_s.split assert_equal "Basic", authorization[0] @@ -87,7 +87,7 @@ class AuthorizationTest < Test::Unit::TestCase @authenticated_conn = ActiveResource::Connection.new("http://@localhost") @authenticated_conn.user = 'david' @authenticated_conn.password = 'test123' - authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) + authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization'] authorization = authorization_header["Authorization"].to_s.split @@ -98,7 +98,7 @@ class AuthorizationTest < Test::Unit::TestCase def test_authorization_header_explicitly_setting_username_but_no_password @conn = ActiveResource::Connection.new("http://@localhost") @conn.user = "david" - authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) + authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) authorization = authorization_header["Authorization"].to_s.split assert_equal "Basic", authorization[0] @@ -108,7 +108,7 @@ class AuthorizationTest < Test::Unit::TestCase def test_authorization_header_explicitly_setting_password_but_no_username @conn = ActiveResource::Connection.new("http://@localhost") @conn.password = "test123" - authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) + authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) authorization = authorization_header["Authorization"].to_s.split assert_equal "Basic", authorization[0] @@ -117,7 +117,7 @@ class AuthorizationTest < Test::Unit::TestCase def test_authorization_header_if_credentials_supplied_and_auth_type_is_basic @authenticated_conn.auth_type = :basic - authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) + authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization'] authorization = authorization_header["Authorization"].to_s.split @@ -127,96 +127,96 @@ class AuthorizationTest < Test::Unit::TestCase def test_authorization_header_if_credentials_supplied_and_auth_type_is_digest @authenticated_conn.auth_type = :digest - authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml')) - assert_equal blank_digest_auth_header("/people/2.xml", "a10c9bd131c9d4d7755b8f4706fd04af"), authorization_header['Authorization'] + authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json')) + assert_equal blank_digest_auth_header("/people/2.json", "fad396f6a34aeba28e28b9b96ddbb671"), authorization_header['Authorization'] end def test_get - david = decode(@authenticated_conn.get("/people/2.xml")) + david = decode(@authenticated_conn.get("/people/2.json")) assert_equal "David", david["name"] end def test_post - response = @authenticated_conn.post("/people/2/addresses.xml") + response = @authenticated_conn.post("/people/2/addresses.json") assert_equal "/people/1/addresses/5", response["Location"] end def test_put - response = @authenticated_conn.put("/people/2.xml") + response = @authenticated_conn.put("/people/2.json") assert_equal 204, response.code end def test_delete - response = @authenticated_conn.delete("/people/2.xml") + response = @authenticated_conn.delete("/people/2.json") assert_equal 200, response.code end def test_head - response = @authenticated_conn.head("/people/2.xml") + response = @authenticated_conn.head("/people/2.json") assert_equal 200, response.code end def test_get_with_digest_auth_handles_initial_401_response_and_retries @authenticated_conn.auth_type = :digest - response = @authenticated_conn.get("/people/2.xml") + response = @authenticated_conn.get("/people/2.json") assert_equal "David", decode(response)["name"] end def test_post_with_digest_auth_handles_initial_401_response_and_retries @authenticated_conn.auth_type = :digest - response = @authenticated_conn.post("/people/2/addresses.xml") + response = @authenticated_conn.post("/people/2/addresses.json") assert_equal "/people/1/addresses/5", response["Location"] assert_equal 201, response.code end def test_put_with_digest_auth_handles_initial_401_response_and_retries @authenticated_conn.auth_type = :digest - response = @authenticated_conn.put("/people/2.xml") + response = @authenticated_conn.put("/people/2.json") assert_equal 204, response.code end def test_delete_with_digest_auth_handles_initial_401_response_and_retries @authenticated_conn.auth_type = :digest - response = @authenticated_conn.delete("/people/2.xml") + response = @authenticated_conn.delete("/people/2.json") assert_equal 200, response.code end def test_head_with_digest_auth_handles_initial_401_response_and_retries @authenticated_conn.auth_type = :digest - response = @authenticated_conn.head("/people/2.xml") + response = @authenticated_conn.head("/people/2.json") assert_equal 200, response.code end def test_get_with_digest_auth_caches_nonce @authenticated_conn.auth_type = :digest - response = @authenticated_conn.get("/people/2.xml") + response = @authenticated_conn.get("/people/2.json") assert_equal "David", decode(response)["name"] # There is no mock for this request with a non-cached nonce. - response = @authenticated_conn.get("/people/1.xml") + response = @authenticated_conn.get("/people/1.json") assert_equal "Matz", decode(response)["name"] end def test_retry_on_401_only_happens_with_digest_auth - assert_raise(ActiveResource::UnauthorizedAccess) { @authenticated_conn.get("/people/1.xml") } + assert_raise(ActiveResource::UnauthorizedAccess) { @authenticated_conn.get("/people/1.json") } assert_equal "", @authenticated_conn.send(:response_auth_header) end def test_raises_invalid_request_on_unauthorized_requests - assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.xml") } - assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") } - assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") } - assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") } - assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.xml") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.json") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.json") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.json") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.json") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.json") } end def test_raises_invalid_request_on_unauthorized_requests_with_digest_auth @conn.auth_type = :digest - assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.xml") } - assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") } - assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") } - assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") } - assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.xml") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.json") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.json") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.json") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.json") } + assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.json") } end def test_client_nonce_is_not_nil diff --git a/activeresource/test/cases/base/custom_methods_test.rb b/activeresource/test/cases/base/custom_methods_test.rb index 0fbf94bc0e..3eaa9b1c5b 100644 --- a/activeresource/test/cases/base/custom_methods_test.rb +++ b/activeresource/test/cases/base/custom_methods_test.rb @@ -5,32 +5,32 @@ require 'active_support/core_ext/hash/conversions' class CustomMethodsTest < Test::Unit::TestCase def setup - @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') - @matz_deep = { :id => 1, :name => 'Matz', :other => 'other' }.to_xml(:root => 'person') - @matz_array = [{ :id => 1, :name => 'Matz' }].to_xml(:root => 'people') - @ryan = { :name => 'Ryan' }.to_xml(:root => 'person') - @addy = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address') - @addy_deep = { :id => 1, :street => '12345 Street', :zip => "27519" }.to_xml(:root => 'address') + @matz = { :person => { :id => 1, :name => 'Matz' } }.to_json + @matz_deep = { :person => { :id => 1, :name => 'Matz', :other => 'other' } }.to_json + @matz_array = { :people => [{ :person => { :id => 1, :name => 'Matz' } }] }.to_json + @ryan = { :person => { :name => 'Ryan' } }.to_json + @addy = { :address => { :id => 1, :street => '12345 Street' } }.to_json + @addy_deep = { :address => { :id => 1, :street => '12345 Street', :zip => "27519" } }.to_json ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, @matz - mock.get "/people/1/shallow.xml", {}, @matz - mock.get "/people/1/deep.xml", {}, @matz_deep - mock.get "/people/retrieve.xml?name=Matz", {}, @matz_array - mock.get "/people/managers.xml", {}, @matz_array - mock.post "/people/hire.xml?name=Matz", {}, nil, 201 - mock.put "/people/1/promote.xml?position=Manager", {}, nil, 204 - mock.put "/people/promote.xml?name=Matz", {}, nil, 204, {} - mock.put "/people/sort.xml?by=name", {}, nil, 204 - mock.delete "/people/deactivate.xml?name=Matz", {}, nil, 200 - mock.delete "/people/1/deactivate.xml", {}, nil, 200 - mock.post "/people/new/register.xml", {}, @ryan, 201, 'Location' => '/people/5.xml' - mock.post "/people/1/register.xml", {}, @matz, 201 - mock.get "/people/1/addresses/1.xml", {}, @addy - mock.get "/people/1/addresses/1/deep.xml", {}, @addy_deep - mock.put "/people/1/addresses/1/normalize_phone.xml?locale=US", {}, nil, 204 - mock.put "/people/1/addresses/sort.xml?by=name", {}, nil, 204 - mock.post "/people/1/addresses/new/link.xml", {}, { :street => '12345 Street' }.to_xml(:root => 'address'), 201, 'Location' => '/people/1/addresses/2.xml' + mock.get "/people/1.json", {}, @matz + mock.get "/people/1/shallow.json", {}, @matz + mock.get "/people/1/deep.json", {}, @matz_deep + mock.get "/people/retrieve.json?name=Matz", {}, @matz_array + mock.get "/people/managers.json", {}, @matz_array + mock.post "/people/hire.json?name=Matz", {}, nil, 201 + mock.put "/people/1/promote.json?position=Manager", {}, nil, 204 + mock.put "/people/promote.json?name=Matz", {}, nil, 204, {} + mock.put "/people/sort.json?by=name", {}, nil, 204 + mock.delete "/people/deactivate.json?name=Matz", {}, nil, 200 + mock.delete "/people/1/deactivate.json", {}, nil, 200 + mock.post "/people/new/register.json", {}, @ryan, 201, 'Location' => '/people/5.json' + mock.post "/people/1/register.json", {}, @matz, 201 + mock.get "/people/1/addresses/1.json", {}, @addy + mock.get "/people/1/addresses/1/deep.json", {}, @addy_deep + mock.put "/people/1/addresses/1/normalize_phone.json?locale=US", {}, nil, 204 + mock.put "/people/1/addresses/sort.json?by=name", {}, nil, 204 + mock.post "/people/1/addresses/new/link.json", {}, { :address => { :street => '12345 Street' } }.to_json, 201, 'Location' => '/people/1/addresses/2.json' end Person.user = nil @@ -81,14 +81,14 @@ class CustomMethodsTest < Test::Unit::TestCase def test_custom_new_element_method # Test POST against a new element URL ryan = Person.new(:name => 'Ryan') - assert_equal ActiveResource::Response.new(@ryan, 201, {'Location' => '/people/5.xml'}), ryan.post(:register) - expected_request = ActiveResource::Request.new(:post, '/people/new/register.xml', @ryan) + assert_equal ActiveResource::Response.new(@ryan, 201, { 'Location' => '/people/5.json' }), ryan.post(:register) + expected_request = ActiveResource::Request.new(:post, '/people/new/register.json', @ryan) assert_equal expected_request.body, ActiveResource::HttpMock.requests.first.body # Test POST against a nested collection URL addy = StreetAddress.new(:street => '123 Test Dr.', :person_id => 1) - assert_equal ActiveResource::Response.new({ :street => '12345 Street' }.to_xml(:root => 'address'), - 201, {'Location' => '/people/1/addresses/2.xml'}), + assert_equal ActiveResource::Response.new({ :address => { :street => '12345 Street' } }.to_json, + 201, { 'Location' => '/people/1/addresses/2.json' }), addy.post(:link) matz = Person.find(1) diff --git a/activeresource/test/cases/base/load_test.rb b/activeresource/test/cases/base/load_test.rb index 228dc36d9b..d6b04cfaa8 100644 --- a/activeresource/test/cases/base/load_test.rb +++ b/activeresource/test/cases/base/load_test.rb @@ -36,10 +36,10 @@ class BaseLoadTest < Test::Unit::TestCase def setup @matz = { :id => 1, :name => 'Matz' } - @first_address = { :id => 1, :street => '12345 Street' } - @addresses = [@first_address, { :id => 2, :street => '67890 Street' }] - @addresses_from_xml = { :street_addresses => @addresses } - @addresses_from_xml_single = { :street_addresses => [ @first_address ] } + @first_address = { :address => { :id => 1, :street => '12345 Street' } } + @addresses = [@first_address, { :address => { :id => 2, :street => '67890 Street' } }] + @addresses_from_json = { :street_addresses => @addresses } + @addresses_from_json_single = { :street_addresses => [ @first_address ] } @deep = { :id => 1, :street => { :id => 1, :state => { :id => 1, :name => 'Oregon', @@ -72,28 +72,29 @@ class BaseLoadTest < Test::Unit::TestCase def test_after_load_attributes_are_accessible_via_indifferent_access assert_equal Hash.new, @person.attributes + matz_attributes = @matz.values.first assert_equal @matz.stringify_keys, @person.load(@matz).attributes assert_equal @matz[:name], @person.attributes['name'] assert_equal @matz[:name], @person.attributes[:name] end def test_load_one_with_existing_resource - address = @person.load(:street_address => @first_address).street_address + address = @person.load(:street_address => @first_address.values.first).street_address assert_kind_of StreetAddress, address - assert_equal @first_address.stringify_keys, address.attributes + assert_equal @first_address.values.first.stringify_keys, address.attributes end def test_load_one_with_unknown_resource - address = silence_warnings { @person.load(:address => @first_address).address } + address = silence_warnings { @person.load(@first_address).address } assert_kind_of Person::Address, address - assert_equal @first_address.stringify_keys, address.attributes + assert_equal @first_address.values.first.stringify_keys, address.attributes end def test_load_collection_with_existing_resource - addresses = @person.load(@addresses_from_xml).street_addresses + addresses = @person.load(@addresses_from_json).street_addresses assert_kind_of Array, addresses addresses.each { |address| assert_kind_of StreetAddress, address } - assert_equal @addresses.map(&:stringify_keys), addresses.map(&:attributes) + assert_equal @addresses.map { |a| a[:address].stringify_keys }, addresses.map(&:attributes) end def test_load_collection_with_unknown_resource @@ -102,14 +103,14 @@ class BaseLoadTest < Test::Unit::TestCase addresses = silence_warnings { @person.load(:addresses => @addresses).addresses } assert Person.const_defined?(:Address), "Address should have been autocreated" addresses.each { |address| assert_kind_of Person::Address, address } - assert_equal @addresses.map(&:stringify_keys), addresses.map(&:attributes) + assert_equal @addresses.map { |a| a[:address].stringify_keys }, addresses.map(&:attributes) end def test_load_collection_with_single_existing_resource - addresses = @person.load(@addresses_from_xml_single).street_addresses + addresses = @person.load(@addresses_from_json_single).street_addresses assert_kind_of Array, addresses addresses.each { |address| assert_kind_of StreetAddress, address } - assert_equal [ @first_address ].map(&:stringify_keys), addresses.map(&:attributes) + assert_equal [ @first_address.values.first ].map(&:stringify_keys), addresses.map(&:attributes) end def test_load_collection_with_single_unknown_resource @@ -118,7 +119,7 @@ class BaseLoadTest < Test::Unit::TestCase addresses = silence_warnings { @person.load(:addresses => [ @first_address ]).addresses } assert Person.const_defined?(:Address), "Address should have been autocreated" addresses.each { |address| assert_kind_of Person::Address, address } - assert_equal [ @first_address ].map(&:stringify_keys), addresses.map(&:attributes) + assert_equal [ @first_address.values.first ].map(&:stringify_keys), addresses.map(&:attributes) end def test_recursively_loaded_collections @@ -164,7 +165,7 @@ class BaseLoadTest < Test::Unit::TestCase end def test_nested_collections_within_the_same_namespace - n = Highrise::Note.new(:comments => [{ :name => "1" }]) + n = Highrise::Note.new(:comments => [{ :comment => { :name => "1" } }]) assert_kind_of Highrise::Comment, n.comments.first end diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb index 37f30e4353..48fdeb13df 100644 --- a/activeresource/test/cases/base/schema_test.rb +++ b/activeresource/test/cases/base/schema_test.rb @@ -118,7 +118,7 @@ class SchemaTest < ActiveModel::TestCase test "with two instances, default schema should match the attributes of the individual instances - even if they differ" do matz = Person.find(1) - rick = Person.find(5) + rick = Person.find(6) m_attrs = matz.attributes.keys.sort r_attrs = rick.attributes.keys.sort @@ -376,7 +376,7 @@ class SchemaTest < ActiveModel::TestCase test "with two instances, known attributes should match the attributes of the individual instances - even if they differ" do matz = Person.find(1) - rick = Person.find(5) + rick = Person.find(6) m_attrs = matz.attributes.keys.sort r_attrs = rick.attributes.keys.sort diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb index 48dacbdf67..f45652d988 100644 --- a/activeresource/test/cases/base_test.rb +++ b/activeresource/test/cases/base_test.rb @@ -448,31 +448,31 @@ class BaseTest < Test::Unit::TestCase end def test_collection_path - assert_equal '/people.xml', Person.collection_path + assert_equal '/people.json', Person.collection_path end def test_collection_path_with_parameters - assert_equal '/people.xml?gender=male', Person.collection_path(:gender => 'male') - assert_equal '/people.xml?gender=false', Person.collection_path(:gender => false) - assert_equal '/people.xml?gender=', Person.collection_path(:gender => nil) + assert_equal '/people.json?gender=male', Person.collection_path(:gender => 'male') + assert_equal '/people.json?gender=false', Person.collection_path(:gender => false) + assert_equal '/people.json?gender=', Person.collection_path(:gender => nil) - assert_equal '/people.xml?gender=male', Person.collection_path('gender' => 'male') + assert_equal '/people.json?gender=male', Person.collection_path('gender' => 'male') # Use includes? because ordering of param hash is not guaranteed - assert Person.collection_path(:gender => 'male', :student => true).include?('/people.xml?') + assert Person.collection_path(:gender => 'male', :student => true).include?('/people.json?') assert Person.collection_path(:gender => 'male', :student => true).include?('gender=male') assert Person.collection_path(:gender => 'male', :student => true).include?('student=true') - assert_equal '/people.xml?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false]) + assert_equal '/people.json?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false]) - assert_equal '/people.xml?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => ActiveSupport::OrderedHash[:a, [2,1], 'b', 'fred']) + assert_equal '/people.json?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => ActiveSupport::OrderedHash[:a, [2,1], 'b', 'fred']) end def test_custom_element_path - assert_equal '/people/1/addresses/1.xml', StreetAddress.element_path(1, :person_id => 1) - assert_equal '/people/1/addresses/1.xml', StreetAddress.element_path(1, 'person_id' => 1) - assert_equal '/people/Greg/addresses/1.xml', StreetAddress.element_path(1, 'person_id' => 'Greg') - assert_equal '/people/ann%20mary/addresses/ann%20mary.xml', StreetAddress.element_path(:'ann mary', 'person_id' => 'ann mary') + assert_equal '/people/1/addresses/1.json', StreetAddress.element_path(1, :person_id => 1) + assert_equal '/people/1/addresses/1.json', StreetAddress.element_path(1, 'person_id' => 1) + assert_equal '/people/Greg/addresses/1.json', StreetAddress.element_path(1, 'person_id' => 'Greg') + assert_equal '/people/ann%20mary/addresses/ann%20mary.json', StreetAddress.element_path(:'ann mary', 'person_id' => 'ann mary') end def test_custom_element_path_without_required_prefix_param @@ -482,7 +482,7 @@ class BaseTest < Test::Unit::TestCase end def test_module_element_path - assert_equal '/sounds/1.xml', Asset::Sound.element_path(1) + assert_equal '/sounds/1.json', Asset::Sound.element_path(1) end def test_custom_element_path_with_redefined_to_param @@ -494,10 +494,10 @@ class BaseTest < Test::Unit::TestCase end # Class method. - assert_equal '/people/Greg.xml', Person.element_path('Greg') + assert_equal '/people/Greg.json', Person.element_path('Greg') # Protected Instance method. - assert_equal '/people/Greg.xml', Person.find('Greg').send(:element_path) + assert_equal '/people/Greg.json', Person.find('Greg').send(:element_path) ensure # revert back to original @@ -509,14 +509,14 @@ class BaseTest < Test::Unit::TestCase end def test_custom_element_path_with_parameters - assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :person_id => 1, :type => 'work') - assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, 'person_id' => 1, :type => 'work') - assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :type => 'work', :person_id => 1) - assert_equal '/people/1/addresses/1.xml?type%5B%5D=work&type%5B%5D=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time']) + assert_equal '/people/1/addresses/1.json?type=work', StreetAddress.element_path(1, :person_id => 1, :type => 'work') + assert_equal '/people/1/addresses/1.json?type=work', StreetAddress.element_path(1, 'person_id' => 1, :type => 'work') + assert_equal '/people/1/addresses/1.json?type=work', StreetAddress.element_path(1, :type => 'work', :person_id => 1) + assert_equal '/people/1/addresses/1.json?type%5B%5D=work&type%5B%5D=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time']) end def test_custom_element_path_with_prefix_and_parameters - assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, {:person_id => 1}, {:type => 'work'}) + assert_equal '/people/1/addresses/1.json?type=work', StreetAddress.element_path(1, {:person_id => 1}, {:type => 'work'}) end def test_custom_collection_path_without_required_prefix_param @@ -526,17 +526,17 @@ class BaseTest < Test::Unit::TestCase end def test_custom_collection_path - assert_equal '/people/1/addresses.xml', StreetAddress.collection_path(:person_id => 1) - assert_equal '/people/1/addresses.xml', StreetAddress.collection_path('person_id' => 1) + assert_equal '/people/1/addresses.json', StreetAddress.collection_path(:person_id => 1) + assert_equal '/people/1/addresses.json', StreetAddress.collection_path('person_id' => 1) end def test_custom_collection_path_with_parameters - assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path(:person_id => 1, :type => 'work') - assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path('person_id' => 1, :type => 'work') + assert_equal '/people/1/addresses.json?type=work', StreetAddress.collection_path(:person_id => 1, :type => 'work') + assert_equal '/people/1/addresses.json?type=work', StreetAddress.collection_path('person_id' => 1, :type => 'work') end def test_custom_collection_path_with_prefix_and_parameters - assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path({:person_id => 1}, {:type => 'work'}) + assert_equal '/people/1/addresses.json?type=work', StreetAddress.collection_path({:person_id => 1}, {:type => 'work'}) end def test_custom_element_name @@ -626,7 +626,7 @@ class BaseTest < Test::Unit::TestCase resp = {'Location' => '/foo/bar/1'} assert_equal '1', p.__send__(:id_from_response, resp) - resp['Location'] << '.xml' + resp['Location'] << '.json' assert_equal '1', p.__send__(:id_from_response, resp) end @@ -698,14 +698,14 @@ class BaseTest < Test::Unit::TestCase # Test that save exceptions get bubbled up too ActiveResource::HttpMock.respond_to do |mock| - mock.post "/people.xml", {}, nil, 409 + mock.post "/people.json", {}, nil, 409 end assert_raise(ActiveResource::ResourceConflict) { Person.create(:name => 'Rick') } end def test_create_without_location ActiveResource::HttpMock.respond_to do |mock| - mock.post "/people.xml", {}, nil, 201 + mock.post "/people.json", {}, nil, 201 end person = Person.create(:name => 'Rick') assert_nil person.id @@ -772,8 +772,8 @@ class BaseTest < Test::Unit::TestCase def test_update_conflict ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/2.xml", {}, @david - mock.put "/people/2.xml", @default_request_headers, nil, 409 + mock.get "/people/2.json", {}, @david + mock.put "/people/2.json", @default_request_headers, nil, 409 end assert_raise(ActiveResource::ResourceConflict) { Person.find(2).save } end @@ -830,7 +830,7 @@ class BaseTest < Test::Unit::TestCase def test_destroy assert Person.find(1).destroy ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, nil, 404 + mock.get "/people/1.json", {}, nil, 404 end assert_raise(ActiveResource::ResourceNotFound) { Person.find(1).destroy } end @@ -838,7 +838,7 @@ class BaseTest < Test::Unit::TestCase def test_destroy_with_custom_prefix assert StreetAddress.find(1, :params => { :person_id => 1 }).destroy ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1/addresses/1.xml", {}, nil, 404 + mock.get "/people/1/addresses/1.json", {}, nil, 404 end assert_raise(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) } end @@ -846,7 +846,7 @@ class BaseTest < Test::Unit::TestCase def test_destroy_with_410_gone assert Person.find(1).destroy ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, nil, 410 + mock.get "/people/1.json", {}, nil, 410 end assert_raise(ActiveResource::ResourceGone) { Person.find(1).destroy } end @@ -854,7 +854,7 @@ class BaseTest < Test::Unit::TestCase def test_delete assert Person.delete(1) ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, nil, 404 + mock.get "/people/1.json", {}, nil, 404 end assert_raise(ActiveResource::ResourceNotFound) { Person.find(1) } end @@ -862,7 +862,7 @@ class BaseTest < Test::Unit::TestCase def test_delete_with_custom_prefix assert StreetAddress.delete(1, :person_id => 1) ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1/addresses/1.xml", {}, nil, 404 + mock.get "/people/1/addresses/1.json", {}, nil, 404 end assert_raise(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) } end @@ -870,7 +870,7 @@ class BaseTest < Test::Unit::TestCase def test_delete_with_410_gone assert Person.delete(1) ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, nil, 410 + mock.get "/people/1.json", {}, nil, 410 end assert_raise(ActiveResource::ResourceGone) { Person.find(1) } end @@ -939,13 +939,14 @@ class BaseTest < Test::Unit::TestCase def test_exists_with_410_gone ActiveResource::HttpMock.respond_to do |mock| - mock.head "/people/1.xml", {}, nil, 410 + mock.head "/people/1.json", {}, nil, 410 end assert !Person.exists?(1) end def test_to_xml + Person.format = :xml matz = Person.find(1) encode = matz.encode xml = matz.to_xml @@ -954,9 +955,12 @@ class BaseTest < Test::Unit::TestCase assert xml.include?('<?xml version="1.0" encoding="UTF-8"?>') assert xml.include?('<name>Matz</name>') assert xml.include?('<id type="integer">1</id>') + ensure + Person.format = :json end def test_to_xml_with_element_name + Person.format = :xml old_elem_name = Person.element_name matz = Person.find(1) Person.element_name = 'ruby_creator' @@ -970,45 +974,45 @@ class BaseTest < Test::Unit::TestCase assert xml.include?('<id type="integer">1</id>') assert xml.include?('</ruby-creator>') ensure + Person.format = :json Person.element_name = old_elem_name end def test_to_xml_with_private_method_name_as_attribute + Person.format = :xml assert_nothing_raised(ArgumentError) { Customer.new(:test => true).to_xml } + ensure + Person.format = :json end def test_to_json Person.include_root_in_json = true - Person.format = :json joe = Person.find(6) encode = joe.encode json = joe.to_json - Person.format = :xml assert_equal encode, json - assert_match %r{^\{"person":\{"person":\{}, json + assert_match %r{^\{"person":\{}, json assert_match %r{"id":6}, json assert_match %r{"name":"Joe"}, json - assert_match %r{\}\}\}$}, json + assert_match %r{\}\}$}, json end def test_to_json_with_element_name old_elem_name = Person.element_name Person.include_root_in_json = true - Person.format = :json joe = Person.find(6) Person.element_name = 'ruby_creator' encode = joe.encode json = joe.to_json - Person.format = :xml assert_equal encode, json - assert_match %r{^\{"ruby_creator":\{"person":\{}, json + assert_match %r{^\{"ruby_creator":\{}, json assert_match %r{"id":6}, json assert_match %r{"name":"Joe"}, json - assert_match %r{\}\}\}$}, json + assert_match %r{\}\}$}, json ensure Person.element_name = old_elem_name end @@ -1043,19 +1047,22 @@ class BaseTest < Test::Unit::TestCase def test_load_yaml_array assert_nothing_raised do + Person.format = :xml marty = Person.find(5) assert_equal 3, marty.colors.size marty.colors.each do |color| assert_kind_of String, color end end + ensure + Person.format = :json end def test_with_custom_formatter - @addresses = [{:id => "1", :street => "1 Infinite Loop", :city => "Cupertino", :state => "CA"}].to_xml(:root => 'addresses') + addresses = [{ :id => "1", :street => "1 Infinite Loop", :city => "Cupertino", :state => "CA" }].to_xml(:root => :addresses) ActiveResource::HttpMock.respond_to do |mock| - mock.get "/addresses.xml", {}, @addresses, 200 + mock.get "/addresses.xml", {}, addresses, 200 end # late bind the site @@ -1066,10 +1073,10 @@ class BaseTest < Test::Unit::TestCase end def test_create_with_custom_primary_key - silver_plan = {:code => "silver", :price => 5.00}.to_xml(:root => "plan") + silver_plan = { :plan => { :code => "silver", :price => 5.00 } }.to_json ActiveResource::HttpMock.respond_to do |mock| - mock.post "/plans.xml", {}, silver_plan, 201, 'Location' => '/plans/silver.xml' + mock.post "/plans.json", {}, silver_plan, 201, 'Location' => '/plans/silver.json' end plan = SubscriptionPlan.new(:code => "silver", :price => 5.00) @@ -1080,12 +1087,12 @@ class BaseTest < Test::Unit::TestCase end def test_update_with_custom_primary_key - silver_plan = {:code => "silver", :price => 5.00}.to_xml(:root => "plan") - silver_plan_updated = {:code => "silver", :price => 10.00}.to_xml(:root => "plan") + silver_plan = { :plan => { :code => "silver", :price => 5.00 } }.to_json + silver_plan_updated = { :plan => { :code => "silver", :price => 10.00 } }.to_json ActiveResource::HttpMock.respond_to do |mock| - mock.get "/plans/silver.xml", {}, silver_plan - mock.put "/plans/silver.xml", {}, silver_plan_updated, 201, 'Location' => '/plans/silver.xml' + mock.get "/plans/silver.json", {}, silver_plan + mock.put "/plans/silver.json", {}, silver_plan_updated, 201, 'Location' => '/plans/silver.json' end plan = SubscriptionPlan.find("silver") @@ -1097,7 +1104,7 @@ class BaseTest < Test::Unit::TestCase plan.save! assert_equal 10.00, plan.price end - + def test_namespacing sound = Asset::Sound.find(1) assert_equal "Asset::Sound::Author", sound.author.class.to_s diff --git a/activeresource/test/connection_test.rb b/activeresource/test/cases/connection_test.rb index 7c36393cf2..09df0fb678 100644 --- a/activeresource/test/connection_test.rb +++ b/activeresource/test/cases/connection_test.rb @@ -5,29 +5,29 @@ class ConnectionTest < Test::Unit::TestCase def setup @conn = ActiveResource::Connection.new('http://localhost') - @matz = { :id => 1, :name => 'Matz' } - @david = { :id => 2, :name => 'David' } - @people = [ @matz, @david ].to_xml(:root => 'people') - @people_single = [ @matz ].to_xml(:root => 'people-single-elements') - @people_empty = [ ].to_xml(:root => 'people-empty-elements') - @matz = @matz.to_xml(:root => 'person') - @david = @david.to_xml(:root => 'person') - @header = {'key' => 'value'}.freeze - - @default_request_headers = { 'Content-Type' => 'application/xml' } + matz = { :person => { :id => 1, :name => 'Matz' } } + david = { :person => { :id => 2, :name => 'David' } } + @people = { :people => [ matz, david ] }.to_json + @people_single = { 'people-single-elements' => [ matz ] }.to_json + @people_empty = { 'people-empty-elements' => [ ] }.to_json + @matz = matz.to_json + @david = david.to_json + @header = { 'key' => 'value' }.freeze + + @default_request_headers = { 'Content-Type' => 'application/json' } ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/2.xml", @header, @david - mock.get "/people.xml", {}, @people - mock.get "/people_single_elements.xml", {}, @people_single - mock.get "/people_empty_elements.xml", {}, @people_empty - mock.get "/people/1.xml", {}, @matz - mock.put "/people/1.xml", {}, nil, 204 - mock.put "/people/2.xml", {}, @header, 204 - mock.delete "/people/1.xml", {}, nil, 200 - mock.delete "/people/2.xml", @header, nil, 200 - mock.post "/people.xml", {}, nil, 201, 'Location' => '/people/5.xml' - mock.post "/members.xml", {}, @header, 201, 'Location' => '/people/6.xml' - mock.head "/people/1.xml", {}, nil, 200 + mock.get "/people/2.json", @header, @david + mock.get "/people.json", {}, @people + mock.get "/people_single_elements.json", {}, @people_single + mock.get "/people_empty_elements.json", {}, @people_empty + mock.get "/people/1.json", {}, @matz + mock.put "/people/1.json", {}, nil, 204 + mock.put "/people/2.json", {}, @header, 204 + mock.delete "/people/1.json", {}, nil, 200 + mock.delete "/people/2.json", @header, nil, 200 + mock.post "/people.json", {}, nil, 201, 'Location' => '/people/5.json' + mock.post "/members.json", {}, @header, 201, 'Location' => '/people/6.json' + mock.head "/people/1.json", {}, nil, 200 end end @@ -120,64 +120,64 @@ class ConnectionTest < Test::Unit::TestCase end def test_get - matz = decode(@conn.get("/people/1.xml")) + matz = decode(@conn.get("/people/1.json")) assert_equal "Matz", matz["name"] end def test_head - response = @conn.head("/people/1.xml") + response = @conn.head("/people/1.json") assert response.body.blank? assert_equal 200, response.code end def test_get_with_header - david = decode(@conn.get("/people/2.xml", @header)) + david = decode(@conn.get("/people/2.json", @header)) assert_equal "David", david["name"] end def test_get_collection - people = decode(@conn.get("/people.xml")) - assert_equal "Matz", people[0]["name"] - assert_equal "David", people[1]["name"] + people = decode(@conn.get("/people.json")) + assert_equal "Matz", people[0]["person"]["name"] + assert_equal "David", people[1]["person"]["name"] end def test_get_collection_single - people = decode(@conn.get("/people_single_elements.xml")) - assert_equal "Matz", people[0]["name"] + people = decode(@conn.get("/people_single_elements.json")) + assert_equal "Matz", people[0]["person"]["name"] end def test_get_collection_empty - people = decode(@conn.get("/people_empty_elements.xml")) + people = decode(@conn.get("/people_empty_elements.json")) assert_equal [], people end def test_post - response = @conn.post("/people.xml") - assert_equal "/people/5.xml", response["Location"] + response = @conn.post("/people.json") + assert_equal "/people/5.json", response["Location"] end def test_post_with_header - response = @conn.post("/members.xml", @header) - assert_equal "/people/6.xml", response["Location"] + response = @conn.post("/members.json", @header) + assert_equal "/people/6.json", response["Location"] end def test_put - response = @conn.put("/people/1.xml") + response = @conn.put("/people/1.json") assert_equal 204, response.code end def test_put_with_header - response = @conn.put("/people/2.xml", @header) + response = @conn.put("/people/2.json", @header) assert_equal 204, response.code end def test_delete - response = @conn.delete("/people/1.xml") + response = @conn.delete("/people/1.json") assert_equal 200, response.code end def test_delete_with_header - response = @conn.delete("/people/2.xml", @header) + response = @conn.delete("/people/2.json", @header) assert_equal 200, response.code end @@ -185,7 +185,7 @@ class ConnectionTest < Test::Unit::TestCase @http = mock('new Net::HTTP') @conn.expects(:http).returns(@http) @http.expects(:get).raises(Timeout::Error, 'execution expired') - assert_raise(ActiveResource::TimeoutError) { @conn.get('/people_timeout.xml') } + assert_raise(ActiveResource::TimeoutError) { @conn.get('/people_timeout.json') } end def test_setting_timeout @@ -203,8 +203,8 @@ class ConnectionTest < Test::Unit::TestCase @http = mock('new Net::HTTP') @conn.expects(:http).returns(@http) path = '/people/1.xml' - @http.expects(:get).with(path, {'Accept' => 'application/xhtml+xml'}).returns(ActiveResource::Response.new(@matz, 200, {'Content-Type' => 'text/xhtml'})) - assert_nothing_raised(Mocha::ExpectationError) { @conn.get(path, {'Accept' => 'application/xhtml+xml'}) } + @http.expects(:get).with(path, { 'Accept' => 'application/xhtml+xml' }).returns(ActiveResource::Response.new(@matz, 200, { 'Content-Type' => 'text/xhtml' })) + assert_nothing_raised(Mocha::ExpectationError) { @conn.get(path, { 'Accept' => 'application/xhtml+xml' }) } end def test_ssl_options_get_applied_to_http @@ -222,7 +222,7 @@ class ConnectionTest < Test::Unit::TestCase http = Net::HTTP.new('') @conn.expects(:http).returns(http) http.expects(:get).raises(OpenSSL::SSL::SSLError, 'Expired certificate') - assert_raise(ActiveResource::SSLError) { @conn.get('/people/1.xml') } + assert_raise(ActiveResource::SSLError) { @conn.get('/people/1.json') } end def test_auth_type_can_be_string diff --git a/activeresource/test/cases/finder_test.rb b/activeresource/test/cases/finder_test.rb index ebb783996d..9c51f2a390 100644 --- a/activeresource/test/cases/finder_test.rb +++ b/activeresource/test/cases/finder_test.rb @@ -100,23 +100,23 @@ class FinderTest < Test::Unit::TestCase end def test_find_all_by_from - ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/people.xml", {}, @people_david } + ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/people.json", {}, @people_david } - people = Person.find(:all, :from => "/companies/1/people.xml") + people = Person.find(:all, :from => "/companies/1/people.json") assert_equal 1, people.size assert_equal "David", people.first.name end def test_find_all_by_from_with_options - ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/people.xml", {}, @people_david } + ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/people.json", {}, @people_david } - people = Person.find(:all, :from => "/companies/1/people.xml") + people = Person.find(:all, :from => "/companies/1/people.json") assert_equal 1, people.size assert_equal "David", people.first.name end def test_find_all_by_symbol_from - ActiveResource::HttpMock.respond_to { |m| m.get "/people/managers.xml", {}, @people_david } + ActiveResource::HttpMock.respond_to { |m| m.get "/people/managers.json", {}, @people_david } people = Person.find(:all, :from => :managers) assert_equal 1, people.size @@ -124,14 +124,14 @@ class FinderTest < Test::Unit::TestCase end def test_find_single_by_from - ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/manager.xml", {}, @david } + ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/manager.json", {}, @david } - david = Person.find(:one, :from => "/companies/1/manager.xml") + david = Person.find(:one, :from => "/companies/1/manager.json") assert_equal "David", david.name end def test_find_single_by_symbol_from - ActiveResource::HttpMock.respond_to { |m| m.get "/people/leader.xml", {}, @david } + ActiveResource::HttpMock.respond_to { |m| m.get "/people/leader.json", {}, @david } david = Person.find(:one, :from => :leader) assert_equal "David", david.name diff --git a/activeresource/test/cases/format_test.rb b/activeresource/test/cases/format_test.rb index f8d33f99fa..174142ec52 100644 --- a/activeresource/test/cases/format_test.rb +++ b/activeresource/test/cases/format_test.rb @@ -51,28 +51,30 @@ class FormatTest < Test::Unit::TestCase end def test_formats_on_custom_element_method - for format in [ :json, :xml ] + [:json, :xml].each do |format| using_format(Person, format) do + david = (format == :json ? { :person => @david } : @david) ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/2.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david) - mock.get "/people/2/shallow.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david) + mock.get "/people/2.#{format}", { 'Accept' => ActiveResource::Formats[format].mime_type }, ActiveResource::Formats[format].encode(david) + mock.get "/people/2/shallow.#{format}", { 'Accept' => ActiveResource::Formats[format].mime_type }, 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' }) + ryan_hash = { :name => 'Ryan' } + ryan_hash = (format == :json ? { :person => ryan_hash } : ryan_hash) + ryan = ActiveResource::Formats[format].encode(ryan_hash) using_format(Person, format) do remote_ryan = Person.new(:name => 'Ryan') - ActiveResource::HttpMock.respond_to.post "/people.#{format}", {'Content-Type' => ActiveResource::Formats[format].mime_type}, ryan, 201, {'Location' => "/people/5.#{format}"} + ActiveResource::HttpMock.respond_to.post "/people.#{format}", { 'Content-Type' => ActiveResource::Formats[format].mime_type}, ryan, 201, { 'Location' => "/people/5.#{format}" } remote_ryan.save remote_ryan = Person.new(:name => 'Ryan') - ActiveResource::HttpMock.respond_to.post "/people/new/register.#{format}", {'Content-Type' => ActiveResource::Formats[format].mime_type}, ryan, 201, {'Location' => "/people/5.#{format}"} - assert_equal ActiveResource::Response.new(ryan, 201, {'Location' => "/people/5.#{format}"}), remote_ryan.post(:register) + ActiveResource::HttpMock.respond_to.post "/people/new/register.#{format}", { 'Content-Type' => ActiveResource::Formats[format].mime_type}, ryan, 201, { 'Location' => "/people/5.#{format}" } + assert_equal ActiveResource::Response.new(ryan, 201, { 'Location' => "/people/5.#{format}" }), remote_ryan.post(:register) end end end diff --git a/activeresource/test/cases/http_mock_test.rb b/activeresource/test/cases/http_mock_test.rb index cd5155924a..d2fd911314 100644 --- a/activeresource/test/cases/http_mock_test.rb +++ b/activeresource/test/cases/http_mock_test.rb @@ -11,10 +11,10 @@ class HttpMockTest < ActiveSupport::TestCase [:post, :put, :get, :delete, :head].each do |method| test "responds to simple #{method} request" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(method, "/people/1", {FORMAT_HEADER[method] => "application/xml"}, "Response") + mock.send(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }, "Response") end - assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/xml").body + assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/json").body end test "adds format header by default to #{method} request" do @@ -22,7 +22,7 @@ class HttpMockTest < ActiveSupport::TestCase mock.send(method, "/people/1", {}, "Response") end - assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/xml").body + assert_equal "Response", request(method, "/people/1", FORMAT_HEADER[method] => "application/json").body end test "respond only when headers match header by default to #{method} request" do @@ -53,8 +53,8 @@ class HttpMockTest < ActiveSupport::TestCase test "responds correctly when format header is given to #{method} request" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(method, "/people/1", {FORMAT_HEADER[method] => "application/xml"}, "XML") - mock.send(method, "/people/1", {FORMAT_HEADER[method] => "application/json"}, "Json") + mock.send(method, "/people/1", { FORMAT_HEADER[method] => "application/xml" }, "XML") + mock.send(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }, "Json") end assert_equal "XML", request(method, "/people/1", FORMAT_HEADER[method] => "application/xml").body @@ -63,22 +63,22 @@ class HttpMockTest < ActiveSupport::TestCase test "raises InvalidRequestError if no response found for the #{method} request" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(method, "/people/1", {FORMAT_HEADER[method] => "application/xml"}, "XML") + mock.send(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }, "json") end assert_raise(::ActiveResource::InvalidRequestError) do - request(method, "/people/1", FORMAT_HEADER[method] => "application/json") + request(method, "/people/1", FORMAT_HEADER[method] => "application/xml") end end end test "allows you to send in pairs directly to the respond_to method" do - matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person") + matz = { :person => { :id => 1, :name => "Matz" } }.to_json - create_matz = ActiveResource::Request.new(:post, '/people.xml', matz, {}) - created_response = ActiveResource::Response.new("", 201, {"Location" => "/people/1.xml"}) - get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil) + create_matz = ActiveResource::Request.new(:post, '/people.json', matz, {}) + created_response = ActiveResource::Response.new("", 201, { "Location" => "/people/1.json" }) + get_matz = ActiveResource::Request.new(:get, '/people/1.json', nil) ok_response = ActiveResource::Response.new(matz, 200, {}) pairs = {create_matz => created_response, get_matz => ok_response} @@ -91,24 +91,24 @@ class HttpMockTest < ActiveSupport::TestCase test "resets all mocked responses on each call to respond_to with a block by default" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/1", {}, "XML1") + mock.send(:get, "/people/1", {}, "JSON1") end assert_equal 1, ActiveResource::HttpMock.responses.length ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/2", {}, "XML2") + mock.send(:get, "/people/2", {}, "JSON2") end assert_equal 1, ActiveResource::HttpMock.responses.length end test "resets all mocked responses on each call to respond_to by passing pairs by default" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/1", {}, "XML1") + mock.send(:get, "/people/1", {}, "JSON1") end assert_equal 1, ActiveResource::HttpMock.responses.length - matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person") - get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil) + matz = { :person => { :id => 1, :name => "Matz" } }.to_json + get_matz = ActiveResource::Request.new(:get, '/people/1.json', nil) ok_response = ActiveResource::Response.new(matz, 200, {}) ActiveResource::HttpMock.respond_to({get_matz => ok_response}) @@ -117,24 +117,24 @@ class HttpMockTest < ActiveSupport::TestCase test "allows you to add new responses to the existing responses by calling a block" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/1", {}, "XML1") + mock.send(:get, "/people/1", {}, "JSON1") end assert_equal 1, ActiveResource::HttpMock.responses.length ActiveResource::HttpMock.respond_to(false) do |mock| - mock.send(:get, "/people/2", {}, "XML2") + mock.send(:get, "/people/2", {}, "JSON2") end assert_equal 2, ActiveResource::HttpMock.responses.length end test "allows you to add new responses to the existing responses by passing pairs" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/1", {}, "XML1") + mock.send(:get, "/people/1", {}, "JSON1") end assert_equal 1, ActiveResource::HttpMock.responses.length - matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person") - get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil) + matz = { :person => { :id => 1, :name => "Matz" } }.to_json + get_matz = ActiveResource::Request.new(:get, '/people/1.json', nil) ok_response = ActiveResource::Response.new(matz, 200, {}) ActiveResource::HttpMock.respond_to({get_matz => ok_response}, false) @@ -143,23 +143,23 @@ class HttpMockTest < ActiveSupport::TestCase test "allows you to replace the existing reponse with the same request by calling a block" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/1", {}, "XML1") + mock.send(:get, "/people/1", {}, "JSON1") end assert_equal 1, ActiveResource::HttpMock.responses.length ActiveResource::HttpMock.respond_to(false) do |mock| - mock.send(:get, "/people/1", {}, "XML2") + mock.send(:get, "/people/1", {}, "JSON2") end assert_equal 1, ActiveResource::HttpMock.responses.length end test "allows you to replace the existing reponse with the same request by passing pairs" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/1", {}, "XML1") + mock.send(:get, "/people/1", {}, "JSON1") end assert_equal 1, ActiveResource::HttpMock.responses.length - matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person") + matz = { :person => { :id => 1, :name => "Matz" } }.to_json get_matz = ActiveResource::Request.new(:get, '/people/1', nil) ok_response = ActiveResource::Response.new(matz, 200, {}) @@ -169,19 +169,19 @@ class HttpMockTest < ActiveSupport::TestCase test "do not replace the response with the same path but different method by calling a block" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/1", {}, "XML1") + mock.send(:get, "/people/1", {}, "JSON1") end assert_equal 1, ActiveResource::HttpMock.responses.length ActiveResource::HttpMock.respond_to(false) do |mock| - mock.send(:put, "/people/1", {}, "XML2") + mock.send(:put, "/people/1", {}, "JSON2") end assert_equal 2, ActiveResource::HttpMock.responses.length end test "do not replace the response with the same path but different method by passing pairs" do ActiveResource::HttpMock.respond_to do |mock| - mock.send(:get, "/people/1", {}, "XML1") + mock.send(:get, "/people/1", {}, "JSON1") end assert_equal 1, ActiveResource::HttpMock.responses.length diff --git a/activeresource/test/cases/log_subscriber_test.rb b/activeresource/test/cases/log_subscriber_test.rb index 3cd96007db..b9143f5b67 100644 --- a/activeresource/test/cases/log_subscriber_test.rb +++ b/activeresource/test/cases/log_subscriber_test.rb @@ -10,9 +10,9 @@ class LogSubscriberTest < ActiveSupport::TestCase def setup super - @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') + @matz = { :person => { :id => 1, :name => 'Matz' } }.to_json ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, @matz + mock.get "/people/1.json", {}, @matz end ActiveResource::LogSubscriber.attach_to :active_resource @@ -26,7 +26,7 @@ class LogSubscriberTest < ActiveSupport::TestCase matz = Person.find(1) wait assert_equal 2, @logger.logged(:info).size - assert_equal "GET http://37s.sunrise.i:3000/people/1.xml", @logger.logged(:info)[0] - assert_match(/\-\-\> 200 200 106/, @logger.logged(:info)[1]) + assert_equal "GET http://37s.sunrise.i:3000/people/1.json", @logger.logged(:info)[0] + assert_match(/\-\-\> 200 200 33/, @logger.logged(:info)[1]) end end diff --git a/activeresource/test/cases/observing_test.rb b/activeresource/test/cases/observing_test.rb index 925ec7a84a..ca3ab5d03d 100644 --- a/activeresource/test/cases/observing_test.rb +++ b/activeresource/test/cases/observing_test.rb @@ -20,13 +20,13 @@ class ObservingTest < Test::Unit::TestCase end def setup - @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') + @matz = { 'person' => { :id => 1, :name => 'Matz' } }.to_json ActiveResource::HttpMock.respond_to do |mock| - mock.get "/people/1.xml", {}, @matz - mock.post "/people.xml", {}, @matz, 201, 'Location' => '/people/1.xml' - mock.put "/people/1.xml", {}, nil, 204 - mock.delete "/people/1.xml", {}, nil, 200 + mock.get "/people/1.json", {}, @matz + mock.post "/people.json", {}, @matz, 201, 'Location' => '/people/1.json' + mock.put "/people/1.json", {}, nil, 204 + mock.delete "/people/1.json", {}, nil, 200 end PersonObserver.instance diff --git a/activeresource/test/cases/validations_test.rb b/activeresource/test/cases/validations_test.rb index 3b1caecb04..c6c6e1d786 100644 --- a/activeresource/test/cases/validations_test.rb +++ b/activeresource/test/cases/validations_test.rb @@ -8,9 +8,9 @@ require 'active_support/core_ext/hash/conversions' class ValidationsTest < ActiveModel::TestCase VALID_PROJECT_HASH = { :name => "My Project", :description => "A project" } def setup - @my_proj = VALID_PROJECT_HASH.to_xml(:root => "person") + @my_proj = { "person" => VALID_PROJECT_HASH }.to_json ActiveResource::HttpMock.respond_to do |mock| - mock.post "/projects.xml", {}, @my_proj, 201, 'Location' => '/projects/5.xml' + mock.post "/projects.json", {}, @my_proj, 201, 'Location' => '/projects/5.json' end end diff --git a/activeresource/test/setter_trap.rb b/activeresource/test/setter_trap.rb index 437fbdad32..7cfd9ca111 100644 --- a/activeresource/test/setter_trap.rb +++ b/activeresource/test/setter_trap.rb @@ -1,5 +1,3 @@ -require 'abstract_unit' - class SetterTrap < ActiveSupport::BasicObject class << self def rollback_sets(obj) diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index a14f008be5..b937d4c50d 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -48,14 +48,17 @@ module ActiveSupport if log.respond_to?(:write) @log = log elsif File.exist?(log) - @log = open(log, (File::WRONLY | File::APPEND)) - @log.binmode - @log.sync = true + @log = open_log(log, (File::WRONLY | File::APPEND)) else FileUtils.mkdir_p(File.dirname(log)) - @log = open(log, (File::WRONLY | File::APPEND | File::CREAT)) - @log.binmode - @log.sync = true + @log = open_log(log, (File::WRONLY | File::APPEND | File::CREAT)) + end + end + + def open_log(log, mode) + open(log, mode).tap do |log| + log.set_encoding(Encoding::BINARY) if log.respond_to?(:set_encoding) + log.sync = true end end diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index ca3db2349e..ec475134ef 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -130,7 +130,6 @@ class Class # :nodoc: end def write_inheritable_attribute(key, value) - ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES) @inheritable_attributes = {} end @@ -148,7 +147,6 @@ class Class # :nodoc: end def read_inheritable_attribute(key) - ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE inheritable_attributes[key] end diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb index e73915ffcf..58a03d508e 100644 --- a/activesupport/lib/active_support/core_ext/numeric/time.rb +++ b/activesupport/lib/active_support/core_ext/numeric/time.rb @@ -1,4 +1,6 @@ require 'active_support/duration' +require 'active_support/core_ext/time/calculations' +require 'active_support/core_ext/time/acts_like' class Numeric # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb index 02cb5dfee7..9d044eba71 100644 --- a/activesupport/lib/active_support/core_ext/object/duplicable.rb +++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb @@ -1,3 +1,4 @@ +#-- # Most objects are cloneable, but not all. For example you can't dup +nil+: # # nil.dup # => TypeError: can't dup NilClass @@ -14,6 +15,7 @@ # # That's why we hardcode the following cases and check duplicable? instead of # using that rescue idiom. +#++ class Object # Can you safely dup this object? # diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index dcac17536a..00fda2b370 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -1,4 +1,5 @@ require 'active_support/duration' +require 'active_support/core_ext/time/zones' class Time COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 8ec4f6e09a..15a3717ea1 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -10,6 +10,10 @@ module ActiveSupport true end + def with_indifferent_access + dup + end + def initialize(constructor = {}) if constructor.is_a?(Hash) super() @@ -58,8 +62,12 @@ module ActiveSupport # hash_1.update(hash_2) # => {"key"=>"New Value!"} # def update(other_hash) - other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } - self + if other_hash.is_a? HashWithIndifferentAccess + super(other_hash) + else + other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } + self + end end alias_method :merge!, :update diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 7cd9bfa947..02c19448fd 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -1,470 +1,317 @@ -begin - require 'ruby-prof' - - require 'fileutils' - require 'rails/version' - require 'active_support/concern' - require 'active_support/core_ext/class/delegating_attributes' - require 'active_support/core_ext/string/inflections' - - module ActiveSupport - module Testing - module Performance - extend ActiveSupport::Concern - - included do - superclass_delegating_accessor :profile_options - self.profile_options = DEFAULTS - - if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions - include ForMiniTest - else - include ForClassicTestUnit - end +require 'fileutils' +require 'rails/version' +require 'active_support/concern' +require 'active_support/core_ext/class/delegating_attributes' +require 'active_support/core_ext/string/inflections' +require 'action_view/helpers/number_helper' + +module ActiveSupport + module Testing + module Performance + extend ActiveSupport::Concern + + included do + superclass_delegating_accessor :profile_options + self.profile_options = {} + + if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions + include ForMiniTest + else + include ForClassicTestUnit end + end + + # each implementation should define metrics and freeze the defaults + DEFAULTS = + if ARGV.include?('--benchmark') # HAX for rake test + { :runs => 4, + :output => 'tmp/performance', + :benchmark => true } + else + { :runs => 1, + :output => 'tmp/performance', + :benchmark => false } + end + + def full_profile_options + DEFAULTS.merge(profile_options) + end - module ForMiniTest - def run(runner) - @runner = runner - - run_warmup - if profile_options && metrics = profile_options[:metrics] - metrics.each do |metric_name| - if klass = Metrics[metric_name.to_sym] - run_profile(klass.new) - end + def full_test_name + "#{self.class.name}##{method_name}" + end + + module ForMiniTest + def run(runner) + @runner = runner + + run_warmup + if full_profile_options && metrics = full_profile_options[:metrics] + metrics.each do |metric_name| + if klass = Metrics[metric_name.to_sym] + run_profile(klass.new) end end end + + return + end - def run_test(metric, mode) - result = '.' + def run_test(metric, mode) + result = '.' + begin + run_callbacks :setup + setup + metric.send(mode) { __send__ method_name } + rescue Exception => e + result = @runner.puke(self.class, method_name, e) + ensure begin - run_callbacks :setup - setup - metric.send(mode) { __send__ method_name } + teardown + run_callbacks :teardown, :enumerator => :reverse_each rescue Exception => e result = @runner.puke(self.class, method_name, e) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each - rescue Exception => e - result = @runner.puke(self.class, method_name, e) - end end - result end + result end + end - module ForClassicTestUnit - def run(result) - return if method_name =~ /^default_test$/ + module ForClassicTestUnit + def run(result) + return if method_name =~ /^default_test$/ - yield(self.class::STARTED, name) - @_result = result + yield(self.class::STARTED, name) + @_result = result - run_warmup - if profile_options && metrics = profile_options[:metrics] - metrics.each do |metric_name| - if klass = Metrics[metric_name.to_sym] - run_profile(klass.new) - result.add_run - end + run_warmup + if full_profile_options && metrics = full_profile_options[:metrics] + metrics.each do |metric_name| + if klass = Metrics[metric_name.to_sym] + run_profile(klass.new) + result.add_run + else + puts '%20s: unsupported' % metric_name end end - - yield(self.class::FINISHED, name) end - def run_test(metric, mode) - run_callbacks :setup - setup - metric.send(mode) { __send__ @method_name } + yield(self.class::FINISHED, name) + end + + def run_test(metric, mode) + run_callbacks :setup + setup + metric.send(mode) { __send__ @method_name } + rescue ::Test::Unit::AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue StandardError, ScriptError => e + add_error(e) + ensure + begin + teardown + run_callbacks :teardown, :enumerator => :reverse_each rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) + add_failure(e.message, e.backtrace) rescue StandardError, ScriptError => e add_error(e) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each - rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue StandardError, ScriptError => e - add_error(e) - end end end + end - DEFAULTS = - if benchmark = ARGV.include?('--benchmark') # HAX for rake test - { :benchmark => true, - :runs => 4, - :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], - :output => 'tmp/performance' } - else - { :benchmark => false, - :runs => 1, - :min_percent => 0.01, - :metrics => [:process_time, :memory, :objects], - :formats => [:flat, :graph_html, :call_tree], - :output => 'tmp/performance' } - end.freeze - - def full_test_name - "#{self.class.name}##{method_name}" - end - - protected - def run_warmup - GC.start - - time = Metrics::Time.new - run_test(time, :benchmark) - puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] + protected + # overridden by each implementation + def run_gc; end + + def run_warmup + run_gc - GC.start - end + time = Metrics::Time.new + run_test(time, :benchmark) + puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] - def run_profile(metric) - klass = profile_options[:benchmark] ? Benchmarker : Profiler - performer = klass.new(self, metric) - - performer.run - puts performer.report - performer.record - end + run_gc + end + + def run_profile(metric) + klass = full_profile_options[:benchmark] ? Benchmarker : Profiler + performer = klass.new(self, metric) + + performer.run + puts performer.report + performer.record + end - class Performer - delegate :run_test, :profile_options, :full_test_name, :to => :@harness + class Performer + delegate :run_test, :full_profile_options, :full_test_name, :to => :@harness - def initialize(harness, metric) - @harness, @metric = harness, metric - end + def initialize(harness, metric) + @harness, @metric, @supported = harness, metric, false + end - def report - rate = @total / profile_options[:runs] + def report + if @supported + rate = @total / full_profile_options[:runs] '%20s: %s' % [@metric.name, @metric.format(rate)] + else + '%20s: unsupported' % @metric.name end - - protected - def output_filename - "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" - end end - class Benchmarker < Performer - def run - profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } - @total = @metric.total - end - - def record - avg = @metric.total / profile_options[:runs].to_i - now = Time.now.utc.xmlschema - with_output_file do |file| - file.puts "#{avg},#{now},#{environment}" - end - end - - def environment - unless defined? @env - app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - - rails = Rails::VERSION::STRING - if File.directory?('vendor/rails/.git') - Dir.chdir('vendor/rails') do - rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - end - end - - ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' - ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - - @env = [app, rails, ruby, RUBY_PLATFORM] * ',' - end - - @env + protected + def output_filename + "#{full_profile_options[:output]}/#{full_test_name}_#{@metric.name}" end - - protected - HEADER = 'measurement,created_at,app,rails,ruby,platform' - - def with_output_file - fname = output_filename - - if new = !File.exist?(fname) - FileUtils.mkdir_p(File.dirname(fname)) - end - - File.open(fname, 'ab') do |file| - file.puts(HEADER) if new - yield file - end - end - - def output_filename - "#{super}.csv" - end + end + + # overridden by each implementation + class Profiler < Performer + def time_with_block + before = Time.now + yield + Time.now - before end + + def run; end + def record; end + end - class Profiler < Performer - def initialize(*args) - super - @supported = @metric.measure_mode rescue false - end - - def run - return unless @supported - - RubyProf.measure_mode = @metric.measure_mode - RubyProf.start - RubyProf.pause - profile_options[:runs].to_i.times { run_test(@metric, :profile) } - @data = RubyProf.stop - @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } - end + class Benchmarker < Performer + def initialize(*args) + super + @supported = @metric.respond_to?('measure') + end + + def run + return unless @supported + + full_profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } + @total = @metric.total + end - def report - if @supported - super - else - '%20s: unsupported' % @metric.name - end + def record + avg = @metric.total / full_profile_options[:runs].to_i + now = Time.now.utc.xmlschema + with_output_file do |file| + file.puts "#{avg},#{now},#{environment}" end + end - def record - return unless @supported - - klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact + def environment + unless defined? @env + app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - klasses.each do |klass| - fname = output_filename(klass) - FileUtils.mkdir_p(File.dirname(fname)) - File.open(fname, 'wb') do |file| - klass.new(@data).print(file, profile_options.slice(:min_percent)) + rails = Rails::VERSION::STRING + if File.directory?('vendor/rails/.git') + Dir.chdir('vendor/rails') do + rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ end end - end - protected - def output_filename(printer_class) - suffix = - case printer_class.name.demodulize - when 'FlatPrinter'; 'flat.txt' - when 'FlatPrinterWithLineNumbers'; 'flat_line_numbers.txt' - when 'GraphPrinter'; 'graph.txt' - when 'GraphHtmlPrinter'; 'graph.html' - when 'GraphYamlPrinter'; 'graph.yml' - when 'CallTreePrinter'; 'tree.txt' - when 'CallStackPrinter'; 'stack.html' - when 'DotPrinter'; 'graph.dot' - else printer_class.name.sub(/Printer$/, '').underscore - end - - "#{super()}_#{suffix}" - end - end + ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' + ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - module Metrics - def self.[](name) - const_get(name.to_s.camelize) - rescue NameError - nil + @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end - class Base - attr_reader :total - - def initialize - @total = 0 - end + @env + end - def name - @name ||= self.class.name.demodulize.underscore - end + protected + HEADER = 'measurement,created_at,app,rails,ruby,platform' - def measure_mode - self.class::Mode - end + def with_output_file + fname = output_filename - def measure - 0 - end - - def benchmark - with_gc_stats do - before = measure - yield - @total += (measure - before) - end + if new = !File.exist?(fname) + FileUtils.mkdir_p(File.dirname(fname)) end - def profile - RubyProf.resume - yield - ensure - RubyProf.pause + File.open(fname, 'ab') do |file| + file.puts(HEADER) if new + yield file end - - protected - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) - def with_gc_stats - GC::Profiler.enable - GC.start - yield - ensure - GC::Profiler.disable - end - - # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) - elsif GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end - - else - def with_gc_stats - yield - end - end end - class Time < Base - def measure - ::Time.now.to_f - end - - def format(measurement) - if measurement < 1 - '%d ms' % (measurement * 1000) - else - '%.2f sec' % measurement - end - end + def output_filename + "#{super}.csv" end + end + + module Metrics + def self.[](name) + const_get(name.to_s.camelize) + rescue NameError + nil + end - class ProcessTime < Time - Mode = RubyProf::PROCESS_TIME + class Base + include ActionView::Helpers::NumberHelper + + attr_reader :total - def measure - RubyProf.measure_process_time - end + def initialize + @total = 0 end - class WallTime < Time - Mode = RubyProf::WALL_TIME - - def measure - RubyProf.measure_wall_time - end + def name + @name ||= self.class.name.demodulize.underscore end - class CpuTime < Time - Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) - - def initialize(*args) - # FIXME: yeah my CPU is 2.33 GHz - RubyProf.cpu_frequency = 2.33e9 unless RubyProf.cpu_frequency > 0 - super - end - - def measure - RubyProf.measure_cpu_time + def benchmark + with_gc_stats do + before = measure + yield + @total += (measure - before) end end - - class Memory < Base - Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) - - # Ruby 1.9 + GCdata patch - if GC.respond_to?(:malloc_allocated_size) - def measure - GC.malloc_allocated_size / 1024.0 - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_memory) - def measure - RubyProf.measure_memory / 1024.0 - end - end - - def format(measurement) - '%.2f KB' % measurement - end + + # overridden by each implementation + def profile; end + + protected + # overridden by each implementation + def with_gc_stats; end + end + + class Time < Base + def measure + ::Time.now.to_f end - class Objects < Base - Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) - - # Ruby 1.9 + GCdata patch - if GC.respond_to?(:malloc_allocations) - def measure - GC.malloc_allocations - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end - end - - def format(measurement) - measurement.to_i.to_s + def format(measurement) + if measurement < 1 + '%d ms' % (measurement * 1000) + else + '%.2f sec' % measurement end end - - class GcRuns < Base - Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - - # Ruby 1.9 - if GC.respond_to?(:count) - def measure - GC.count - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end - end - - def format(measurement) - measurement.to_i.to_s - end + end + + class Amount < Base + def format(measurement) + number_with_delimiter(measurement.floor) end - - class GcTime < Base - Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) - - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) - def measure - GC::Profiler.total_time - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time / 1000 - end - end - - def format(measurement) - '%.2f ms' % measurement - end + end + + class DigitalInformationUnit < Base + def format(measurement) + number_to_human_size(measurement, :precision => 2) end end + + # each implementation provides its own metrics like ProcessTime, Memory or GcRuns end end end -rescue LoadError +end + +RUBY_ENGINE = 'ruby' unless defined?(RUBY_ENGINE) # mri 1.8 +case RUBY_ENGINE + when 'ruby' then require 'active_support/testing/performance/ruby' + when 'rbx' then require 'active_support/testing/performance/rubinius' + when 'jruby' then require 'active_support/testing/performance/jruby' + else + $stderr.puts 'Your ruby interpreter is not supported for benchmarking.' + exit end diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb new file mode 100644 index 0000000000..6b27959840 --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -0,0 +1,115 @@ +require 'jruby/profiler' +require 'java' +import java.lang.management.ManagementFactory + +module ActiveSupport + module Testing + module Performance + DEFAULTS.merge!( + if ARGV.include?('--benchmark') + {:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]} + else + { :metrics => [:wall_time], + :formats => [:flat, :graph] } + end).freeze + + protected + def run_gc + ManagementFactory.memory_mx_bean.gc + end + + class Profiler < Performer + def initialize(*args) + super + @supported = @metric.is_a?(Metrics::WallTime) + end + + def run + return unless @supported + + @total = time_with_block do + @data = JRuby::Profiler.profile do + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + end + end + end + + def record + return unless @supported + + klasses = full_profile_options[:formats].map { |f| JRuby::Profiler.const_get("#{f.to_s.camelize}ProfilePrinter") }.compact + + klasses.each do |klass| + fname = output_filename(klass) + FileUtils.mkdir_p(File.dirname(fname)) + file = File.open(fname, 'wb') do |file| + klass.new(@data).printProfile(file) + end + end + end + + protected + def output_filename(printer_class) + suffix = + case printer_class.name.demodulize + when 'FlatProfilePrinter'; 'flat.txt' + when 'GraphProfilePrinter'; 'graph.txt' + else printer_class.name.sub(/ProfilePrinter$/, '').underscore + end + + "#{super()}_#{suffix}" + end + end + + module Metrics + class Base + def profile + yield + end + + protected + def with_gc_stats + ManagementFactory.memory_mx_bean.gc + yield + end + end + + class WallTime < Time + def measure + super + end + end + + class CpuTime < Time + def measure + ManagementFactory.thread_mx_bean.get_current_thread_cpu_time / 1000 / 1000 / 1000.0 # seconds + end + end + + class UserTime < Time + def measure + ManagementFactory.thread_mx_bean.get_current_thread_user_time / 1000 / 1000 / 1000.0 # seconds + end + end + + class Memory < DigitalInformationUnit + def measure + ManagementFactory.memory_mx_bean.non_heap_memory_usage.used + ManagementFactory.memory_mx_bean.heap_memory_usage.used + end + end + + class GcRuns < Amount + def measure + ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_runs, current_gc| total_runs += current_gc.collection_count } + end + end + + class GcTime < Time + def measure + ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_time, current_gc| total_time += current_gc.collection_time } / 1000.0 # seconds + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb new file mode 100644 index 0000000000..198d235548 --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -0,0 +1,113 @@ +require 'rubinius/agent' + +module ActiveSupport + module Testing + module Performance + DEFAULTS.merge!( + if ARGV.include?('--benchmark') + {:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]} + else + { :metrics => [:wall_time], + :formats => [:flat, :graph] } + end).freeze + + protected + def run_gc + GC.run(true) + end + + class Performer; end + + class Profiler < Performer + def initialize(*args) + super + @supported = @metric.is_a?(Metrics::WallTime) + end + + def run + return unless @supported + + @profiler = Rubinius::Profiler::Instrumenter.new + + @total = time_with_block do + @profiler.profile(false) do + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + end + end + end + + def record + return unless @supported + + if(full_profile_options[:formats].include?(:flat)) + create_path_and_open_file(:flat) do |file| + @profiler.show(file) + end + end + + if(full_profile_options[:formats].include?(:graph)) + create_path_and_open_file(:graph) do |file| + @profiler.show(file) + end + end + end + + protected + def create_path_and_open_file(printer_name) + fname = "#{output_filename}_#{printer_name}.txt" + FileUtils.mkdir_p(File.dirname(fname)) + File.open(fname, 'wb') do |file| + yield(file) + end + end + end + + module Metrics + class Base + attr_reader :loopback + + def profile + yield + end + + protected + def with_gc_stats + @loopback = Rubinius::Agent.loopback + GC.run(true) + yield + end + end + + class WallTime < Time + def measure + super + end + end + + class Memory < DigitalInformationUnit + def measure + loopback.get("system.memory.counter.bytes").last + end + end + + class Objects < Amount + def measure + loopback.get("system.memory.counter.objects").last + end + end + + class GcRuns < Amount + def measure + loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last + end + end + + class GcTime < Time + def measure + (loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last) / 1000.0 + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb new file mode 100644 index 0000000000..b29ec6719c --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -0,0 +1,152 @@ +begin + require 'ruby-prof' +rescue LoadError + $stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.' + exit +end + +module ActiveSupport + module Testing + module Performance + DEFAULTS.merge!( + if ARGV.include?('--benchmark') + { :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time] } + else + { :min_percent => 0.01, + :metrics => [:process_time, :memory, :objects], + :formats => [:flat, :graph_html, :call_tree, :call_stack] } + end).freeze + + protected + def run_gc + GC.start + end + + class Profiler < Performer + def initialize(*args) + super + @supported = @metric.measure_mode rescue false + end + + def run + return unless @supported + + RubyProf.measure_mode = @metric.measure_mode + RubyProf.start + RubyProf.pause + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + @data = RubyProf.stop + @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } + end + + def record + return unless @supported + + klasses = full_profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact + + klasses.each do |klass| + fname = output_filename(klass) + FileUtils.mkdir_p(File.dirname(fname)) + File.open(fname, 'wb') do |file| + klass.new(@data).print(file, full_profile_options.slice(:min_percent)) + end + end + end + + protected + def output_filename(printer_class) + suffix = + case printer_class.name.demodulize + when 'FlatPrinter'; 'flat.txt' + when 'FlatPrinterWithLineNumbers'; 'flat_line_numbers.txt' + when 'GraphPrinter'; 'graph.txt' + when 'GraphHtmlPrinter'; 'graph.html' + when 'GraphYamlPrinter'; 'graph.yml' + when 'CallTreePrinter'; 'tree.txt' + when 'CallStackPrinter'; 'stack.html' + when 'DotPrinter'; 'graph.dot' + else printer_class.name.sub(/Printer$/, '').underscore + end + + "#{super()}_#{suffix}" + end + end + + module Metrics + class Base + def measure_mode + self.class::Mode + end + + def profile + RubyProf.resume + yield + ensure + RubyProf.pause + end + + protected + # overridden by each implementation + def with_gc_stats + yield + end + end + + class ProcessTime < Time + Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME) + + def measure + RubyProf.measure_process_time + end + end + + class WallTime < Time + Mode = RubyProf::WALL_TIME if RubyProf.const_defined?(:WALL_TIME) + + def measure + RubyProf.measure_wall_time + end + end + + class CpuTime < Time + Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) + + def initialize(*args) + # FIXME: yeah my CPU is 2.33 GHz + RubyProf.cpu_frequency = 2.33e9 unless RubyProf.cpu_frequency > 0 + super + end + + def measure + RubyProf.measure_cpu_time + end + end + + class Memory < DigitalInformationUnit + Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) + end + + class Objects < Amount + Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) + end + + class GcRuns < Amount + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + end + + class GcTime < Time + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + end + end + end + end +end + +if RUBY_VERSION.between?('1.9.2', '2.0') + require 'active_support/testing/performance/ruby/yarv' +elsif RUBY_VERSION.between?('1.8.6', '1.9') + require 'active_support/testing/performance/ruby/mri' +else + $stderr.puts 'Update your ruby interpreter to be able to run benchmarks.' + exit +end diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb new file mode 100644 index 0000000000..86e650050b --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/ruby/mri.rb @@ -0,0 +1,59 @@ +module ActiveSupport + module Testing + module Performance + module Metrics + class Base + protected + # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) + if GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + GC.start + yield + ensure + GC.disable_stats + end + end + end + + class Memory < DigitalInformationUnit + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory + end + end + end + + class Objects < Amount + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations + end + end + end + + class GcRuns < Amount + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs + end + end + end + + class GcTime < Time + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time / 1000.0 / 1000.0 + end + end + end + end + end + end +end + + diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb new file mode 100644 index 0000000000..62095a8fe4 --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -0,0 +1,57 @@ +module ActiveSupport + module Testing + module Performance + module Metrics + class Base + protected + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) + def with_gc_stats + GC::Profiler.enable + GC.start + yield + ensure + GC::Profiler.disable + end + end + end + + class Memory < DigitalInformationUnit + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocated_size) + def measure + GC.malloc_allocated_size + end + end + end + + class Objects < Amount + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocations) + def measure + GC.malloc_allocations + end + end + end + + class GcRuns < Amount + # Ruby 1.9 + if GC.respond_to?(:count) + def measure + GC.count + end + end + end + + class GcTime < Time + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) + def measure + GC::Profiler.total_time + end + end + end + end + end + end +end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 4557a10688..0b3f18faec 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -971,6 +971,12 @@ class HashToXmlTest < Test::Unit::TestCase assert_nil hash_wia.default end + def test_should_return_dup_for_with_indifferent_access + hash_wia = HashWithIndifferentAccess.new + assert_equal hash_wia, hash_wia.with_indifferent_access + assert_not_same hash_wia, hash_wia.with_indifferent_access + end + def test_should_copy_the_default_value_when_converting_to_hash_with_indifferent_access hash = Hash.new(3) hash_wia = hash.with_indifferent_access diff --git a/activesupport/test/option_merger_test.rb b/activesupport/test/option_merger_test.rb index 5b2e16a212..2bdd3034e5 100644 --- a/activesupport/test/option_merger_test.rb +++ b/activesupport/test/option_merger_test.rb @@ -66,11 +66,11 @@ class OptionMergerTest < Test::Unit::TestCase end end - def test_nested_method_with_options_using_lamdba - local_lamdba = lambda { { :lambda => true } } + def test_nested_method_with_options_using_lambda + local_lambda = lambda { { :lambda => true } } with_options(@options) do |o| - assert_equal @options.merge(local_lamdba.call), - o.method_with_options(local_lamdba).call + assert_equal @options.merge(local_lambda.call), + o.method_with_options(local_lambda).call end end diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb index 911655e0f4..5dcac8e74c 100644 --- a/railties/guides/source/layout.html.erb +++ b/railties/guides/source/layout.html.erb @@ -27,7 +27,7 @@ <a href="http://rubyonrails.org/">Overview</a> | <a href="http://rubyonrails.org/download">Download</a> | <a href="http://rubyonrails.org/deploy">Deploy</a> | - <a href="http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/overview">Code</a> | + <a href="https://github.com/rails/rails">Code</a> | <a href="http://rubyonrails.org/screencasts">Screencasts</a> | <a href="http://rubyonrails.org/documentation">Documentation</a> | <a href="http://rubyonrails.org/ecosystem">Ecosystem</a> | diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index 27f8a9303e..d60b68ec7f 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -386,7 +386,7 @@ class ExampleMigration < ActiveRecord::Migration end </ruby> -Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise +IrreversibleMigration+ from your +down+ method. If someone tries to revert your migration an error message will be displayed saying that it can't be done. +Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise +ActiveRecord::IrreversibleMigration+ from your +down+ method. If someone tries to revert your migration an error message will be displayed saying that it can't be done. h3. Running Migrations diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index 5679bae531..2b79237c59 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -4,7 +4,7 @@ This guide covers the various ways of performance testing a Ruby on Rails applic * Understand the various types of benchmarking and profiling metrics * Generate performance and benchmarking tests -* Use a GC-patched Ruby binary to measure memory usage and object allocation +* Install and use a GC-patched Ruby binary to measure memory usage and object allocation * Understand the benchmarking information provided by Rails inside the log files * Learn about various tools facilitating benchmarking and profiling @@ -23,7 +23,7 @@ require 'test_helper' require 'rails/performance_test_help' # Profiling results for each test method are written to tmp/performance. -class BrowsingTest < ActionController::PerformanceTest +class BrowsingTest < ActionDispatch::PerformanceTest def test_homepage get '/' end @@ -34,10 +34,10 @@ This example is a simple performance test case for profiling a GET request to th h4. Generating Performance Tests -Rails provides a generator called +test_unit:performance+ for creating new performance tests: +Rails provides a generator called +performance_test+ for creating new performance tests: <shell> -$ rails generate test_unit:performance homepage +$ rails generate performance_test homepage </shell> This generates +homepage_test.rb+ in the +test/performance+ directory: @@ -46,7 +46,7 @@ This generates +homepage_test.rb+ in the +test/performance+ directory: require 'test_helper' require 'rails/performance_test_help' -class HomepageTest < ActionController::PerformanceTest +class HomepageTest < ActionDispatch::PerformanceTest # Replace this with your real tests. def test_homepage get '/' @@ -105,7 +105,7 @@ Here's the performance test for +HomeController#dashboard+ and +PostsController# require 'test_helper' require 'rails/performance_test_help' -class PostPerformanceTest < ActionController::PerformanceTest +class PostPerformanceTest < ActionDispatch::PerformanceTest def setup # Application requires logged-in user login_as(:lifo) @@ -133,7 +133,7 @@ Performance test for +Post+ model: require 'test_helper' require 'rails/performance_test_help' -class PostModelTest < ActionController::PerformanceTest +class PostModelTest < ActionDispatch::PerformanceTest def test_creation Post.create :body => 'still fooling you', :cost => '100' end @@ -151,7 +151,7 @@ Performance tests can be run in two modes: Benchmarking and Profiling. h5. Benchmarking -Benchmarking helps find out how fast each performance test runs. Each test case is run +4 times+ in benchmarking mode. +Benchmarking makes it easy to quickly gather a few metrics about each test tun. By default, each test case is run +4 times+ in benchmarking mode. To run performance tests in benchmarking mode: @@ -161,7 +161,7 @@ $ rake test:benchmark h5. Profiling -Profiling helps you see the details of a performance test and provide an in-depth picture of the slow and memory hungry parts. Each test case is run +1 time+ in profiling mode. +Profiling allows you to make an in-depth analysis of each of your tests by using an external profiler. Depending on your Ruby interpreter, this profiler can be native (Rubinius, JRuby) or not (MRI, which uses RubyProf). By default, each test case is run +1 time+ in profiling mode. To run performance tests in profiling mode: @@ -171,43 +171,59 @@ $ rake test:profile h4. Metrics -Benchmarking and profiling run performance tests in various modes described below. +Benchmarking and profiling run performance tests and give you multiple metrics. The availability of each metric is determined by the interpreter being used—none of them support all metrics—and by the mode in use. A brief description of each metric and their availability across interpreters/modes is given below. h5. Wall Time Wall time measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system. -Mode: Benchmarking - h5. Process Time Process time measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load. -Mode: Profiling +h5. CPU Time + +Similar to process time, but leverages the more accurate CPU clock counter available on the Pentium and PowerPC platforms. + +h5. User Time + +User time measures the amount of time the CPU spent in user-mode, i.e. within the process. This is not affected by other processes and by the time it possibly spends blocked. h5. Memory Memory measures the amount of memory used for the performance test case. -Mode: Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby - h5. Objects Objects measures the number of objects allocated for the performance test case. -Mode: Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby - h5. GC Runs GC Runs measures the number of times GC was invoked for the performance test case. -Mode: Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby - h5. GC Time GC Time measures the amount of time spent in GC for the performance test case. -Mode: Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby +h5. Metric Availability + +h6. Benchmarking + +|_.Interpreter|_.Wall Time|_.Process Time|_.CPU Time|_.User Time|_.Memory|_.Objects|_.GC Runs|_.GC Time| +|_.MRI | yes | yes | yes | no | yes | yes | yes | yes | +|_.REE | yes | yes | yes | no | yes | yes | yes | yes | +|_.Rubinius | yes | no | no | no | yes | yes | yes | yes | +|_.JRuby | yes | no | no | yes | yes | yes | yes | yes | + +h6. Profiling + +|_.Interpreter|_.Wall Time|_.Process Time|_.CPU Time|_.User Time|_.Memory|_.Objects|_.GC Runs|_.GC Time| +|_.MRI | yes | yes | no | no | yes | yes | yes | yes | +|_.REE | yes | yes | no | no | yes | yes | yes | yes | +|_.Rubinius | yes | no | no | no | no | no | no | no | +|_.JRuby | yes | no | no | no | no | no | no | no | + +NOTE: To profile under JRuby you'll need to run +export JRUBY_OPTS="-Xlaunch.inproc=false --profile.api"+ *before* the performance tests. h4. Understanding the Output @@ -215,7 +231,7 @@ Performance tests generate different outputs inside +tmp/performance+ directory h5(#output-benchmarking). Benchmarking -In benchmarking mode, performance tests generate two types of outputs: +In benchmarking mode, performance tests generate two types of outputs. h6(#output-command-line). Command Line @@ -225,7 +241,7 @@ This is the primary form of output in benchmarking mode. Example: BrowsingTest#test_homepage (31 ms warmup) wall_time: 6 ms memory: 437.27 KB - objects: 5514 + objects: 5,514 gc_runs: 0 gc_time: 19 ms </shell> @@ -260,7 +276,7 @@ measurement,created_at,app,rails,ruby,platform h5(#output-profiling). Profiling -In profiling mode, you can choose from four types of output. +In profiling mode, performance tests can generate multiple types of outputs. The command line output is always presented but support for the others is dependant on the interpreter in use. A brief description of each type and their availability across interpreters is given below. h6. Command Line @@ -270,26 +286,67 @@ This is a very basic form of output in profiling mode: BrowsingTest#test_homepage (58 ms warmup) process_time: 63 ms memory: 832.13 KB - objects: 7882 + objects: 7,882 </shell> h6. Flat -Flat output shows the total amount of time spent in each method. "Check ruby prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/flat_txt.html. +Flat output shows the metric—time, memory, etc—measure in each method. "Check Ruby-Prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/flat_txt.html. h6. Graph -Graph output shows how long each method takes to run, which methods call it and which methods it calls. "Check ruby prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/graph_txt.html. +Graph output shows the metric measure in each method, which methods call it and which methods it calls. "Check Ruby-Prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/graph_txt.html. h6. Tree Tree output is profiling information in calltree format for use by "kcachegrind":http://kcachegrind.sourceforge.net/html/Home.html and similar tools. +h6. Output Availability + +|_. |_.Flat|_.Graph|_.Tree| +|_.MRI | yes | yes | yes | +|_.REE | yes | yes | yes | +|_.Rubinius | yes | yes | no | +|_.JRuby | yes | yes | no | + h4. Tuning Test Runs -By default, each performance test is run +4 times+ in benchmarking mode and +1 time+ in profiling. However, test runs can easily be configured. +Test runs can be tuned by setting the +profile_options+ class variable on your test class. -WARNING: Performance test configurability is not yet enabled in Rails. But it will be soon. +<ruby> +require 'test_helper' +require 'rails/performance_test_help' + +# Profiling results for each test method are written to tmp/performance. +class BrowsingTest < ActionDispatch::PerformanceTest + self.profile_options = { :runs => 5, + :metrics => [:wall_time, :memory] } + + def test_homepage + get '/' + end +end +</ruby> + +In this example, the test would run 5 times and measure wall time and memory. There are a few configurable options: + +|_.Option |_.Description|_.Default|_.Mode| +|+:runs+ |Number of runs.|Benchmarking: 4, Profiling: 1|Both| +|+:output+ |Directory to use when writing the results.|+tmp/performance+|Both| +|+:metrics+ |Metrics to use.|See below.|Both| +|+:formats+ |Formats to output to.|See below.|Profiling| + +Metrics and formats have different defaults depending on the interpreter in use. + +|_.Interpreter|_.Mode|_.Default metrics|_.Default formats| +|/2.MRI/REE |Benchmarking|+[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| +|Profiling |+[:process_time, :memory, :objects]+|+[:flat, :graph_html, :call_tree, :call_stack]+| +|/2.Rubinius|Benchmarking|+[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| +|Profiling |+[:wall_time]+|+[:flat, :graph]+| +|/2.JRuby |Benchmarking|+[:wall_time, :user_time, :memory, :gc_runs, :gc_time]+|N/A| +|Profiling |+[:wall_time]+|+[:flat, :graph]+| + +As you've probably noticed by now, metrics and formats are specified using a symbol array with each name "underscored.":http://api.rubyonrails.org/classes/String.html#method-i-underscore h4. Performance Test Environment @@ -303,41 +360,71 @@ Rails.logger.level = ActiveSupport::BufferedLogger::INFO As +ActionController::Base.perform_caching+ is set to +true+, performance tests will behave much as they do in the +production+ environment. -h4. Installing GC-Patched Ruby +h4. Installing GC-Patched MRI + +To get the best from Rails' performance tests under MRI, you'll need to build a special Ruby binary with some super powers. -To get the best from Rails performance tests, you need to build a special Ruby binary with some super powers - "GC patch":http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch for measuring GC Runs/Time and memory/object allocation. +The recommended patches for each MRI version are: -The process is fairly straightforward. If you've never compiled a Ruby binary before, follow these steps to build a ruby binary inside your home directory: +|_.Version|_.Patch| +|1.8.6|ruby186gc| +|1.8.7|ruby187gc| +|1.9.2 and above|gcdata| -h5. Installation +All of these can be found on "RVM's _patches_ directory":https://github.com/wayneeseguin/rvm/tree/master/patches/ruby under each specific interpreter version. -Compile Ruby and apply this "GC Patch":http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch. +Concerning the installation itself, you can either do this easily by using "RVM":http://rvm.beginrescueend.com or you can build everything from source, which is a little bit harder. -h5. Download and Extract +h5. Install Using RVM + +The process of installing a patched Ruby interpreter is very easy if you let RVM do the hard work. All of the following RVM commands will provide you with a patched Ruby interpreter: + +<shell> +$ rvm install 1.9.2-p180 --patch gcdata +$ rvm install 1.8.7 --patch ruby187gc +$ rvm install 1.9.2-p180 --patch ~/Downloads/downloaded_gcdata_patch.patch +</shell> + +You can even keep your regular interpreter by assigning a name to the patched one: + +<shell> +$ rvm install 1.9.2-p180 --patch gcdata --name gcdata +$ rvm use 1.9.2-p180 # your regular ruby +$ rvm use 1.9.2-p180-gcdata # your patched ruby +</shell> + +And it's done! You have installed a patched Ruby interpreter. + +h5. Install From Source + +This process is a bit more complicated, but straightforward nonetheless. If you've never compiled a Ruby binary before, follow these steps to build a Ruby binary inside your home directory. + +h6. Download and Extract <shell> $ mkdir rubygc -$ wget <download the latest stable ruby from ftp://ftp.ruby-lang.org/pub/ruby> +$ wget <the version you want from ftp://ftp.ruby-lang.org/pub/ruby> $ tar -xzvf <ruby-version.tar.gz> $ cd <ruby-version> </shell> -h5. Apply the Patch +h6. Apply the Patch <shell> -$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0 +$ curl http://github.com/wayneeseguin/rvm/raw/master/patches/ruby/1.9.2/p180/gcdata.patch | patch -p0 # if you're on 1.9.2! +$ curl http://github.com/wayneeseguin/rvm/raw/master/patches/ruby/1.8.7/ruby187gc.patch | patch -p0 # if you're on 1.8.7! </shell> -h5. Configure and Install +h6. Configure and Install -The following will install ruby in your home directory's +/rubygc+ directory. Make sure to replace +<homedir>+ with a full patch to your actual home directory. +The following will install Ruby in your home directory's +/rubygc+ directory. Make sure to replace +<homedir>+ with a full patch to your actual home directory. <shell> $ ./configure --prefix=/<homedir>/rubygc $ make && make install </shell> -h5. Prepare Aliases +h6. Prepare Aliases For convenience, add the following lines in your +~/.profile+: @@ -349,26 +436,21 @@ alias gcirb='~/rubygc/bin/irb' alias gcrails='~/rubygc/bin/rails' </shell> -h5. Install Rubygems and Dependency Gems +Don't forget to use your aliases from now on. -Download "Rubygems":http://rubyforge.org/projects/rubygems and install it from source. Rubygem's README file should have necessary installation instructions. +h6. Install Rubygems (1.8 only!) -Additionally, install the following gems: +Download "Rubygems":http://rubyforge.org/projects/rubygems and install it from source. Rubygem's README file should have necessary installation instructions. Please note that this step isn't necessary if you've installed Ruby 1.9 and above. -* +rake+ -* +rails+ -* +ruby-prof+ -* +rack+ -* +mysql+ +h4. Using Ruby-Prof on MRI and REE -If installing +mysql+ fails, you can try to install it manually: +Add Ruby-Prof to your applications' Gemfile if you want to benchmark/profile under MRI or REE: -<shell> -$ gcruby extconf.rb --with-mysql-config -$ make && make install -</shell> +<ruby> +gem 'ruby-prof', :path => 'git://github.com/wycats/ruby-prof.git' +</ruby> -And you're ready to go. Don't forget to use +gcruby+ and +gcrake+ aliases when running the performance tests. +Now run +bundle install+ and you're ready to go. h3. Command Line Tools @@ -376,55 +458,47 @@ Writing performance test cases could be an overkill when you are looking for one h4. +benchmarker+ -+benchmarker+ is a wrapper around Ruby's "Benchmark":http://ruby-doc.org/core/classes/Benchmark.html standard library. - Usage: <shell> -$ rails benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ... -</shell> - -Examples: - -<shell> -$ rails benchmarker 10 'Item.all' 'CouchItem.all' +Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS] + -r, --runs N Number of runs. + Default: 4 + -o, --output PATH Directory to use when writing the results. + Default: tmp/performance + -m, --metrics a,b,c Metrics to use. + Default: wall_time,memory,objects,gc_runs,gc_time </shell> -If the +[times]+ argument is omitted, supplied methods are run just once: +Example: <shell> -$ rails benchmarker 'Item.first' 'Item.last' +$ rails benchmarker 'Item.all' 'CouchItem.all' --runs 3 --metrics wall_time,memory </shell> h4. +profiler+ -+profiler+ is a wrapper around the "ruby-prof":http://ruby-prof.rubyforge.org gem. - Usage: <shell> -$ rails profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html] +Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS] + -r, --runs N Number of runs. + Default: 1 + -o, --output PATH Directory to use when writing the results. + Default: tmp/performance + --metrics a,b,c Metrics to use. + Default: process_time,memory,objects + -m, --formats x,y,z Formats to output to. + Default: flat,graph_html,call_tree </shell> -Examples: +Example: <shell> -$ rails profiler 'Item.all' +$ rails profiler 'Item.all' 'CouchItem.all' --runs 2 --metrics process_time --formats flat </shell> -This will profile +Item.all+ in +RubyProf::WALL_TIME+ measure mode. By default, it prints flat output to the shell. - -<shell> -$ rails profiler 'Item.all' 10 graph -</shell> - -This will profile +10.times { Item.all }+ with +RubyProf::WALL_TIME+ measure mode and print graph output to the shell. - -If you want to store the output in a file: - -<shell> -$ rails profiler 'Item.all' 10 graph 2> graph.txt -</shell> +NOTE: Metrics and formats vary from interpreter to interpreter. Pass +--help+ to each tool to see the defaults for your interpreter. h3. Helper Methods @@ -517,12 +591,13 @@ h4. Tutorials and Documentation h3. Commercial Products -Rails has been lucky to have two startups dedicated to Rails specific performance tools: +Rails has been lucky to have a few companies dedicated to Rails-specific performance tools. A couple of those are: * "New Relic":http://www.newrelic.com * "Scout":http://scoutapp.com h3. Changelog +* March 30, 2011: Documented the recent improvements (multiple interpreters, options, etc) and necessary adjustments. Other minor improvements. "Gonçalo Silva":http://goncalossilva.com. * January 9, 2009: Complete rewrite by "Pratik":credits.html#lifo * September 6, 2008: Initial version by Matthew Bergman diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 1e4d25f18c..17d772b269 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -195,6 +195,7 @@ module Rails end def initialize_console(sandbox=false) + require "pp" require "rails/console/app" require "rails/console/helpers" end diff --git a/railties/lib/rails/commands/benchmarker.rb b/railties/lib/rails/commands/benchmarker.rb index f230f405c0..b06c915ac3 100644 --- a/railties/lib/rails/commands/benchmarker.rb +++ b/railties/lib/rails/commands/benchmarker.rb @@ -1,25 +1,34 @@ -require 'active_support/core_ext/object/inclusion' +require 'optparse' +require 'rails/test_help' +require 'rails/performance_test_help' -if ARGV.first.in?([nil, "-h", "--help"]) - puts "Usage: rails benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..." - exit 1 -end +ARGV.push('--benchmark') # HAX +require 'active_support/testing/performance' +ARGV.pop -begin - N = Integer(ARGV.first) - ARGV.shift -rescue ArgumentError - N = 1 +def options + options = {} + defaults = ActiveSupport::Testing::Performance::DEFAULTS + + OptionParser.new do |opt| + opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]" + opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r } + opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o } + opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) } + opt.parse!(ARGV) + end + + options end -require 'benchmark' -include Benchmark - -# Don't include compilation in the benchmark -ARGV.each { |expression| eval(expression) } - -bm(6) do |x| - ARGV.each_with_index do |expression, idx| - x.report("##{idx + 1}") { N.times { eval(expression) } } +class BenchmarkerTest < ActionDispatch::PerformanceTest + self.profile_options = options + + ARGV.each do |expression| + eval <<-RUBY + def test_#{expression.parameterize('_')} + #{expression} + end + RUBY end end diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb index 7959d8a981..94cf32d32d 100644 --- a/railties/lib/rails/commands/profiler.rb +++ b/railties/lib/rails/commands/profiler.rb @@ -1,48 +1,32 @@ -require 'active_support/core_ext/object/inclusion' +require 'optparse' +require 'rails/test_help' +require 'rails/performance_test_help' +require 'active_support/testing/performance' -if ARGV.first.in?([nil, "-h", "--help"]) - $stderr.puts "Usage: rails profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]" - exit(1) -end - -# Define a method to profile. -if ARGV[1] and ARGV[1].to_i > 1 - eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end" -else - eval "def profile_me() #{ARGV[0]} end" +def options + options = {} + defaults = ActiveSupport::Testing::Performance::DEFAULTS + + OptionParser.new do |opt| + opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]" + opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r } + opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o } + opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) } + opt.on('-f', '--formats x,y,z', Array, 'Formats to output to.', "Default: #{defaults[:formats].join(",")}") { |m| options[:formats] = m.map(&:to_sym) } + opt.parse!(ARGV) + end + + options end -# Use the ruby-prof extension if available. Fall back to stdlib profiler. -begin - begin - require "ruby-prof" - $stderr.puts 'Using the ruby-prof extension.' - RubyProf.measure_mode = RubyProf::WALL_TIME - RubyProf.start - profile_me - results = RubyProf.stop - if ARGV[2] - printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify) - else - printer_class = RubyProf::FlatPrinter - end - printer = printer_class.new(results) - printer.print($stderr) - rescue LoadError - require "prof" - $stderr.puts 'Using the old ruby-prof extension.' - Prof.clock_mode = Prof::GETTIMEOFDAY - Prof.start - profile_me - results = Prof.stop - require 'rubyprof_ext' - Prof.print_profile(results, $stderr) +class ProfilerTest < ActionDispatch::PerformanceTest + self.profile_options = options + + ARGV.each do |expression| + eval <<-RUBY + def test_#{expression.parameterize('_')} + #{expression} + end + RUBY end -rescue LoadError - require 'profiler' - $stderr.puts 'Using the standard Ruby profiler.' - Profiler__.start_profile - profile_me - Profiler__.stop_profile - Profiler__.print_profile($stderr) end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 6a125685d0..e26f73ed8d 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -522,9 +522,9 @@ module Rails end initializer :append_assets_path do |app| - app.config.assets.paths.unshift *paths["vendor/assets"].existent - app.config.assets.paths.unshift *paths["lib/assets"].existent - app.config.assets.paths.unshift *paths["app/assets"].existent + app.config.assets.paths.unshift(*paths["vendor/assets"].existent) + app.config.assets.paths.unshift(*paths["lib/assets"].existent) + app.config.assets.paths.unshift(*paths["app/assets"].existent) end initializer :prepend_helpers_path do |app| diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index c323df3e95..d31a3262e3 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -278,7 +278,7 @@ module Rails say args.first.to_s unless options.quiet? else args << (self.behavior == :invoke ? :green : :red) - say_status *args + say_status(*args) end end diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index a5743762e5..8512b1ca4a 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -165,7 +165,7 @@ module Rails end def gem_for_ruby_debugger - if RUBY_VERSION < "1.9.2" + if RUBY_VERSION < "1.9" "gem 'ruby-debug'" else "gem 'ruby-debug19', :require => 'ruby-debug'" @@ -173,7 +173,7 @@ module Rails end def gem_for_turn - unless RUBY_VERSION < "1.9.2" + unless RUBY_VERSION < "1.9.2" || options[:skip_test_unit] <<-GEMFILE.strip_heredoc group :test do # Pretty printed test output diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb index d12b2ff0e5..32546936e3 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb @@ -11,7 +11,7 @@ </div> <%% end %> -<% for attribute in attributes -%> +<% attributes.each do |attribute| -%> <div class="field"> <%%= f.label :<%= attribute.name %> %><br /> <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb index a7393cfe18..7b1a2a1841 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb @@ -2,7 +2,7 @@ <table> <tr> -<% for attribute in attributes -%> +<% attributes.each do |attribute| -%> <th><%= attribute.human_name %></th> <% end -%> <th></th> @@ -12,7 +12,7 @@ <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> <tr> -<% for attribute in attributes -%> +<% attributes.each do |attribute| -%> <td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td> <% end -%> <td><%%= link_to 'Show', <%= singular_table_name %> %></td> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb index c0e5ccff1e..67f263efbb 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb @@ -1,6 +1,6 @@ <p id="notice"><%%= notice %></p> -<% for attribute in attributes -%> +<% attributes.each do |attribute| -%> <p> <b><%= attribute.human_name %>:</b> <%%= @<%= singular_table_name %>.<%= attribute.name %> %> diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index cf317eb21f..7e7f8d2d08 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -13,6 +13,7 @@ module Rails :desc => "Force using old style hash (:foo => 'bar') on Ruby >= 1.9" def initialize(args, *options) #:nodoc: + @inside_template = nil # Unfreeze name in case it's given as a frozen string args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen? super diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml index c0c3588be1..4807986333 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml @@ -2,7 +2,10 @@ # # Get the bindings: # gem install ruby-frontbase - +# +# Configure Using Gemfile +# gem 'ruby-frontbase' +# development: adapter: frontbase host: localhost diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml index df5ef33064..3d689a110a 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml @@ -28,6 +28,9 @@ # On Windows: # Issue the command: gem install ibm_db # +# Configure Using Gemfile +# gem 'ibm_db' +# # For more details on the installation and the connection parameters below, # please refer to the latest documents at http://rubyforge.org/docman/?group_id=2361 diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml index ca807c9f3f..6bf83e86a5 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml @@ -3,6 +3,9 @@ # Install the MySQL driver: # gem install activerecord-jdbcmysql-adapter # +# Configure Using Gemfile +# gem 'activerecord-jdbcmysql-adapter' +# # And be sure to use new-style password hashing: # http://dev.mysql.com/doc/refman/5.0/en/old-client.html development: diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml index a228aca5d2..0c7f45322b 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml @@ -8,6 +8,10 @@ # gem install pg # Choose the win32 build. # Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem 'activerecord-jdbcpostgresql-adapter' + development: adapter: jdbcpostgresql encoding: unicode diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml index 30776b3b4e..6d241d57ae 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml @@ -1,6 +1,9 @@ # SQLite version 3.x # gem 'activerecord-jdbcsqlite3-adapter' - +# +# Configure Using Gemfile +# gem 'activerecord-jdbcsqlite3-adapter' +# development: adapter: jdbcsqlite3 database: db/development.sqlite3 diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml index 5d28c7c312..cce166c7c3 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml @@ -1,8 +1,11 @@ # MySQL. Versions 4.1 and 5.0 are recommended. -# -# Install the MySQL driver: +# +# Install the MYSQL driver # gem install mysql2 # +# Ensure the MySQL gem is defined in your Gemfile +# gem 'mysql2' +# # And be sure to use new-style password hashing: # http://dev.mysql.com/doc/refman/5.0/en/old-client.html development: diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml index 4e6391e3d6..467dfc3956 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml @@ -8,6 +8,10 @@ # gem install pg # Choose the win32 build. # Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem 'pg' +# development: adapter: postgresql encoding: unicode diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml index 90d87cc295..51a4dd459d 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml @@ -1,5 +1,8 @@ # SQLite version 3.x # gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' development: adapter: sqlite3 database: db/development.sqlite3 diff --git a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt index 9a2efa68a7..f75c5dd941 100644 --- a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb.tt @@ -4,4 +4,4 @@ # Examples: # # cities = City.create([{ <%= key_value :name, "'Chicago'" %> }, { <%= key_value :name, "'Copenhagen'" %> }]) -# Mayor.create(<%= key_value :name, "'Daley'" %>, <%= key_value :city, "cities.first" %>) +# Mayor.create(<%= key_value :name, "'Emanuel'" %>, <%= key_value :city, "cities.first" %>) diff --git a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb index 867fc8c985..5d1be041a5 100644 --- a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb +++ b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb @@ -1,8 +1,11 @@ require 'test_helper' require 'rails/performance_test_help' -# Profiling results for each test method are written to tmp/performance. class BrowsingTest < ActionDispatch::PerformanceTest + # Refer to the documentation for all available options + # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] + # :output => 'tmp/performance', :formats => [:flat] } + def test_homepage get '/' end diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb index c61ea4b510..8f5f78556f 100644 --- a/railties/lib/rails/generators/rails/controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb @@ -1,6 +1,6 @@ <% module_namespacing do -%> class <%= class_name %>Controller < ApplicationController -<% for action in actions -%> +<% actions.each do |action| -%> def <%= action %> end diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 939c0cd727..9ddb3cae33 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -75,6 +75,7 @@ task :default => :test def generate_test_dummy(force = false) opts = (options || {}).slice(*PASSTHROUGH_OPTIONS) opts[:force] = force + opts[:skip_bundle] = true invoke Rails::Generators::AppGenerator, [ File.expand_path(dummy_path, destination_root) ], opts diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb index 0bc5fd8ca2..509bd60564 100644 --- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb @@ -7,7 +7,7 @@ class <%= class_name %>ControllerTest < ActionController::TestCase # assert true # end <% else -%> -<% for action in actions -%> +<% actions.each do |action| -%> test "should get <%= action %>" do get :<%= action %> assert_response :success diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb index c05102290c..7e204105a3 100644 --- a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb @@ -2,7 +2,7 @@ require 'test_helper' <% module_namespacing do -%> class <%= class_name %>Test < ActionMailer::TestCase -<% for action in actions -%> +<% actions.each do |action| -%> test "<%= action %>" do mail = <%= class_name %>.<%= action %> assert_equal <%= action.to_s.humanize.inspect %>, mail.subject diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml index 6465a6a6e2..d4138ca2f5 100644 --- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml +++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml @@ -2,12 +2,12 @@ <% unless attributes.empty? -%> one: -<% for attribute in attributes -%> +<% attributes.each do |attribute| -%> <%= attribute.name %>: <%= attribute.default %> <% end -%> two: -<% for attribute in attributes -%> +<% attributes.each do |attribute| -%> <%= attribute.name %>: <%= attribute.default %> <% end -%> <% else -%> @@ -20,4 +20,4 @@ one: {} # two: {} # column: value -<% end -%>
\ No newline at end of file +<% end -%> diff --git a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb index e827aa918f..14a878328b 100644 --- a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb +++ b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb @@ -2,7 +2,10 @@ require 'test_helper' require 'rails/performance_test_help' class <%= class_name %>Test < ActionDispatch::PerformanceTest - # Replace this with your real tests. + # Refer to the documentation for all available options + # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] + # :output => 'tmp/performance', :formats => [:flat] } + def test_homepage get '/' end diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index b03dc3132b..98a702f134 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -11,6 +11,10 @@ module ApplicationTests boot_rails end + def app + @app ||= Rails.application + end + test "assets routes have higher priority" do app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();" @@ -20,8 +24,36 @@ module ApplicationTests end RUBY + require "#{app_path}/config/environment" + + get "/assets/demo.js" + assert_match "alert()", last_response.body + end + + test "does not stream session cookies back" do + app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();" + + app_file "config/routes.rb", <<-RUBY + AppTemplate::Application.routes.draw do + match '/omg', :to => "omg#index" + end + RUBY + + require "#{app_path}/config/environment" + + class ::OmgController < ActionController::Base + def index + flash[:cool_story] = true + render :text => "ok" + end + end + + get "/omg" + assert_equal 'ok', last_response.body + get "/assets/demo.js" assert_match "alert()", last_response.body + assert_equal nil, last_response.headers["Set-Cookie"] end end end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 43876c0a72..0e27c9606d 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -452,7 +452,7 @@ module ApplicationTests app_file 'app/models/post.rb', <<-RUBY class Post - def self.column_names + def self.attribute_names %w(title) end end diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 68d4c17623..597746c4aa 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -118,8 +118,8 @@ class ActionsTest < Rails::Generators::TestCase end assert_file 'config/application.rb' do |content| - assert_match /# This will be added/, content - assert_no_match /# This wont be added/, content + assert_match(/# This will be added/, content) + assert_no_match(/# This wont be added/, content) end end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 9e1d47cd2f..42fe7a7fea 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -155,7 +155,7 @@ class AppGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "--skip-active-record"] assert_no_file "config/database.yml" assert_file "test/test_helper.rb" do |helper_content| - assert_no_match /fixtures :all/, helper_content + assert_no_match(/fixtures :all/, helper_content) end assert_file "test/performance/browsing_test.rb" end @@ -178,7 +178,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_match %r{^//= require jquery_ujs}, contents end assert_file 'Gemfile' do |contents| - assert_match /^gem 'jquery-rails'/, contents + assert_match(/^gem 'jquery-rails'/, contents) end end @@ -190,7 +190,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_match %r{^//= require prototype_ujs}, contents end assert_file 'Gemfile' do |contents| - assert_match /^gem 'prototype-rails'/, contents + assert_match(/^gem 'prototype-rails'/, contents) end end @@ -202,9 +202,38 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_inclusion_of_turn_gem_in_gemfile + run_generator + assert_file "Gemfile" do |contents| + assert_match(/gem 'turn'/, contents) unless RUBY_VERSION < '1.9.2' + assert_no_match(/gem 'turn'/, contents) if RUBY_VERSION < '1.9.2' + end + end + + def test_turn_gem_is_not_included_in_gemfile_if_skipping_test_unit + run_generator [destination_root, "--skip-test-unit"] + assert_file "Gemfile" do |contents| + assert_no_match(/gem 'turn'/, contents) unless RUBY_VERSION < '1.9.2' + end + end + + def test_inclusion_of_ruby_debug + run_generator + assert_file "Gemfile" do |contents| + assert_match(/gem 'ruby-debug'/, contents) if RUBY_VERSION < '1.9' + end + end + + def test_inclusion_of_ruby_debug19_if_ruby19 + run_generator + assert_file "Gemfile" do |contents| + assert_match(/gem 'ruby-debug19', :require => 'ruby-debug'/, contents) unless RUBY_VERSION < '1.9' + end + end + def test_template_from_dir_pwd FileUtils.cd(Rails.root) - assert_match /It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"]) + assert_match(/It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"])) end def test_usage_read_from_file @@ -214,7 +243,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_default_usage File.expects(:exist?).returns(false) - assert_match /Create rails files for app generator/, Rails::Generators::AppGenerator.desc + assert_match(/Create rails files for app generator/, Rails::Generators::AppGenerator.desc) end def test_default_namespace @@ -241,9 +270,9 @@ class AppGeneratorTest < Rails::Generators::TestCase run_generator [destination_root] assert_file "config/initializers/session_store.rb" do |file| if RUBY_VERSION < "1.9" - assert_match /config.session_store :cookie_store, :key => '_.+_session'/, file + assert_match(/config.session_store :cookie_store, :key => '_.+_session'/, file) else - assert_match /config.session_store :cookie_store, key: '_.+_session'/, file + assert_match(/config.session_store :cookie_store, key: '_.+_session'/, file) end end end @@ -251,7 +280,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_force_old_style_hash run_generator [destination_root, "--old-style-hash"] assert_file "config/initializers/session_store.rb" do |file| - assert_match /config.session_store :cookie_store, :key => '_.+_session'/, file + assert_match(/config.session_store :cookie_store, :key => '_.+_session'/, file) end end diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb index 2dfc91c683..46533b70be 100644 --- a/railties/test/generators/controller_generator_test.rb +++ b/railties/test/generators/controller_generator_test.rb @@ -9,7 +9,7 @@ class ControllerGeneratorTest < Rails::Generators::TestCase def test_help_does_not_show_invoked_generators_options_if_they_already_exist content = run_generator ["--help"] - assert_no_match /Helper options\:/, content + assert_no_match(/Helper options\:/, content) end def test_controller_skeleton_is_created @@ -20,7 +20,7 @@ class ControllerGeneratorTest < Rails::Generators::TestCase def test_check_class_collision Object.send :const_set, :ObjectController, Class.new content = capture(:stderr){ run_generator ["object"] } - assert_match /The name 'ObjectController' is either already used in your application or reserved/, content + assert_match(/The name 'ObjectController' is either already used in your application or reserved/, content) ensure Object.send :remove_const, :ObjectController end diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb index f0bfebd57f..8da3aa61a4 100644 --- a/railties/test/generators/helper_generator_test.rb +++ b/railties/test/generators/helper_generator_test.rb @@ -20,17 +20,17 @@ class HelperGeneratorTest < Rails::Generators::TestCase def test_logs_if_the_test_framework_cannot_be_found content = run_generator ["admin", "--test-framework=rspec"] - assert_match /rspec \[not found\]/, content + assert_match(/rspec \[not found\]/, content) end def test_check_class_collision content = capture(:stderr){ run_generator ["object"] } - assert_match /The name 'ObjectHelper' is either already used in your application or reserved/, content + assert_match(/The name 'ObjectHelper' is either already used in your application or reserved/, content) end def test_check_class_collision_on_tests content = capture(:stderr){ run_generator ["another_object"] } - assert_match /The name 'AnotherObjectHelperTest' is either already used in your application or reserved/, content + assert_match(/The name 'AnotherObjectHelperTest' is either already used in your application or reserved/, content) end def test_namespaced_and_not_namespaced_helpers diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb index bf1cfe5305..139d6b1421 100644 --- a/railties/test/generators/mailer_generator_test.rb +++ b/railties/test/generators/mailer_generator_test.rb @@ -9,11 +9,11 @@ class MailerGeneratorTest < Rails::Generators::TestCase def test_mailer_skeleton_is_created run_generator assert_file "app/mailers/notifier.rb" do |mailer| - assert_match /class Notifier < ActionMailer::Base/, mailer + assert_match(/class Notifier < ActionMailer::Base/, mailer) if RUBY_VERSION < "1.9" - assert_match /default :from => "from@example.com"/, mailer + assert_match(/default :from => "from@example.com"/, mailer) else - assert_match /default from: "from@example.com"/, mailer + assert_match(/default from: "from@example.com"/, mailer) end end end @@ -21,35 +21,35 @@ class MailerGeneratorTest < Rails::Generators::TestCase def test_mailer_with_i18n_helper run_generator assert_file "app/mailers/notifier.rb" do |mailer| - assert_match /en\.notifier\.foo\.subject/, mailer - assert_match /en\.notifier\.bar\.subject/, mailer + assert_match(/en\.notifier\.foo\.subject/, mailer) + assert_match(/en\.notifier\.bar\.subject/, mailer) end end def test_check_class_collision content = capture(:stderr){ run_generator ["object"] } - assert_match /The name 'Object' is either already used in your application or reserved/, content + assert_match(/The name 'Object' is either already used in your application or reserved/, content) end def test_invokes_default_test_framework run_generator assert_file "test/functional/notifier_test.rb" do |test| - assert_match /class NotifierTest < ActionMailer::TestCase/, test - assert_match /test "foo"/, test - assert_match /test "bar"/, test + assert_match(/class NotifierTest < ActionMailer::TestCase/, test) + assert_match(/test "foo"/, test) + assert_match(/test "bar"/, test) end end def test_invokes_default_template_engine run_generator assert_file "app/views/notifier/foo.text.erb" do |view| - assert_match %r(app/views/notifier/foo\.text\.erb), view - assert_match /<%= @greeting %>/, view + assert_match(%r(app/views/notifier/foo\.text\.erb), view) + assert_match(/<%= @greeting %>/, view) end assert_file "app/views/notifier/bar.text.erb" do |view| - assert_match %r(app/views/notifier/bar\.text\.erb), view - assert_match /<%= @greeting %>/, view + assert_match(%r(app/views/notifier/bar\.text\.erb), view) + assert_match(/<%= @greeting %>/, view) end end @@ -60,14 +60,14 @@ class MailerGeneratorTest < Rails::Generators::TestCase def test_logs_if_the_template_engine_cannot_be_found content = run_generator ["notifier", "foo", "bar", "--template-engine=haml"] - assert_match /haml \[not found\]/, content + assert_match(/haml \[not found\]/, content) end def test_mailer_with_namedspaced_mailer run_generator ["Farm::Animal", "moos"] assert_file "app/mailers/farm/animal.rb" do |mailer| - assert_match /class Farm::Animal < ActionMailer::Base/, mailer - assert_match /en\.farm\.animal\.moos\.subject/, mailer + assert_match(/class Farm::Animal < ActionMailer::Base/, mailer) + assert_match(/en\.farm\.animal\.moos\.subject/, mailer) end assert_file "app/views/farm/animal/moos.text.erb" end @@ -78,20 +78,20 @@ class MailerGeneratorTest < Rails::Generators::TestCase assert_file "app/mailers/notifier.rb" do |mailer| assert_instance_method :foo, mailer do |foo| if RUBY_VERSION < "1.9" - assert_match /mail :to => "to@example.org"/, foo + assert_match(/mail :to => "to@example.org"/, foo) else - assert_match /mail to: "to@example.org"/, foo + assert_match(/mail to: "to@example.org"/, foo) end - assert_match /@greeting = "Hi"/, foo + assert_match(/@greeting = "Hi"/, foo) end assert_instance_method :bar, mailer do |bar| if RUBY_VERSION < "1.9" - assert_match /mail :to => "to@example.org"/, bar + assert_match(/mail :to => "to@example.org"/, bar) else - assert_match /mail to: "to@example.org"/, bar + assert_match(/mail to: "to@example.org"/, bar) end - assert_match /@greeting = "Hi"/, bar + assert_match(/@greeting = "Hi"/, bar) end end end @@ -99,10 +99,10 @@ class MailerGeneratorTest < Rails::Generators::TestCase def test_force_old_style_hash run_generator ["notifier", "foo", "--old-style-hash"] assert_file "app/mailers/notifier.rb" do |mailer| - assert_match /default :from => "from@example.com"/, mailer + assert_match(/default :from => "from@example.com"/, mailer) assert_instance_method :foo, mailer do |foo| - assert_match /mail :to => "to@example.org"/, foo + assert_match(/mail :to => "to@example.org"/, foo) end end end diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb index 6eecfc8e2e..337257df7d 100644 --- a/railties/test/generators/migration_generator_test.rb +++ b/railties/test/generators/migration_generator_test.rb @@ -35,8 +35,8 @@ class MigrationGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :change, content do |up| - assert_match /add_column :posts, :title, :string/, up - assert_match /add_column :posts, :body, :text/, up + assert_match(/add_column :posts, :title, :string/, up) + assert_match(/add_column :posts, :body, :text/, up) end end end @@ -47,13 +47,13 @@ class MigrationGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :up, content do |up| - assert_match /remove_column :posts, :title/, up - assert_match /remove_column :posts, :body/, up + assert_match(/remove_column :posts, :title/, up) + assert_match(/remove_column :posts, :body/, up) end assert_method :down, content do |down| - assert_match /add_column :posts, :title, :string/, down - assert_match /add_column :posts, :body, :text/, down + assert_match(/add_column :posts, :title, :string/, down) + assert_match(/add_column :posts, :body, :text/, down) end end end @@ -64,11 +64,11 @@ class MigrationGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :up, content do |up| - assert_match /^\s*$/, up + assert_match(/^\s*$/, up) end assert_method :down, content do |down| - assert_match /^\s*$/, down + assert_match(/^\s*$/, down) end end end diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 3d773b4134..8c5ba9926b 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -7,14 +7,14 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_help_shows_invoked_generators_options content = run_generator ["--help"] - assert_match /ActiveRecord options:/, content - assert_match /TestUnit options:/, content + assert_match(/ActiveRecord options:/, content) + assert_match(/TestUnit options:/, content) end def test_model_with_missing_attribute_type content = capture(:stderr) { run_generator ["post", "title:string", "body"] } - assert_match /Missing type for attribute 'body'/, content - assert_match /Example: 'body:string' where string is the type/, content + assert_match(/Missing type for attribute 'body'/, content) + assert_match(/Example: 'body:string' where string is the type/, content) end def test_invokes_default_orm @@ -100,9 +100,9 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/create_products.rb" do |m| assert_method :change, m do |up| - assert_match /create_table :products/, up - assert_match /t\.string :name/, up - assert_match /t\.integer :supplier_id/, up + assert_match(/create_table :products/, up) + assert_match(/t\.string :name/, up) + assert_match(/t\.integer :supplier_id/, up) end end end @@ -138,7 +138,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/create_accounts.rb" do |m| assert_method :change, m do |up| - assert_no_match /t.timestamps/, up + assert_no_match(/t.timestamps/, up) end end end @@ -164,7 +164,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_migration_error_is_not_shown_on_revoke run_generator error = capture(:stderr){ run_generator ["Account"], :behavior => :revoke } - assert_no_match /Another migration is already named create_accounts/, error + assert_no_match(/Another migration is already named create_accounts/, error) end def test_migration_is_removed_on_revoke @@ -177,7 +177,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase run_generator old_migration = Dir["#{destination_root}/db/migrate/*_create_accounts.rb"].first error = capture(:stderr) { run_generator ["Account", "--force"] } - assert_no_match /Another migration is already named create_accounts/, error + assert_no_match(/Another migration is already named create_accounts/, error) assert_no_file old_migration assert_migration 'db/migrate/create_accounts.rb' end @@ -195,13 +195,13 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_fixture_is_skipped_if_fixture_replacement_is_given content = run_generator ["account", "-r", "factory_girl"] - assert_match /factory_girl \[not found\]/, content + assert_match(/factory_girl \[not found\]/, content) assert_no_file "test/fixtures/accounts.yml" end def test_check_class_collision content = capture(:stderr){ run_generator ["object"] } - assert_match /The name 'Object' is either already used in your application or reserved/, content + assert_match(/The name 'Object' is either already used in your application or reserved/, content) end def test_index_is_added_for_belongs_to_association @@ -209,7 +209,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/create_accounts.rb" do |m| assert_method :change, m do |up| - assert_match /add_index/, up + assert_match(/add_index/, up) end end end @@ -219,7 +219,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/create_accounts.rb" do |m| assert_method :change, m do |up| - assert_match /add_index/, up + assert_match(/add_index/, up) end end end @@ -229,7 +229,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/create_accounts.rb" do |m| assert_method :change, m do |up| - assert_no_match /add_index/, up + assert_no_match(/add_index/, up) end end end @@ -239,9 +239,8 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/create_accounts.rb" do |m| assert_method :change, m do |up| - assert_no_match /add_index/, up + assert_no_match(/add_index/, up) end end end - end diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb index 38f024f061..6f8a9b4280 100644 --- a/railties/test/generators/namespaced_generators_test.rb +++ b/railties/test/generators/namespaced_generators_test.rb @@ -161,12 +161,12 @@ class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase def test_mailer_skeleton_is_created run_generator assert_file "app/mailers/test_app/notifier.rb" do |mailer| - assert_match /module TestApp/, mailer - assert_match /class Notifier < ActionMailer::Base/, mailer + assert_match(/module TestApp/, mailer) + assert_match(/class Notifier < ActionMailer::Base/, mailer) if RUBY_VERSION < "1.9" - assert_match /default :from => "from@example.com"/, mailer + assert_match(/default :from => "from@example.com"/, mailer) else - assert_match /default from: "from@example.com"/, mailer + assert_match(/default from: "from@example.com"/, mailer) end end end @@ -174,31 +174,31 @@ class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase def test_mailer_with_i18n_helper run_generator assert_file "app/mailers/test_app/notifier.rb" do |mailer| - assert_match /en\.notifier\.foo\.subject/, mailer - assert_match /en\.notifier\.bar\.subject/, mailer + assert_match(/en\.notifier\.foo\.subject/, mailer) + assert_match(/en\.notifier\.bar\.subject/, mailer) end end def test_invokes_default_test_framework run_generator assert_file "test/functional/test_app/notifier_test.rb" do |test| - assert_match /module TestApp/, test - assert_match /class NotifierTest < ActionMailer::TestCase/, test - assert_match /test "foo"/, test - assert_match /test "bar"/, test + assert_match(/module TestApp/, test) + assert_match(/class NotifierTest < ActionMailer::TestCase/, test) + assert_match(/test "foo"/, test) + assert_match(/test "bar"/, test) end end def test_invokes_default_template_engine run_generator assert_file "app/views/test_app/notifier/foo.text.erb" do |view| - assert_match %r(app/views/test_app/notifier/foo\.text\.erb), view - assert_match /<%= @greeting %>/, view + assert_match(%r(app/views/test_app/notifier/foo\.text\.erb), view) + assert_match(/<%= @greeting %>/, view) end assert_file "app/views/test_app/notifier/bar.text.erb" do |view| - assert_match %r(app/views/test_app/notifier/bar\.text\.erb), view - assert_match /<%= @greeting %>/, view + assert_match(%r(app/views/test_app/notifier/bar\.text\.erb), view) + assert_match(/<%= @greeting %>/, view) end end diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb index 45fe8dfbd3..afcee0a2dc 100644 --- a/railties/test/generators/observer_generator_test.rb +++ b/railties/test/generators/observer_generator_test.rb @@ -22,6 +22,6 @@ class ObserverGeneratorTest < Rails::Generators::TestCase def test_logs_if_the_test_framework_cannot_be_found content = run_generator ["account", "--test-framework=rspec"] - assert_match /rspec \[not found\]/, content + assert_match(/rspec \[not found\]/, content) end end diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index e6686a6af4..5c0774ddbd 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -32,7 +32,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase def test_check_class_collision content = capture(:stderr){ run_generator ["object"] } - assert_match /The name 'Object' is either already used in your application or reserved/, content + assert_match(/The name 'Object' is either already used in your application or reserved/, content) end def test_invokes_default_test_framework @@ -44,7 +44,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase def test_logs_if_the_test_framework_cannot_be_found content = nil silence(:stderr) { content = run_generator ["plugin_fu", "--test-framework=rspec"] } - assert_match /rspec \[not found\]/, content + assert_match(/rspec \[not found\]/, content) end def test_creates_tasks_if_required @@ -66,6 +66,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase def test_deprecation output = capture(:stderr) { run_generator } - assert_match /Plugin generator is deprecated, please use 'rails plugin new' command to generate plugin structure./, output + assert_match(/Plugin generator is deprecated, please use 'rails plugin new' command to generate plugin structure./, output) end end diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index 2af728e766..297ac5d238 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -95,6 +95,11 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase assert_file "test/dummy/config/database.yml", /postgres/ end + def test_generation_runs_bundle_install_with_full_and_mountable + result = run_generator [destination_root, "--mountable", "--full"] + assert_equal 1, result.scan("Your bundle is complete").size + end + def test_skipping_javascripts_without_mountable_option run_generator assert_no_file "app/assets/javascripts/application.js" diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index 71b3619351..73804dae45 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -9,8 +9,8 @@ class ResourceGeneratorTest < Rails::Generators::TestCase def test_help_with_inherited_options content = run_generator ["--help"] - assert_match /ActiveRecord options:/, content - assert_match /TestUnit options:/, content + assert_match(/ActiveRecord options:/, content) + assert_match(/TestUnit options:/, content) end def test_files_from_inherited_invocation @@ -55,7 +55,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase run_generator assert_file "config/routes.rb" do |route| - assert_match /resources :accounts$/, route + assert_match(/resources :accounts$/, route) end end @@ -63,19 +63,19 @@ class ResourceGeneratorTest < Rails::Generators::TestCase content = run_generator ["accounts".freeze] assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/ assert_file "test/unit/account_test.rb", /class AccountTest/ - assert_match /Plural version of the model detected, using singularized version. Override with --force-plural./, content + assert_match(/Plural version of the model detected, using singularized version. Override with --force-plural./, content) end def test_plural_names_can_be_forced content = run_generator ["accounts", "--force-plural"] assert_file "app/models/accounts.rb", /class Accounts < ActiveRecord::Base/ assert_file "test/unit/accounts_test.rb", /class AccountsTest/ - assert_no_match /Plural version of the model detected/, content + assert_no_match(/Plural version of the model detected/, content) end def test_mass_nouns_do_not_throw_warnings content = run_generator ["sheep".freeze] - assert_no_match /Plural version of the model detected/, content + assert_no_match(/Plural version of the model detected/, content) end def test_route_is_removed_on_revoke @@ -83,7 +83,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase run_generator ["account"], :behavior => :revoke assert_file "config/routes.rb" do |route| - assert_no_match /resources :accounts$/, route + assert_no_match(/resources :accounts$/, route) end end end diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index c7f45a807d..65b30b9fbd 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -14,39 +14,39 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase run_generator assert_file "app/controllers/users_controller.rb" do |content| - assert_match /class UsersController < ApplicationController/, content + assert_match(/class UsersController < ApplicationController/, content) assert_instance_method :index, content do |m| - assert_match /@users = User\.all/, m + assert_match(/@users = User\.all/, m) end assert_instance_method :show, content do |m| - assert_match /@user = User\.find\(params\[:id\]\)/, m + assert_match(/@user = User\.find\(params\[:id\]\)/, m) end assert_instance_method :new, content do |m| - assert_match /@user = User\.new/, m + assert_match(/@user = User\.new/, m) end assert_instance_method :edit, content do |m| - assert_match /@user = User\.find\(params\[:id\]\)/, m + assert_match(/@user = User\.find\(params\[:id\]\)/, m) end assert_instance_method :create, content do |m| - assert_match /@user = User\.new\(params\[:user\]\)/, m - assert_match /@user\.save/, m - assert_match /@user\.errors/, m + assert_match(/@user = User\.new\(params\[:user\]\)/, m) + assert_match(/@user\.save/, m) + assert_match(/@user\.errors/, m) end assert_instance_method :update, content do |m| - assert_match /@user = User\.find\(params\[:id\]\)/, m - assert_match /@user\.update_attributes\(params\[:user\]\)/, m - assert_match /@user\.errors/, m + assert_match(/@user = User\.find\(params\[:id\]\)/, m) + assert_match(/@user\.update_attributes\(params\[:user\]\)/, m) + assert_match(/@user\.errors/, m) end assert_instance_method :destroy, content do |m| - assert_match /@user = User\.find\(params\[:id\]\)/, m - assert_match /@user\.destroy/, m + assert_match(/@user = User\.find\(params\[:id\]\)/, m) + assert_match(/@user\.destroy/, m) end end end @@ -73,8 +73,8 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase run_generator assert_file "test/functional/users_controller_test.rb" do |content| - assert_match /class UsersControllerTest < ActionController::TestCase/, content - assert_match /test "should get index"/, content + assert_match(/class UsersControllerTest < ActionController::TestCase/, content) + assert_match(/test "should get index"/, content) end end @@ -93,10 +93,10 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase run_generator ["User", "--orm=unknown"] assert_file "app/controllers/users_controller.rb" do |content| - assert_match /class UsersController < ApplicationController/, content + assert_match(/class UsersController < ApplicationController/, content) assert_instance_method :index, content do |m| - assert_match /@users = User\.all/, m + assert_match(/@users = User\.all/, m) end end end @@ -112,11 +112,11 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase run_generator ["User", "--orm=unknown"] assert_file "app/controllers/users_controller.rb" do |content| - assert_match /class UsersController < ApplicationController/, content + assert_match(/class UsersController < ApplicationController/, content) assert_instance_method :index, content do |m| - assert_match /@users = User\.find\(:all\)/, m - assert_no_match /@users = User\.all/, m + assert_match(/@users = User\.find\(:all\)/, m) + assert_no_match(/@users = User\.all/, m) end end ensure @@ -127,9 +127,9 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase run_generator assert_file "app/controllers/users_controller.rb" do |content| if RUBY_VERSION < "1.9" - assert_match /\{ render :action => "new" \}/, content + assert_match(/\{ render :action => "new" \}/, content) else - assert_match /\{ render action: "new" \}/, content + assert_match(/\{ render action: "new" \}/, content) end end end @@ -137,7 +137,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase def test_force_old_style_hash run_generator ["User", "--old-style-hash"] assert_file "app/controllers/users_controller.rb" do |content| - assert_match /\{ render :action => "new" \}/, content + assert_match(/\{ render :action => "new" \}/, content) end end end diff --git a/railties/test/generators/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb index 9fee948d7c..b590047ff0 100644 --- a/railties/test/generators/session_migration_generator_test.rb +++ b/railties/test/generators/session_migration_generator_test.rb @@ -18,8 +18,8 @@ class SessionMigrationGeneratorTest < Rails::Generators::TestCase ActiveRecord::SessionStore::Session.table_name = "custom_table_name" run_generator assert_migration "db/migrate/add_sessions_table.rb" do |migration| - assert_match /class AddSessionsTable < ActiveRecord::Migration/, migration - assert_match /create_table :custom_table_name/, migration + assert_match(/class AddSessionsTable < ActiveRecord::Migration/, migration) + assert_match(/create_table :custom_table_name/, migration) end ensure ActiveRecord::SessionStore::Session.table_name = "sessions" diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 99c9d790eb..1264ac7764 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -28,7 +28,7 @@ class GeneratorsTest < Rails::Generators::TestCase def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments output = capture(:stdout){ Rails::Generators.invoke :model, [] } - assert_match /Description:/, output + assert_match(/Description:/, output) end def test_should_give_higher_preference_to_rails_generators @@ -90,8 +90,8 @@ class GeneratorsTest < Rails::Generators::TestCase def test_find_by_namespace_show_warning_if_generator_cant_be_loaded output = capture(:stderr) { Rails::Generators.find_by_namespace(:wrong) } - assert_match /\[WARNING\] Could not load generator/, output - assert_match /Rails 2\.x generator/, output + assert_match(/\[WARNING\] Could not load generator/, output) + assert_match(/Rails 2\.x generator/, output) end def test_invoke_with_nested_namespaces @@ -104,38 +104,38 @@ class GeneratorsTest < Rails::Generators::TestCase def test_rails_generators_help_with_builtin_information output = capture(:stdout){ Rails::Generators.help } - assert_match /Rails:/, output - assert_match /^ model$/, output - assert_match /^ scaffold_controller$/, output - assert_no_match /^ app$/, output + assert_match(/Rails:/, output) + assert_match(/^ model$/, output) + assert_match(/^ scaffold_controller$/, output) + assert_no_match(/^ app$/, output) end def test_rails_generators_help_does_not_include_app_nor_plugin_new output = capture(:stdout){ Rails::Generators.help } - assert_no_match /app/, output - assert_no_match /plugin_new/, output + assert_no_match(/app/, output) + assert_no_match(/plugin_new/, output) end def test_rails_generators_with_others_information output = capture(:stdout){ Rails::Generators.help } - assert_match /Fixjour:/, output - assert_match /^ fixjour$/, output + assert_match(/Fixjour:/, output) + assert_match(/^ fixjour$/, output) end def test_rails_generators_does_not_show_active_record_hooks output = capture(:stdout){ Rails::Generators.help } - assert_match /ActiveRecord:/, output - assert_match /^ active_record:fixjour$/, output + assert_match(/ActiveRecord:/, output) + assert_match(/^ active_record:fixjour$/, output) end def test_default_banner_should_show_generator_namespace klass = Rails::Generators.find_by_namespace(:foobar) - assert_match /^rails generate foobar:foobar/, klass.banner + assert_match(/^rails generate foobar:foobar/, klass.banner) end def test_default_banner_should_not_show_rails_generator_namespace klass = Rails::Generators.find_by_namespace(:model) - assert_match /^rails generate model/, klass.banner + assert_match(/^rails generate model/, klass.banner) end def test_no_color_sets_proper_shell |