diff --git a/Gemfile b/Gemfile index 086a007d65c41..b29bc10b6cb8c 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,7 @@ gem "bcrypt-ruby", "~> 3.0.0" gem "jquery-rails" # This needs to be with require false to avoid # it being automatically loaded by sprockets -gem "uglifier", ">= 1.0.0", :require => false +gem "uglifier", ">= 1.0.3", :require => false # Temp fix until rake 0.9.3 is out if RUBY_VERSION >= "1.9.3" @@ -68,7 +68,7 @@ end platforms :jruby do gem "ruby-debug", ">= 0.10.3" gem "json" - gem "activerecord-jdbcsqlite3-adapter" + gem "activerecord-jdbcsqlite3-adapter", ">= 1.2.0" # This is needed by now to let tests work on JRuby # TODO: When the JRuby guys merge jruby-openssl in @@ -76,8 +76,8 @@ platforms :jruby do gem "jruby-openssl" group :db do - gem "activerecord-jdbcmysql-adapter" - gem "activerecord-jdbcpostgresql-adapter" + gem "activerecord-jdbcmysql-adapter", ">= 1.2.0" + gem "activerecord-jdbcpostgresql-adapter", ">= 1.2.0" end end diff --git a/RAILS_VERSION b/RAILS_VERSION index fd2a01863fdd3..f5b6d1d026de3 100644 --- a/RAILS_VERSION +++ b/RAILS_VERSION @@ -1 +1 @@ -3.1.0 +3.1.1.rc1 diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 14c887eb535c9..defe5a7d61988 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,3 +1,8 @@ +*Rails 3.1.1 (unreleased)* + +* No changes + + *Rails 3.1.0 (August 30, 2011)* * No changes diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index f89396f08865b..9d75339be533a 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -2,8 +2,8 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - TINY = 0 - PRE = nil + TINY = 1 + PRE = "rc1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/actionmailer/test/old_base/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb index 0b5b0b2da31ca..5e53780dfb593 100644 --- a/actionmailer/test/old_base/mail_service_test.rb +++ b/actionmailer/test/old_base/mail_service_test.rb @@ -514,7 +514,8 @@ def test_from_without_name_for_smtp assert_not_nil mail mail, from, to = mail - assert_equal 'system@loudthinking.com', from.to_s + assert_equal ['root@loudthinking.com'], to + assert_equal 'system@loudthinking.com', from end def test_from_with_name_for_smtp @@ -525,6 +526,7 @@ def test_from_with_name_for_smtp assert_not_nil mail mail, from, to = mail + assert_equal ['root@loudthinking.com'], to assert_equal 'system@loudthinking.com', from end @@ -979,7 +981,7 @@ def test_file_delivery_should_create_a_file TestMailer.delivery_method = :file tmp_location = TestMailer.file_settings[:location] - result = TestMailer.cc_bcc(@recipient).deliver + TestMailer.cc_bcc(@recipient).deliver assert File.exists?(tmp_location) assert File.directory?(tmp_location) assert File.exists?(File.join(tmp_location, @recipient)) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 219b2228f6f00..34d5804d3d01a 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,15 @@ *Rails 3.1.1 (unreleased)* +* Allow asset tag helper methods to accept :digest => false option in order to completely avoid the digest generation. +Useful for linking assets from static html files or from emails when the user +could probably look at an older html email with an older asset. [Santiago Pastorino] + +* Don't mount Sprockets server at config.assets.prefix if config.assets.compile is false. [Mark J. Titorenko] + +* Set relative url root in assets when controller isn't available for Sprockets (eg. Sass files using asset_path). Fixes #2435 [Guillermo Iguaran] + +* Fix basic auth credential generation to not make newlines. GH #2882 + * Fixed the behavior of asset pipeline when config.assets.digest and config.assets.compile are false and requested asset isn't precompiled. Before the requested asset were compiled anyway ignoring that the config.assets.compile flag is false. [Guillermo Iguaran] @@ -11,6 +21,10 @@ * Fixed stylesheet_link_tag and javascript_include_tag to respect additional options passed by the users when debug is on. [Guillermo Iguaran] +* Fix ActiveRecord#exists? when passsed a nil value + +* Fix assert_select_email to work on multipart and non-multipart emails as the method stopped working correctly in Rails 3.x due to changes in the new mail gem. + *Rails 3.1.0 (August 30, 2011)* diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index cb9013d63bca0..22919783902df 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -145,7 +145,7 @@ def decode_credentials(request) end def encode_credentials(user_name, password) - "Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}" + "Basic #{ActiveSupport::Base64.encode64s("#{user_name}:#{password}")}" end def authentication_request(controller, realm) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index d11ab910c743b..e8e613e86b5c5 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -458,7 +458,9 @@ def define_generate_prefix(app, name) prefix_options = options.slice(*_route.segment_keys) # we must actually delete prefix segment keys to avoid passing them to next url_for _route.segment_keys.each { |k| options.delete(k) } - _routes.url_helpers.send("#{name}_path", prefix_options) + prefix = _routes.url_helpers.send("#{name}_path", prefix_options) + prefix = '' if prefix == '/' + prefix end end end diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb index 5fa91d1a76bc7..b4555f4f5982b 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb @@ -415,9 +415,9 @@ def assert_select_email(&block) assert !deliveries.empty?, "No e-mail in delivery list" for delivery in deliveries - for part in delivery.parts + for part in (delivery.parts.empty? ? [delivery] : delivery.parts) if part["Content-Type"].to_s =~ /^text\/html\W/ - root = HTML::Document.new(part.body).root + root = HTML::Document.new(part.body.to_s).root assert_select root, ":root", &block end end diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index d99ec3a4c8112..8e576c618e45f 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,8 +2,8 @@ module ActionPack module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - TINY = 0 - PRE = nil + TINY = 1 + PRE = "rc1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb index 8f356a7e1a324..95c640404f0c4 100644 --- a/actionpack/lib/action_view/asset_paths.rb +++ b/actionpack/lib/action_view/asset_paths.rb @@ -21,14 +21,15 @@ def initialize(config, controller = nil) # When :relative (default), the protocol will be determined by the client using current protocol # When :request, the protocol will be the request protocol # Otherwise, the protocol is used (E.g. :http, :https, etc) - def compute_public_path(source, dir, ext = nil, include_host = true, protocol = nil) + def compute_public_path(source, dir, options = {}) source = source.to_s return source if is_uri?(source) - source = rewrite_extension(source, dir, ext) if ext - source = rewrite_asset_path(source, dir) - source = rewrite_relative_url_root(source, relative_url_root) if has_request? - source = rewrite_host_and_protocol(source, protocol) if include_host + options[:include_host] ||= true + source = rewrite_extension(source, dir, options[:ext]) if options[:ext] + source = rewrite_asset_path(source, dir, options) + source = rewrite_relative_url_root(source, relative_url_root) + source = rewrite_host_and_protocol(source, options[:protocol]) if options[:include_host] source end @@ -119,10 +120,11 @@ def compute_asset_host(source) end def relative_url_root - config = controller.config if controller.respond_to?(:config) - config ||= config.action_controller if config.action_controller.present? - config ||= config - config.relative_url_root + if config.action_controller.present? + config.action_controller.relative_url_root + else + config.relative_url_root + end end def asset_host_config diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb index 3c05173a1b656..05d5f1870a5cb 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb @@ -60,8 +60,8 @@ def include_tag(*sources) private - def path_to_asset(source, include_host = true, protocol = nil) - asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host, protocol) + def path_to_asset(source, options = {}) + asset_paths.compute_public_path(source, asset_name.to_s.pluralize, options.merge(:ext => extension)) end def path_to_asset_source(source) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb index 8b35aa8896943..dd4e9ae4ccb96 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb @@ -41,7 +41,7 @@ def rewrite_extension(source, dir, ext) # Break out the asset path rewrite in case plugins wish to put the asset id # someplace other than the query string. - def rewrite_asset_path(source, dir) + def rewrite_asset_path(source, dir, options = nil) source = "/#{dir}/#{source}" unless source[0] == ?/ path = config.asset_path diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb index 25cc5616083d0..09700bd0c5bcb 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb @@ -83,7 +83,7 @@ def register_javascript_expansion(expansions) # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js def javascript_path(source) - asset_paths.compute_public_path(source, 'javascripts', 'js') + asset_paths.compute_public_path(source, 'javascripts', :ext => 'js') end alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb index 8c25d38bbd11e..2eb3eb31af4b6 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb @@ -17,7 +17,7 @@ def extension def asset_tag(source, options) # We force the :request protocol here to avoid a double-download bug in IE7 and IE8 - tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source, true, :request)) }.merge(options), false, false) + tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source, :protocol => :request)) }.merge(options), false, false) end def custom_dir @@ -61,7 +61,7 @@ def register_stylesheet_expansion(expansions) # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css def stylesheet_path(source) - asset_paths.compute_public_path(source, 'stylesheets', 'css', true, :request) + asset_paths.compute_public_path(source, 'stylesheets', :ext => 'css', :protocol => :request) end alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route diff --git a/actionpack/lib/sprockets/assets.rake b/actionpack/lib/sprockets/assets.rake index a8128d9a82bf7..81223b7ead0eb 100644 --- a/actionpack/lib/sprockets/assets.rake +++ b/actionpack/lib/sprockets/assets.rake @@ -9,6 +9,7 @@ namespace :assets do Kernel.exec $0, *ARGV else Rake::Task["environment"].invoke + Rake::Task["tmp:cache:clear"].invoke # Ensure that action view is loaded and the appropriate sprockets hooks get executed ActionView::Base @@ -26,6 +27,8 @@ namespace :assets do env.each_logical_path do |logical_path| if path.is_a?(Regexp) next unless path.match(logical_path) + elsif path.is_a?(Proc) + next unless path.call(logical_path) else next unless File.fnmatch(path.to_s, logical_path) end @@ -42,7 +45,7 @@ namespace :assets do end end - File.open("#{manifest_path}/manifest.yml", 'w') do |f| + File.open("#{manifest_path}/manifest.yml", 'wb') do |f| YAML.dump(manifest, f) end end diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb index eae882cd55f4f..e165a51cac450 100644 --- a/actionpack/lib/sprockets/helpers/rails_helper.rb +++ b/actionpack/lib/sprockets/helpers/rails_helper.rb @@ -25,38 +25,40 @@ def javascript_include_tag(*sources) options = sources.extract_options! debug = options.key?(:debug) ? options.delete(:debug) : debug_assets? body = options.key?(:body) ? options.delete(:body) : false + digest = options.key?(:digest) ? options.delete(:digest) : digest_assets? sources.collect do |source| if debug && asset = asset_paths.asset_for(source, 'js') asset.to_a.map { |dep| - super(dep.to_s, { :src => asset_path(dep, 'js', true) }.merge!(options)) + super(dep.to_s, { :src => asset_path(dep, :ext => 'js', :body => true, :digest => digest) }.merge!(options)) } else - super(source.to_s, { :src => asset_path(source, 'js', body) }.merge!(options)) + super(source.to_s, { :src => asset_path(source, :ext => 'js', :body => body, :digest => digest) }.merge!(options)) end end.join("\n").html_safe end def stylesheet_link_tag(*sources) options = sources.extract_options! - debug = options.key?(:debug) ? options.delete(:debug) : debug_assets? - body = options.key?(:body) ? options.delete(:body) : false + debug = options.key?(:debug) ? options.delete(:debug) : debug_assets? + body = options.key?(:body) ? options.delete(:body) : false + digest = options.key?(:digest) ? options.delete(:digest) : digest_assets? sources.collect do |source| if debug && asset = asset_paths.asset_for(source, 'css') asset.to_a.map { |dep| - super(dep.to_s, { :href => asset_path(dep, 'css', true, :request) }.merge!(options)) + super(dep.to_s, { :href => asset_path(dep, :ext => 'css', :body => true, :protocol => :request, :digest => digest) }.merge!(options)) } else - super(source.to_s, { :href => asset_path(source, 'css', body, :request) }.merge!(options)) + super(source.to_s, { :href => asset_path(source, :ext => 'css', :body => body, :protocol => :request, :digest => digest) }.merge!(options)) end end.join("\n").html_safe end - def asset_path(source, default_ext = nil, body = false, protocol = nil) + def asset_path(source, options = {}) source = source.logical_path if source.respond_to?(:logical_path) - path = asset_paths.compute_public_path(source, 'assets', default_ext, true, protocol) - body ? "#{path}?body=1" : path + path = asset_paths.compute_public_path(source, 'assets', options.merge(:body => true)) + options[:body] ? "#{path}?body=1" : path end private @@ -105,8 +107,8 @@ class AssetPaths < ::ActionView::AssetPaths #:nodoc: class AssetNotPrecompiledError < StandardError; end - def compute_public_path(source, dir, ext=nil, include_host=true, protocol=nil) - super(source, asset_prefix, ext, include_host, protocol) + def compute_public_path(source, dir, options = {}) + super(source, asset_prefix, options) end # Return the filesystem path for the source @@ -136,11 +138,11 @@ def digest_for(logical_path) end end - def rewrite_asset_path(source, dir) + def rewrite_asset_path(source, dir, options = {}) if source[0] == ?/ source else - source = digest_for(source) + source = digest_for(source) unless options[:digest] == false source = File.join(dir, source) source = "/#{source}" unless source =~ /^\// source diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index 7927b7bc2c1d6..f05d8355547ba 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -67,11 +67,13 @@ class Railtie < ::Rails::Railtie end end - app.routes.prepend do - mount app.assets => config.assets.prefix + if config.assets.compile + app.routes.prepend do + mount app.assets => config.assets.prefix + end end - if config.action_controller.perform_caching + if config.assets.digest app.assets = app.assets.index end end diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb index 878484eb57853..5eef8a32d7ad0 100644 --- a/actionpack/test/controller/assert_select_test.rb +++ b/actionpack/test/controller/assert_select_test.rb @@ -20,6 +20,15 @@ def test(html) end end + class AssertMultipartSelectMailer < ActionMailer::Base + def test(options) + mail :subject => "Test e-mail", :from => "test@test.host", :to => "test " do |format| + format.text { render :text => options[:text] } + format.html { render :text => options[:html] } + end + end + end + class AssertSelectController < ActionController::Base def response_with=(content) @content = content @@ -313,6 +322,16 @@ def test_assert_select_email end end + def test_assert_select_email_multipart + AssertMultipartSelectMailer.test(:html => "

foo

bar

", :text => 'foo bar').deliver + assert_select_email do + assert_select "div:root" do + assert_select "p:first-child", "foo" + assert_select "p:last-child", "bar" + end + end + end + protected def render_html(html) @controller.response_with = html diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index bd3e13e6fabbc..364e96d4f6885 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -85,6 +85,14 @@ def authenticate_long_credentials end end + def test_encode_credentials_has_no_newline + username = 'laskjdfhalksdjfhalkjdsfhalksdjfhklsdjhalksdjfhalksdjfhlakdsjfh' + password = 'kjfhueyt9485osdfasdkljfh4lkjhakldjfhalkdsjf' + result = ActionController::HttpAuthentication::Basic.encode_credentials( + username, password) + assert_no_match(/\n/, result) + end + test "authentication request without credential" do get :display diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb index 31ce97a25bb07..904398f563f07 100644 --- a/actionpack/test/dispatch/session/test_session_test.rb +++ b/actionpack/test/dispatch/session/test_session_test.rb @@ -29,7 +29,6 @@ def test_calling_update_with_params_passes_to_attributes end def test_clear_emptys_session - params = {:one => 'one', :two => 'two'} session = ActionController::TestSession.new({:one => 'one', :two => 'two'}) session.clear assert_nil(session[:one]) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 7e3197e08d6bc..a33688cfa8808 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -697,12 +697,11 @@ def test_form_for_with_isolated_namespaced_model end expected = - "
" + - snowman + - "" + - "" + - "" + - "
" + whole_form('/posts/44', "edit_blog_post_44", 'edit_blog_post', :method => 'put') do + "" + + "" + end + assert_dom_equal expected, output_buffer end def test_form_for_with_symbol_object_name diff --git a/actionpack/test/template/html-scanner/document_test.rb b/actionpack/test/template/html-scanner/document_test.rb index ddfb351595f76..3db2fba7839e5 100644 --- a/actionpack/test/template/html-scanner/document_test.rb +++ b/actionpack/test/template/html-scanner/document_test.rb @@ -123,7 +123,7 @@ def test_find_empty_tag def test_parse_invalid_document assert_nothing_raised do - doc = HTML::Document.new(" + HTML::Document.new(" @@ -135,7 +135,7 @@ def test_parse_invalid_document def test_invalid_document_raises_exception_when_strict assert_raise RuntimeError do - doc = HTML::Document.new(" + HTML::Document.new("
About Us
diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 74d7bba4fec6b..1ba14e8bc9500 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -48,14 +48,12 @@ def test_block_not_in_erb_multiple_calls end def test_block_works_with_content_tag_for_in_erb - __in_erb_template = '' expected = %(#{@post.body}) actual = content_tag_for(:tr, @post) { concat @post.body } assert_dom_equal expected, actual end def test_div_for_in_erb - __in_erb_template = '' expected = %(
#{@post.body}
) actual = div_for(@post, :class => "bar") { concat @post.body } assert_dom_equal expected, actual diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 86d08a43a5ae8..4187a0ac78ca1 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -370,7 +370,7 @@ def test_render_utf8_template_with_default_external_encoding def test_render_utf8_template_with_incompatible_external_encoding with_external_encoding Encoding::SHIFT_JIS do begin - result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") + @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") flunk 'Should have raised incompatible encoding error' rescue ActionView::Template::Error => error assert_match 'Your template was not saved as valid Shift_JIS', error.original_exception.message @@ -381,7 +381,7 @@ def test_render_utf8_template_with_incompatible_external_encoding def test_render_utf8_template_with_partial_with_incompatible_encoding with_external_encoding Encoding::SHIFT_JIS do begin - result = @view.render(:file => "test/utf8_magic_with_bare_partial.html.erb", :layouts => "layouts/yield") + @view.render(:file => "test/utf8_magic_with_bare_partial.html.erb", :layouts => "layouts/yield") flunk 'Should have raised incompatible encoding error' rescue ActionView::Template::Error => error assert_match 'Your template was not saved as valid Shift_JIS', error.original_exception.message diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb index cf6476d156eba..9be945e1cb25d 100644 --- a/actionpack/test/template/sprockets_helper_test.rb +++ b/actionpack/test/template/sprockets_helper_test.rb @@ -44,6 +44,10 @@ def url_for(*args) test "asset_path" do assert_match %r{/assets/logo-[0-9a-f]+.png}, asset_path("logo.png") + assert_match %r{/assets/logo-[0-9a-f]+.png}, + asset_path("logo.png", :digest => true) + assert_match %r{/assets/logo.png}, + asset_path("logo.png", :digest => false) end test "asset_path with root relative assets" do @@ -127,27 +131,38 @@ def url_for(*args) asset_path("/images/logo.gif") end + test "asset path with relative url root when controller isn't present but relative_url_root is" do + @controller = nil + @config.action_controller.relative_url_root = "/collaboration/hieraki" + assert_equal "/collaboration/hieraki/images/logo.gif", + asset_path("/images/logo.gif") + end + test "javascript path" do assert_match %r{/assets/application-[0-9a-f]+.js}, - asset_path(:application, "js") + asset_path(:application, :ext => "js") assert_match %r{/assets/xmlhr-[0-9a-f]+.js}, - asset_path("xmlhr", "js") + asset_path("xmlhr", :ext => "js") assert_match %r{/assets/dir/xmlhr-[0-9a-f]+.js}, - asset_path("dir/xmlhr.js", "js") + asset_path("dir/xmlhr.js", :ext => "js") assert_equal "/dir/xmlhr.js", - asset_path("/dir/xmlhr", "js") + asset_path("/dir/xmlhr", :ext => "js") assert_equal "http://www.example.com/js/xmlhr", - asset_path("http://www.example.com/js/xmlhr", "js") + asset_path("http://www.example.com/js/xmlhr", :ext => "js") assert_equal "http://www.example.com/js/xmlhr.js", - asset_path("http://www.example.com/js/xmlhr.js", "js") + asset_path("http://www.example.com/js/xmlhr.js", :ext => "js") end test "javascript include tag" do assert_match %r{}, javascript_include_tag(:application) + assert_match %r{}, + javascript_include_tag(:application, :digest => true) + assert_match %r{}, + javascript_include_tag(:application, :digest => false) assert_match %r{}, javascript_include_tag("xmlhr") @@ -169,21 +184,25 @@ def url_for(*args) end test "stylesheet path" do - assert_match %r{/assets/application-[0-9a-f]+.css}, asset_path(:application, "css") + assert_match %r{/assets/application-[0-9a-f]+.css}, asset_path(:application, :ext => "css") - assert_match %r{/assets/style-[0-9a-f]+.css}, asset_path("style", "css") - assert_match %r{/assets/dir/style-[0-9a-f]+.css}, asset_path("dir/style.css", "css") - assert_equal "/dir/style.css", asset_path("/dir/style.css", "css") + assert_match %r{/assets/style-[0-9a-f]+.css}, asset_path("style", :ext => "css") + assert_match %r{/assets/dir/style-[0-9a-f]+.css}, asset_path("dir/style.css", :ext => "css") + assert_equal "/dir/style.css", asset_path("/dir/style.css", :ext => "css") assert_equal "http://www.example.com/css/style", - asset_path("http://www.example.com/css/style", "css") + asset_path("http://www.example.com/css/style", :ext => "css") assert_equal "http://www.example.com/css/style.css", - asset_path("http://www.example.com/css/style.css", "css") + asset_path("http://www.example.com/css/style.css", :ext => "css") end test "stylesheet link tag" do assert_match %r{}, stylesheet_link_tag(:application) + assert_match %r{}, + stylesheet_link_tag(:application, :digest => true) + assert_match %r{}, + stylesheet_link_tag(:application, :digest => false) assert_match %r{}, stylesheet_link_tag("style") @@ -214,14 +233,14 @@ def url_for(*args) test "alternate asset prefix" do stubs(:asset_prefix).returns("/themes/test") - assert_match %r{/themes/test/style-[0-9a-f]+.css}, asset_path("style", "css") + assert_match %r{/themes/test/style-[0-9a-f]+.css}, asset_path("style", :ext => "css") end test "alternate asset environment" do assets = Sprockets::Environment.new assets.append_path(FIXTURES.join("sprockets/alternate/stylesheets")) stubs(:asset_environment).returns(assets) - assert_match %r{/assets/style-[0-9a-f]+.css}, asset_path("style", "css") + assert_match %r{/assets/style-[0-9a-f]+.css}, asset_path("style", :ext => "css") end test "alternate hash based on environment" do @@ -229,10 +248,10 @@ def url_for(*args) assets.version = 'development' assets.append_path(FIXTURES.join("sprockets/alternate/stylesheets")) stubs(:asset_environment).returns(assets) - dev_path = asset_path("style", "css") + dev_path = asset_path("style", :ext => "css") assets.version = 'production' - prod_path = asset_path("style", "css") + prod_path = asset_path("style", :ext => "css") assert_not_equal prod_path, dev_path end diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb index 81fb34b80f4b7..b0ca7de0b6f24 100644 --- a/actionpack/test/template/template_test.rb +++ b/actionpack/test/template/template_test.rb @@ -153,7 +153,6 @@ def test_lying_with_magic_comment def test_encoding_can_be_specified_with_magic_comment_in_erb with_external_encoding Encoding::UTF_8 do @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat", :virtual_path => nil) - result = render assert_equal Encoding::UTF_8, render.encoding assert_equal "hello \u{fc}mlat", render end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 740f577a6ed56..42c78b4e72e85 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -291,7 +291,7 @@ def test_cycle_class end def test_cycle_class_with_no_arguments - assert_raise(ArgumentError) { value = Cycle.new() } + assert_raise(ArgumentError) { Cycle.new() } end def test_cycle @@ -304,7 +304,7 @@ def test_cycle end def test_cycle_with_no_arguments - assert_raise(ArgumentError) { value = cycle() } + assert_raise(ArgumentError) { cycle() } end def test_cycle_resets_with_new_values diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb index 7fd71c271166a..b92af9e2ac579 100644 --- a/activemodel/lib/active_model/version.rb +++ b/activemodel/lib/active_model/version.rb @@ -2,8 +2,8 @@ module ActiveModel module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - TINY = 0 - PRE = nil + TINY = 1 + PRE = "rc1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index a23374ec7efb0..e76554ecd4cbb 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,28 @@ *Rails 3.1.1 (unreleased)* +* LRU cache in mysql and sqlite are now per-process caches. + + * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache + keys are per process id. + * lib/active_record/connection_adapters/sqlite_adapter.rb: ditto + + [Aaron Patterson] + +* Database adapters use a statement pool for limiting the number of open + prepared statments on the database. The limit defaults to 1000, but can + be adjusted in your database config by changing 'statement_limit'. + +* Fix clash between using 'preload', 'joins' or 'eager_load' in a default scope and including the + default scoped model in a nested through association. (GH #2834.) [Jon Leighton] + +* Ensure we are not comparing a string with a symbol in HasManyAssociation#inverse_updates_counter_cache?. + Fixes GH #2755, where a counter cache could be decremented twice as far as it was supposed to be. + + [Jon Leighton] + +* Don't send any queries to the database when the foreign key of a belongs_to is nil. Fixes + GH #2828. [Georg Friedrich] + * Fixed find_in_batches method to not include order from default_scope. See GH #2832 [Arun Agrawal] * Don't compute table name for abstract classes. Fixes problem with setting the primary key diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d084790a5b94a..3162c2310f67d 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -76,12 +76,6 @@ def initialize(owner, reflection) end end - class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc: - def initialize(reflection) - super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).") - end - end - class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc: def initialize(reflection) super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.") diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index adb6af7165fc1..58c9648ce8a28 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -20,6 +20,10 @@ def updated? private + def find_target? + !loaded? && foreign_key_present? && klass + end + def update_counters(record) counter_cache_name = reflection.counter_cache_column diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index 2261e2e7d8a6e..30fc44b4c2426 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -37,10 +37,6 @@ def check_validity(reflection) model.send(:undecorated_table_name, model.to_s), model.send(:undecorated_table_name, reflection.class_name) ) - - if model.connection.supports_primary_key? && (model.connection.primary_key(reflection.options[:join_table]) rescue false) - raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection) - end end # Generates a join table name from two provided table names. diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index fe5271c7f9a7c..21f369026a4d5 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -78,7 +78,7 @@ def respond_to?(name, include_private = false) def method_missing(method, *args, &block) match = DynamicFinderMatch.match(method) if match && match.instantiator? - record = send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r| + send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r| proxy_association.send :set_owner_attributes, r proxy_association.send :add_to_target, r yield(r) if block_given? diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index 81172179e04c2..b347a94978022 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -16,7 +16,7 @@ def target_scope chain[1..-1].each do |reflection| scope = scope.merge( reflection.klass.scoped.with_default_scope. - except(:select, :create_with, :includes) + except(:select, :create_with, :includes, :preload, :joins, :eager_load) ) end scope diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index a9c3fffceef76..088eaafc1fc67 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -421,7 +421,7 @@ def connected?(klass) # can be used as an argument for establish_connection, for easily # re-establishing the connection. def remove_connection(klass) - pool = @connection_pools[klass.name] + pool = @connection_pools.delete(klass.name) return nil unless pool pool.automatic_reconnect = false diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index bcd3abc08d575..4a39cc4da50ad 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -100,7 +100,7 @@ def connection_config end def connection_pool - connection_handler.retrieve_connection_pool(self) + connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished end def retrieve_connection diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index b14fef1108bb6..94974379d1cf4 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/kernel/requires' require 'active_support/core_ext/object/blank' require 'set' +require 'active_record/connection_adapters/statement_pool' gem 'mysql', '~> 2.8.1' require 'mysql' @@ -184,11 +185,45 @@ class MysqlAdapter < AbstractAdapter :boolean => { :name => "tinyint", :limit => 1 } } + class StatementPool < ConnectionAdapters::StatementPool + def initialize(connection, max = 1000) + super + @cache = Hash.new { |h,pid| h[pid] = {} } + end + + def each(&block); cache.each(&block); end + def key?(key); cache.key?(key); end + def [](key); cache[key]; end + def length; cache.length; end + def delete(key); cache.delete(key); end + + def []=(sql, key) + while @max <= cache.size + cache.shift.last[:stmt].close + end + cache[sql] = key + end + + def clear + cache.values.each do |hash| + hash[:stmt].close + end + cache.clear + end + + private + def cache + @cache[$$] + end + end + def initialize(connection, logger, connection_options, config) super(connection, logger) @connection_options, @config = connection_options, config @quoted_column_names, @quoted_table_names = {}, {} @statements = {} + @statements = StatementPool.new(@connection, + config.fetch(:statement_limit) { 1000 }) @client_encoding = nil connect end @@ -334,9 +369,6 @@ def select_rows(sql, name = nil) # Clears the prepared statements cache. def clear_cache! - @statements.values.each do |cache| - cache[:stmt].close - end @statements.clear end @@ -755,7 +787,7 @@ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) def quoted_columns_for_index(column_names, options = {}) length = options[:length] if options.is_a?(Hash) - quoted_column_names = case length + case length when Hash column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) } when Fixnum diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 9824645f9b79b..b269c5df5fed9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -1,6 +1,7 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_support/core_ext/kernel/requires' require 'active_support/core_ext/object/blank' +require 'active_record/connection_adapters/statement_pool' # Make sure we're using pg high enough for PGResult#values gem 'pg', '~> 0.11' @@ -247,6 +248,47 @@ def supports_statement_cache? true end + class StatementPool < ConnectionAdapters::StatementPool + def initialize(connection, max) + super + @counter = 0 + @cache = Hash.new { |h,pid| h[pid] = {} } + end + + def each(&block); cache.each(&block); end + def key?(key); cache.key?(key); end + def [](key); cache[key]; end + def length; cache.length; end + + def next_key + "a#{@counter + 1}" + end + + def []=(sql, key) + while @max <= cache.size + dealloc(cache.shift.last) + end + @counter += 1 + cache[sql] = key + end + + def clear + cache.each_value do |stmt_key| + dealloc stmt_key + end + cache.clear + end + + private + def cache + @cache[$$] + end + + def dealloc(key) + @connection.query "DEALLOCATE #{key}" + end + end + # Initializes and connects a PostgreSQL adapter. def initialize(connection, logger, connection_parameters, config) super(connection, logger) @@ -255,9 +297,10 @@ def initialize(connection, logger, connection_parameters, config) # @local_tz is initialized as nil to avoid warnings when connect tries to use it @local_tz = nil @table_alias_length = nil - @statements = {} connect + @statements = StatementPool.new @connection, + config.fetch(:statement_limit) { 1000 } if postgresql_version < 80200 raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!" @@ -272,9 +315,6 @@ def self.visitor_for(pool) # :nodoc: # Clears the prepared statements cache. def clear_cache! - @statements.each_value do |value| - @connection.query "DEALLOCATE #{value}" - end @statements.clear end @@ -681,7 +721,7 @@ def table_exists?(name) SELECT COUNT(*) FROM pg_tables WHERE tablename = $1 - #{schema ? "AND schemaname = $2" : ''} + AND schemaname = #{schema ? "$2" : "ANY (current_schemas(false))"} SQL end @@ -965,7 +1005,7 @@ def exec_no_cache(sql, binds) def exec_cache(sql, binds) unless @statements.key? sql - nextkey = "a#{@statements.length + 1}" + nextkey = @statements.next_key @connection.prepare nextkey, sql @statements[sql] = nextkey end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 5d0e000fb7184..efede6b82bab3 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -1,5 +1,6 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_support/core_ext/kernel/requires' +require 'active_record/connection_adapters/statement_pool' require 'active_support/core_ext/string/encoding' module ActiveRecord @@ -49,9 +50,45 @@ def <=>(version_string) end end + class StatementPool < ConnectionAdapters::StatementPool + def initialize(connection, max) + super + @cache = Hash.new { |h,pid| h[pid] = {} } + end + + def each(&block); cache.each(&block); end + def key?(key); cache.key?(key); end + def [](key); cache[key]; end + def length; cache.length; end + + def []=(sql, key) + while @max <= cache.size + dealloc(cache.shift.last[:stmt]) + end + cache[sql] = key + end + + def clear + cache.values.each do |hash| + dealloc hash[:stmt] + end + cache.clear + end + + private + def cache + @cache[$$] + end + + def dealloc(stmt) + stmt.close unless stmt.closed? + end + end + def initialize(connection, logger, config) super(connection, logger) - @statements = {} + @statements = StatementPool.new(@connection, + config.fetch(:statement_limit) { 1000 }) @config = config end @@ -108,7 +145,6 @@ def disconnect! # Clears the prepared statements cache. def clear_cache! - @statements.values.each { |hash| hash[:stmt].close } @statements.clear end diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb new file mode 100644 index 0000000000000..c6b1bc8b5bfa4 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb @@ -0,0 +1,40 @@ +module ActiveRecord + module ConnectionAdapters + class StatementPool + include Enumerable + + def initialize(connection, max = 1000) + @connection = connection + @max = max + end + + def each + raise NotImplementedError + end + + def key?(key) + raise NotImplementedError + end + + def [](key) + raise NotImplementedError + end + + def length + raise NotImplementedError + end + + def []=(sql, key) + raise NotImplementedError + end + + def clear + raise NotImplementedError + end + + def delete(key) + raise NotImplementedError + end + end + end +end diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 9e07e1ca8cc63..96e0c4ba3dcb8 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -71,7 +71,7 @@ def invert_add_column(args) end def invert_rename_index(args) - [:rename_index, args.reverse] + [:rename_index, [args.first] + args.last(2).reverse] end def invert_rename_column(args) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 69d4d4e51d9f5..a71f43ebc4b27 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -203,11 +203,13 @@ db_namespace = namespace :db do end db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}") file_list = [] - Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file| - # only files matching "20091231235959_some_name.rb" pattern - if match_data = /^(\d{14})_(.+)\.rb$/.match(file) - status = db_list.delete(match_data[1]) ? 'up' : 'down' - file_list << [status, match_data[1], match_data[2].humanize] + ActiveRecord::Migrator.migrations_paths.each do |path| + Dir.foreach(path) do |file| + # only files matching "20091231235959_some_name.rb" pattern + if match_data = /^(\d{14})_(.+)\.rb$/.match(file) + status = db_list.delete(match_data[1]) ? 'up' : 'down' + file_list << [status, match_data[1], match_data[2].humanize] + end end end db_list.map! do |version| diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 6ddf76e836e55..dfeed44e69fe0 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -238,7 +238,7 @@ def counter_cache_column if options[:counter_cache] == true "#{active_record.name.demodulize.underscore.pluralize}_count" elsif options[:counter_cache] - options[:counter_cache] + options[:counter_cache].to_s end end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 63a7fa5cfbde6..7390104cf978e 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -184,7 +184,9 @@ def all(*args) # Person.exists?(:name => "David") # Person.exists?(['name LIKE ?', "%#{query}%"]) # Person.exists? - def exists?(id = nil) + def exists?(id = false) + return false if id.nil? + id = id.id if ActiveRecord::Base === id join_dependency = construct_join_dependency_for_association_find diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index c56d9e6d2d3eb..ca06d66d2443c 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,8 +2,8 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - TINY = 0 - PRE = nil + TINY = 1 + PRE = "rc1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb new file mode 100644 index 0000000000000..83de90f179a6a --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb @@ -0,0 +1,23 @@ +require 'cases/helper' + +module ActiveRecord::ConnectionAdapters + class MysqlAdapter + class StatementPoolTest < ActiveRecord::TestCase + def test_cache_is_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + cache = StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] + + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } + + Process.waitpid pid + assert $?.success?, 'process should exit successfully' + end + end + end +end diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb index 752b864818248..3a9744e78f636 100644 --- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb @@ -87,8 +87,8 @@ def test_activerecord_model assert_nothing_raised { x.save } x.order = 'y' assert_nothing_raised { x.save } - assert_nothing_raised { y = Group.find_by_order('y') } - assert_nothing_raised { y = Group.find(1) } + assert_nothing_raised { Group.find_by_order('y') } + assert_nothing_raised { Group.find(1) } x = Group.find(1) end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index a5c3e69af9e4e..2ba5e5e41cd4f 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -68,7 +68,9 @@ def test_table_exists_wrong_schema end def test_table_exists_quoted_table - assert(@connection.table_exists?('"things.table"'), "table should exist") + with_schema_search_path(SCHEMA_NAME) do + assert(@connection.table_exists?('"things.table"'), "table should exist") + end end def test_with_schema_prefixed_table_name diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb new file mode 100644 index 0000000000000..a82c6f67d6604 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb @@ -0,0 +1,23 @@ +require 'cases/helper' + +module ActiveRecord::ConnectionAdapters + class PostgreSQLAdapter < AbstractAdapter + class StatementPoolTest < ActiveRecord::TestCase + def test_cache_is_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + cache = StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] + + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } + + Process.waitpid pid + assert $?.success?, 'process should exit successfully' + end + end + end +end diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb new file mode 100644 index 0000000000000..ae272e2c4ba60 --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb @@ -0,0 +1,24 @@ +require 'cases/helper' + +module ActiveRecord::ConnectionAdapters + class SQLiteAdapter + class StatementPoolTest < ActiveRecord::TestCase + def test_cache_is_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + cache = StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] + + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } + + Process.waitpid pid + assert $?.success?, 'process should exit successfully' + end + end + end +end + diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index a9a4bbd0de8af..33e407d19d712 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -343,6 +343,12 @@ def test_polymorphic_setting_foreign_key_after_nil_target_loaded assert_equal members(:groucho), sponsor.sponsorable end + def test_dont_find_target_when_foreign_key_is_null + tagging = taggings(:thinking_general) + queries = assert_sql { tagging.super_tag } + assert_equal 0, queries.length + end + def test_field_name_same_as_foreign_key computer = Computer.find(1) assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # ' diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb index 490fc5177e630..8dc1423375dcb 100644 --- a/activerecord/test/cases/associations/extension_test.rb +++ b/activerecord/test/cases/associations/extension_test.rb @@ -36,6 +36,11 @@ def test_extension_with_scopes end def test_marshalling_extensions + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + david = developers(:david) assert_equal projects(:action_controller), david.projects.find_most_recent @@ -46,6 +51,11 @@ def test_marshalling_extensions end def test_marshalling_named_extensions + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + david = developers(:david) assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent diff --git a/activerecord/test/cases/associations/habtm_join_table_test.rb b/activerecord/test/cases/associations/habtm_join_table_test.rb index 745f169ad7c2e..fe2b82f2c1cf1 100644 --- a/activerecord/test/cases/associations/habtm_join_table_test.rb +++ b/activerecord/test/cases/associations/habtm_join_table_test.rb @@ -32,13 +32,4 @@ def teardown ActiveRecord::Base.connection.drop_table :my_readers ActiveRecord::Base.connection.drop_table :my_books_my_readers end - - uses_transaction :test_should_raise_exception_when_join_table_has_a_primary_key - def test_should_raise_exception_when_join_table_has_a_primary_key - if ActiveRecord::Base.connection.supports_primary_key? - assert_raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError do - MyReader.has_and_belongs_to_many :my_books - end - end - 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 05ff7a2db18cb..16062bf19c733 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -17,6 +17,7 @@ require 'models/line_item' require 'models/car' require 'models/bulb' +require 'models/engine' class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase class Invoice < ActiveRecord::Base @@ -808,6 +809,15 @@ def test_clearing_updates_counter_cache end end + def test_clearing_updates_counter_cache_when_inverse_counter_cache_is_a_symbol_with_dependent_destroy + car = Car.first + car.engines.create! + + assert_difference 'car.reload.engines_count', -1 do + car.engines.clear + end + end + def test_clearing_a_dependent_association_collection firm = companies(:first_firm) client_id = firm.dependent_clients_of_firm.first.id diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 80c6e41169bc8..530f5212a295f 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -356,6 +356,17 @@ def test_has_one_through_has_one_through_with_belongs_to_source_reflection assert_equal categories(:general), members(:groucho).club_category end + def test_joins_and_includes_from_through_models_not_included_in_association + prev_default_scope = Club.default_scopes + + [:includes, :preload, :joins, :eager_load].each do |q| + Club.default_scopes = [Club.send(q, :category)] + assert_equal categories(:general), members(:groucho).reload.club_category + end + ensure + Club.default_scopes = prev_default_scope + end + def test_has_one_through_has_one_through_with_belongs_to_source_reflection_preload members = assert_queries(4) { Member.includes(:club_category).to_a.sort_by(&:id) } general = categories(:general) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 568e1204e48ce..9f0b922296f08 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1834,6 +1834,11 @@ def test_current_scope_is_reset end def test_marshal_round_trip + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + expected = posts(:welcome) marshalled = Marshal.dump(expected) actual = Marshal.load(marshalled) @@ -1842,6 +1847,11 @@ def test_marshal_round_trip end def test_marshal_new_record_round_trip + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + marshalled = Marshal.dump(Post.new) post = Marshal.load(marshalled) @@ -1849,6 +1859,11 @@ def test_marshal_new_record_round_trip end def test_marshalling_with_associations + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + post = Post.new post.comments.build diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index abf317768fc7b..bd0d161838241 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -6,7 +6,12 @@ class ConnectionHandlerTest < ActiveRecord::TestCase def setup @handler = ConnectionHandler.new @handler.establish_connection 'america', Base.connection_pool.spec - @klass = Struct.new(:name).new('america') + @klass = Class.new do + def self.name; 'america'; end + end + @subklass = Class.new(@klass) do + def self.name; 'north america'; end + end end def test_retrieve_connection @@ -28,6 +33,20 @@ def test_retrieve_connection_pool_with_ar_base def test_retrieve_connection_pool assert_not_nil @handler.retrieve_connection_pool(@klass) end + + def test_retrieve_connection_pool_uses_superclass_when_no_subclass_connection + assert_not_nil @handler.retrieve_connection_pool(@subklass) + end + + def test_retrieve_connection_pool_uses_superclass_pool_after_subclass_establish_and_remove + @handler.establish_connection 'north america', Base.connection_pool.spec + assert_not_same @handler.retrieve_connection_pool(@klass), + @handler.retrieve_connection_pool(@subklass) + + @handler.remove_connection @subklass + assert_same @handler.retrieve_connection_pool(@klass), + @handler.retrieve_connection_pool(@subklass) + end end end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 7a07ad7461f10..45d3d85700ecd 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -48,6 +48,15 @@ def test_exists_returns_true_with_one_record_and_no_args assert Topic.exists? end + # exists? should handle nil for id's that come from URLs and always return false + # (example: Topic.exists?(params[:id])) where params[:id] is nil + def test_exists_with_nil_arg + assert !Topic.exists?(nil) + assert Topic.exists? + assert !Topic.first.replies.exists?(nil) + assert Topic.first.replies.exists? + end + def test_does_not_exist_with_empty_table_and_no_args_given Topic.delete_all assert !Topic.exists? diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index bd7c33b829ce7..bb53240c4ac06 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -48,11 +48,11 @@ def test_clean_fixtures def test_broken_yaml_exception badyaml = Tempfile.new ['foo', '.yml'] - badyaml.write 'a: !ruby.yaml.org,2002:str |\nfoo' + badyaml.write 'a: : ' badyaml.flush dir = File.dirname badyaml.path - name =File.basename badyaml.path, '.yml' + name = File.basename badyaml.path, '.yml' assert_raises(ActiveRecord::Fixture::FormatError) do ActiveRecord::Fixtures.create_fixtures(dir, name) end diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb index 36007255fa8f3..d108b456f0fe5 100644 --- a/activerecord/test/cases/migration/command_recorder_test.rb +++ b/activerecord/test/cases/migration/command_recorder_test.rb @@ -104,9 +104,9 @@ def test_invert_add_index_with_no_options end def test_invert_rename_index - @recorder.record :rename_index, [:old, :new] + @recorder.record :rename_index, [:table, :old, :new] rename = @recorder.inverse.first - assert_equal [:rename_index, [:new, :old]], rename + assert_equal [:rename_index, [:table, :new, :old]], rename end def test_invert_add_timestamps diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 45134d9bbf941..a6134b4ae65c3 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -182,7 +182,7 @@ def test_first_and_last_should_support_find_options def test_first_and_last_should_allow_integers_for_limit assert_equal Topic.base.first(2), Topic.base.to_a.first(2) - assert_equal Topic.base.last(2), Topic.base.to_a.last(2) + assert_equal Topic.base.last(2), Topic.base.order("id").to_a.last(2) end def test_first_and_last_should_not_use_query_when_results_are_loaded diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb index 76f20b1061a56..b9c2e8ec9a4bf 100644 --- a/activerecord/test/models/car.rb +++ b/activerecord/test/models/car.rb @@ -8,7 +8,7 @@ class Car < ActiveRecord::Base has_one :frickinawesome_bulb, :class_name => "Bulb", :conditions => { :frickinawesome => true } has_many :tyres - has_many :engines + has_many :engines, :dependent => :destroy has_many :wheels, :as => :wheelable scope :incl_tyres, includes(:tyres) diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index fe356d769173a..7a3c84a8e7275 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,3 +1,8 @@ +*Rails 3.1.1 (unreleased)* + +* No changes + + *Rails 3.1.0 (August 30, 2011)* * 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. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 7528456c60cf4..14798695748df 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -1357,7 +1357,9 @@ def create end def load_attributes_from_response(response) - if !response['Content-Length'].blank? && response['Content-Length'] != "0" && !response.body.nil? && response.body.strip.size > 0 + if (response_code_allows_body?(response.code) && + (response['Content-Length'].nil? || response['Content-Length'] != "0") && + !response.body.nil? && response.body.strip.size > 0) load(self.class.format.decode(response.body), true) @persisted = true end @@ -1381,6 +1383,12 @@ def collection_path(options = nil) end private + + # Determine whether the response is allowed to have a body per HTTP 1.1 spec section 4.4.1 + def response_code_allows_body?(c) + !((100..199).include?(c) || [204,304].include?(c)) + end + # Tries to find a resource for a given collection name; if it fails, then the resource is created def find_or_create_resource_for_collection(name) find_or_create_resource_for(ActiveSupport::Inflector.singularize(name.to_s)) diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index ff175b382e577..460b3013bf6a1 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -2,8 +2,8 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - TINY = 0 - PRE = nil + TINY = 1 + PRE = "rc1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/activeresource/test/cases/base/load_test.rb b/activeresource/test/cases/base/load_test.rb index d6b04cfaa8f8c..0d030148d055d 100644 --- a/activeresource/test/cases/base/load_test.rb +++ b/activeresource/test/cases/base/load_test.rb @@ -72,7 +72,6 @@ def test_after_load_attributes_are_accessible 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] diff --git a/activeresource/test/cases/finder_test.rb b/activeresource/test/cases/finder_test.rb index 9c51f2a390a00..5fbbfeef6e755 100644 --- a/activeresource/test/cases/finder_test.rb +++ b/activeresource/test/cases/finder_test.rb @@ -95,7 +95,7 @@ def test_find_all_sub_objects def test_find_all_sub_objects_not_found assert_nothing_raised do - addys = StreetAddress.find(:all, :params => { :person_id => 2 }) + StreetAddress.find(:all, :params => { :person_id => 2 }) end end diff --git a/activeresource/test/cases/log_subscriber_test.rb b/activeresource/test/cases/log_subscriber_test.rb index b9143f5b673fe..ab5c22a783ddb 100644 --- a/activeresource/test/cases/log_subscriber_test.rb +++ b/activeresource/test/cases/log_subscriber_test.rb @@ -23,7 +23,7 @@ def set_logger(logger) end def test_request_notification - matz = Person.find(1) + Person.find(1) wait assert_equal 2, @logger.logged(:info).size assert_equal "GET http://37s.sunrise.i:3000/people/1.json", @logger.logged(:info)[0] diff --git a/activeresource/test/cases/observing_test.rb b/activeresource/test/cases/observing_test.rb index ca3ab5d03db23..58d3d389ffd60 100644 --- a/activeresource/test/cases/observing_test.rb +++ b/activeresource/test/cases/observing_test.rb @@ -37,7 +37,7 @@ def teardown end def test_create_fires_save_and_create_notifications - rick = Person.create(:name => 'Rick') + Person.create(:name => 'Rick') assert_equal [:before_save, :before_create, :after_create, :after_save], self.history end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 0d3bc7d112411..948414145c388 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.1.1 (unreleased)* +* Fixed performance issue where TimeZone lookups would require tzinfo each time [Tim Lucas] + * ActiveSupport::OrderedHash is now marked as extractable when using Array#extract_options! [Prem Sichanugrist] *Rails 3.1.0 (August 30, 2011)* diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 102378a029139..5f07bb4f5aae8 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -95,7 +95,7 @@ def typecast_xml_value(value) case value.class.to_s when 'Hash' if value['type'] == 'array' - _, entries = Array.wrap(value.detect { |k,v| k != 'type' }) + _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) }) if entries.nil? || (c = value['__content__'] && c.blank?) [] else diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb index 3f1540f685959..5d5fcf00e0a8c 100644 --- a/activesupport/lib/active_support/core_ext/object/to_query.rb +++ b/activesupport/lib/active_support/core_ext/object/to_query.rb @@ -7,7 +7,7 @@ class Object # Note: This method is defined as a default implementation for all Objects for Hash#to_query to work. def to_query(key) require 'cgi' unless defined?(CGI) && defined?(CGI::escape) - "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}" + "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}" end end diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 430b2721ee557..d709caecdd9e4 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -136,7 +136,7 @@ def to_yaml(*args) end for unsafe_method in UNSAFE_STRING_METHODS - class_eval <<-EOT, __FILE__, __LINE__ + class_eval <<-EOT, __FILE__, __LINE__ + 1 def #{unsafe_method}(*args) super.to_str end diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 3d092529d610d..b897578fac346 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -12,7 +12,7 @@ module ActiveSupport # # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)' # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00 - # Time.zone.parse('2007-02-01 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00 + # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00 # Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00 # Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00 # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00 diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 123ab2e192b55..d6eb8e1284366 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -373,7 +373,7 @@ def us_zones protected def require_tzinfo - require 'tzinfo' + require 'tzinfo' unless defined?(::TZInfo) rescue LoadError $stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install" raise diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index c5f84548a7435..965188d76866e 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,8 +2,8 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - TINY = 0 - PRE = nil + TINY = 1 + PRE = "rc1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 0b3f18faec413..1813ba2a4d031 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -670,6 +670,55 @@ def test_timezoned_attributes assert_match %r{1999-02-01T19:00:00-05:00}, xml end + def test_multiple_records_from_xml_with_attributes_other_than_type_ignores_them_without_exploding + topics_xml = <<-EOT + + + The First Topic + David + 1 + false + 0 + 2592000000 + 2003-07-16 + 2003-07-16T09:28:00+0000 + Have a nice day + david@loudthinking.com + + + + The Second Topic + Jason + 1 + false + 0 + 2592000000 + 2003-07-16 + 2003-07-16T09:28:00+0000 + Have a nice day + david@loudthinking.com + + + + EOT + + expected_topic_hash = { + :title => "The First Topic", + :author_name => "David", + :id => 1, + :approved => false, + :replies_count => 0, + :replies_close_in => 2592000000, + :written_on => Date.new(2003, 7, 16), + :viewed_at => Time.utc(2003, 7, 16, 9, 28), + :content => "Have a nice day", + :author_email_address => "david@loudthinking.com", + :parent_id => nil + }.stringify_keys + + assert_equal expected_topic_hash, Hash.from_xml(topics_xml)["topics"].first + end + def test_single_record_from_xml topic_xml = <<-EOT diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb index 84da52f4bfa42..c146f6cc9b90b 100644 --- a/activesupport/test/core_ext/object/to_query_test.rb +++ b/activesupport/test/core_ext/object/to_query_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'active_support/ordered_hash' require 'active_support/core_ext/object/to_query' +require 'active_support/core_ext/string/output_safety.rb' class ToQueryTest < Test::Unit::TestCase def test_simple_conversion @@ -11,6 +12,14 @@ def test_cgi_escaping assert_query_equal 'a%3Ab=c+d', 'a:b' => 'c d' end + def test_html_safe_parameter_key + assert_query_equal 'a%3Ab=c+d', 'a:b'.html_safe => 'c d' + end + + def test_html_safe_parameter_value + assert_query_equal 'a=%5B10%5D', 'a' => '[10]'.html_safe + end + def test_nil_parameter_value empty = Object.new def empty.to_param; nil end diff --git a/railties/CHANGELOG b/railties/CHANGELOG index d94c2d176a52d..e0d53965e34e1 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,3 +1,8 @@ +*Rails 3.1.1 (unreleased)* + +* No changes + + *Rails 3.1.0 (August 30, 2011)* * The default database schema file is written as UTF-8. [Aaron Patterson] diff --git a/railties/bin/rails b/railties/bin/rails deleted file mode 100755 index a7d6938e0d719..0000000000000 --- a/railties/bin/rails +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env ruby -require "rails/cli" \ No newline at end of file diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index ab92dc693ef54..e28e66c1d6a74 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -1016,7 +1016,7 @@ class A class_attribute :x, :instance_reader => false end -A.x = 1 # NoMethodError +A.new.x = 1 # NoMethodError For convenience +class_attribute+ also defines an instance predicate which is the double negation of what the instance reader returns. In the examples above it would be called +x?+. diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile index 48986c13c8b96..e66ef047e63c7 100644 --- a/railties/guides/source/asset_pipeline.textile +++ b/railties/guides/source/asset_pipeline.textile @@ -63,7 +63,7 @@ This has several disadvantages:
  1. Not all caches will cache content with a query string
    - "Steve Souders recommends":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/, "...avoiding a querystring for cacheable resources". He found that in these case 5-20% of requests will not be cached. + "Steve Souders recommends":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/, "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached.
  2. The file name can change between nodes in multi-server environments.
    @@ -95,7 +95,7 @@ When a scaffold or controller is generated for the application, Rails also gener For example, if a +ProjectsController+ is generated, there will be a new file at +app/assets/javascripts/projects.js.coffee+ and another at +app/assets/stylesheets/projects.css.scss+. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as +<%= javascript_include_tag params[:controller] %>+ or +<%= stylesheet_link_tag params[:controller] %>+. -NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme - supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes. +NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes. h4. Asset Organization @@ -132,7 +132,7 @@ In regular views you can access images in the +assets/images+ directory like thi Provided that the pipeline is enabled within your application (and not disabled in the current environment context), this file is served by Sprockets. If a file exists at +public/assets/rails.png+ it is served by the webserver. -Alternatively, a request for a file with an MD5 hash such as +public/assets/rails-af27b6a414e6da00003503148be9b409.png+ is treated the same way. How these hashes are generated is covered in the "Production Assets":#production_assets section later on in this guide. +Alternatively, a request for a file with an MD5 hash such as +public/assets/rails-af27b6a414e6da00003503148be9b409.png+ is treated the same way. How these hashes are generated is covered in the "In Production":#in-production section later on in this guide. Sprockets will also look through the paths specified in +config.assets.paths+ which includes the standard application paths and any path added by Rails engines. @@ -164,15 +164,33 @@ Note that the closing tag cannot be of the style +-%>+. h5. CSS and Sass -When using the asset pipeline, paths to assets must be re-written and +sass-rails+ provides +_url+ and +_path+ helpers for the following asset classes: image, font, video, audio, javascript, stylesheet. +When using the asset pipeline, paths to assets must be re-written and +sass-rails+ provides +_url+ and +_path+ helpers for the following asset classes: image, font, video, audio, JavaScript and stylesheet. -* +image-url("rails.png")+ becomes +url(/assets/rails.png)+ -* +image-path("rails.png")+ becomes +"/assets/rails.png"+. +* +image_url("rails.png")+ becomes +url(/assets/rails.png)+. +* +image_path("rails.png")+ becomes +"/assets/rails.png"+. The more generic form can also be used but the asset path and class must both be specified: -* +asset-url("rails.png", image)+ becomes +url(/assets/rails.png)+ -* +asset-path("rails.png", image)+ becomes +"/assets/rails.png"+ +* +asset_url("rails.png", image)+ becomes +url(/assets/rails.png)+. +* +asset_path("rails.png", image)+ becomes +"/assets/rails.png"+. + +h5. JavaScript/CoffeeScript and ERB + +If you add an +erb+ extension to a JavaScript asset, making it something such as +application.js.erb+, then you can use the +asset_path+ helper in your JavaScript code: + + +$('#logo').attr({ + src: "<%= asset_path('logo.png') %>" +}); + + +This writes the path to the particular asset being referenced. + +Similarly, you can use the +asset_path+ helper in CoffeeScript files with +erb+ extension (eg. application.js.coffee.erb): + + +$('#logo').attr src: "<% asset_path('logo.png') %>" + h4. Manifest Files and Directives @@ -264,7 +282,7 @@ When debug mode is off Sprockets will concatenate and run the necessary preproce -Assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-validate+ Cache-Control HTTP header to reduce request overhead on subsequent requests -- on these the browser gets a 304 (not-modified) response. +Assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-revalidate+ Cache-Control HTTP header to reduce request overhead on subsequent requests -- on these the browser gets a 304 (not-modified) response. If any of the files in the manifest have changed between requests, the server responds with a new compiled file. @@ -320,7 +338,7 @@ The rake task is: bundle exec rake assets:precompile -Capistrano (v2.8.0+) has a recipe to handle this in deployment. Add the following line to +Capfile+: +Capistrano (v2.8.0 and above) has a recipe to handle this in deployment. Add the following line to +Capfile+: load 'deploy/assets' @@ -330,6 +348,8 @@ This links the folder specified in +config.assets.prefix+ to +shared/assets+. If It is important that this folder is shared between deployments so that remotely cached pages that reference the old compiled assets still work for the life of the cached page. +NOTE. If you are precompiling your assets locally, you can use +bundle install --without assets+ on the server to avoid installing the assets gems (the gems in the assets group in the Gemfile). + The default matcher for compiling files includes +application.js+, +application.css+ and all files that do not end in +js+ or +css+: @@ -342,7 +362,7 @@ If you have other manifests or individual stylesheets and JavaScript files to in config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js'] -The rake task also generates a +manifest.yml+ that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods and avoids handing the mapping requests back to Sprockets. Manifest file typically look like this: +The rake task also generates a +manifest.yml+ that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods and avoids handing the mapping requests back to Sprockets. A typical manifest file looks like: --- @@ -383,7 +403,21 @@ For Apache: -TODO: nginx instructions +For nginx: + + +location ~ ^/assets/ { + expires 1y; + add_header Cache-Control public; + + # Some browsers still send conditional-GET requests if there's a + # Last-Modified header or an ETag header even if they haven't + # reached the expiry date sent in the Expires header. + add_header Last-Modified ""; + add_header ETag ""; + break; +} + When files are precompiled, Sprockets also creates a "Gzip":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. This avoids the server having to do this for any requests; it can simply read the compressed files from disk. You must configure your server to use gzip compression and serve the compressed assets that will be stored in the +public/assets+ folder. The following configuration options can be used: @@ -394,16 +428,16 @@ For Apache: # 2 lines to serve pre-gzipped version RewriteCond %{REQUEST_FILENAME}.gz -s RewriteRule ^(.+) $1.gz [L] + - # without it, Content-Type will be "application/x-gzip" - - ForceType text/css - +# without these, Content-Type will be "application/x-gzip" + + ForceType text/css + - - ForceType text/javascript - - + + ForceType text/javascript + For nginx: @@ -431,7 +465,15 @@ On the first request the assets are compiled and cached as outlined in developme Sprockets also sets the +Cache-Control+ HTTP header to +max-age=31536000+. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache. -This mode uses more memory and is lower performance than the default. It is not recommended. +This mode uses more memory, performs poorer than the default and is not recommended. + +When deploying a production application to a system without any pre-existing JavaScript runtimes, you may want to add one to your Gemfile: + + +group :production do + gem 'therubyracer' +end + h3. Customizing the Pipeline @@ -461,7 +503,7 @@ config.assets.js_compressor = :uglifier The +config.assets.compress+ must be set to +true+ to enable JavaScript compression -NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme -- supported runtime in order to use +uglifier+. If you are using Mac OS X or Windows you have installed a JavaScript runtime in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes. +NOTE: You will need a "ExecJS":https://github.com/sstephenson/execjs#readme supported runtime in order to use +uglifier+. If you are using Mac OS X or Windows you have installed a JavaScript runtime in your operating system. Check "ExecJS":https://github.com/sstephenson/execjs#readme documentation to know all supported JavaScript runtimes. h4. Using Your Own Compressor @@ -509,7 +551,7 @@ WARNING: If you are upgrading an existing application and intend to use this opt h3. How Caching Works -Sprockets uses the default rails cache store to cache assets in development and production. +Sprockets uses the default Rails cache store to cache assets in development and production. TODO: Add more about changing the default store. diff --git a/railties/guides/source/contribute.textile b/railties/guides/source/contribute.textile deleted file mode 100644 index f9bb80861ca6e..0000000000000 --- a/railties/guides/source/contribute.textile +++ /dev/null @@ -1,70 +0,0 @@ -h2. Contribute to the Rails Guides - -Rails Guides aim to improve the Rails documentation and to make the barrier to entry as low as possible. A reasonably experienced developer should be able to use the guides to come up to speed on Rails quickly. Our sponsors have contributed prizes for those who write an entire guide, but there are many other ways to contribute. - -endprologue. - -h3. How to Contribute? - -* We have an open commit policy: anyone is welcome to contribute and to review contributions. -* "docrails is hosted on GitHub":https://github.com/lifo/docrails and has public write access. -* Guides are written in Textile, and reside at +railties/guides/source+ in the docrails project. -* Follow the "Rails Guides Conventions":https://wiki.github.com/lifo/docrails/rails-guides-conventions. -* Assets are stored in the +railties/guides/assets+ directory. -* Sample format : "Active Record Associations":https://github.com/lifo/docrails/blob/3e56a3832415476fdd1cb963980d0ae390ac1ed3/railties/guides/source/association_basics.textile. -* Sample output : "Active Record Associations":association_basics.html. -* You can build the Guides during testing by running +bundle exec rake generate_guides+ in the +railties+ directory. -* You're encouraged to validate XHTML for the generated guides before committing your changes by running +bundle exec rake validate_guides+ in the +railties+ directory. -* Edge guides "can be consulted online":http://edgeguides.rubyonrails.org/. That website is generated periodically from docrails. - -h3. What to Contribute? - -* We need authors, editors, proofreaders, and translators. Adding a single paragraph of quality content to a guide is a good way to get started. -* The easiest way to start is by improving an existing guide: -** Improve the structure to make it more coherent. -** Add missing information. -** Correct any factual errors. -** Fix typos or improve style. -** Bring it up to date with the latest Edge Rails. -* We're also open to suggestions for entire new guides: -** Contact lifo or fxn to get your idea approved. See the Contact section below. -** If you're the main author on a significant guide, you're eligible for the prizes. - -h3. How is the process? - -* The preferred way to contribute is to commit to docrails directly. -* A new guide is only edited by its author until finished though. -* If you are writing a new guide freely commit to docrails partial work and ping lifo or fxn when done with a first draft. -* Guides reviewers will then provide feedback, some of it possibly in form of direct commits to agilize the process. -* Eventually the guide will be approved and added to the index. - -h3. Prizes - -For each completed guide, the lead contributor will receive all of the following prizes: - -* $200 from Caboose Rails Documentation Project. -* 1 year of GitHub Micro account worth $84. -* 1 year of RPM Basic (Production performance management) for up to 10 hosts worth 12 months x $40 per host x 10 hosts = $4800. And also, savings of $45 per host per month over list price to upgrade to advanced product. - -h3. Rules - -* Guides are licensed under a Creative Commons Attribution-Share Alike 3.0 License. -* If you're not sure whether a guide is actively being worked on, stop by IRC and ask. -* If the same guide writer wants to write multiple guides, that's ideally the situation we'd love to be in! However, that guide writer will only receive the cash prize for all the subsequent guides (and not the GitHub or RPM prizes). -* Our review team will have the final say on whether the guide is complete and of good enough quality. - -All authors should read and follow the "Rails Guides Conventions":ruby_on_rails_guides_guidelines.html and the "Rails API Documentation Conventions":api_documentation_guidelines.html. - -h3. Translations - -The translation effort for the Rails Guides is just getting underway. We know about projects to translate the Guides into Spanish, Portuguese, Polish, and French. For more details or to get involved see the "Translating Rails Guides":https://wiki.github.com/lifo/docrails/translating-rails-guides page. - -h3. Mailing List - -"Ruby on Rails: Documentation":http://groups.google.com/group/rubyonrails-docs is the mailing list for all the guides/documentation related discussions. - -h3. Contact - -* IRC : #docrails channel in irc.freenode.net -* Twitter: "@docrails":http://twitter.com/docrails, "@lifo":http://twitter.com/lifo, "@fxn":http://twitter.com/fxn -* Email : pratiknaik aT gmail, fxn aT hashref dot com diff --git a/railties/guides/source/contributing_to_ruby_on_rails.textile b/railties/guides/source/contributing_to_ruby_on_rails.textile index e6ec061c9a14a..ef79185038907 100644 --- a/railties/guides/source/contributing_to_ruby_on_rails.textile +++ b/railties/guides/source/contributing_to_ruby_on_rails.textile @@ -258,16 +258,18 @@ h3. Contributing to the Rails Documentation Ruby on Rails has two main sets of documentation: The guides help you to learn Ruby on Rails, and the API is a reference. -You can create an issue in GitHub issues to fix or expand documentation. However, if you're confident about your changes you can push them yourself directly via "docrails":https://github.com/lifo/docrails/tree/master. docrails is a branch with an *open commit policy* and public write access. Commits to docrails are still reviewed, but that happens after they are pushed. docrails is merged with master regularly, so you are effectively editing the Ruby on Rails documentation. +You can help improve the Rails guides by making them more coherent, adding missing information, correcting factual errors, fixing typos, bringing it up to date with the latest edge Rails. To get involved in the translation of Rails guides, please see "Translating Rails Guides":https://wiki.github.com/lifo/docrails/translating-rails-guides. + +If you're confident about your changes, you can push them yourself directly via "docrails":https://github.com/lifo/docrails. docrails is a branch with an *open commit policy* and public write access. Commits to docrails are still reviewed, but that happens after they are pushed. docrails is merged with master regularly, so you are effectively editing the Ruby on Rails documentation. + +If you are unsure of the documentation changes, you can create an issue in the "Rails":https://github.com/rails/rails/issues issues tracker on GitHub. When working with documentation, please take into account the "API Documentation Guidelines":api_documentation_guidelines.html and the "Ruby on Rails Guides Guidelines":ruby_on_rails_guides_guidelines.html. -NOTE: As explained above, ordinary code patches should have proper documentation coverage. docrails is only used for isolated documentation improvements. +NOTE: As explained earlier, ordinary code patches should have proper documentation coverage. docrails is only used for isolated documentation improvements. WARNING: docrails has a very strict policy: no code can be touched whatsoever, no matter how trivial or small the change. Only RDoc and guides can be edited via docrails. Also, CHANGELOGs should never be edited in docrails. -If you have an idea for a new guide you can refer to the "contribution page":contribute.html for instructions on getting involved. - h3. Contributing to the Rails Code h4. Clone the Rails Repository diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 584b08f56c71f..09e3f1cffd055 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -550,9 +550,9 @@ folders, and edit config/routes.rb. Here's a quick overview of what it |app/views/posts/new.html.erb |A view to create a new post| |app/views/posts/_form.html.erb |A partial to control the overall look and feel of the form used in edit and new views| |app/helpers/posts_helper.rb |Helper functions to be used from the post views| -|app/assets/stylesheets/scaffold.css.scss |Cascading style sheet to make the scaffolded views look better| -|app/assets/stylesheets/post.css.scss |Cascading style sheet for the posts controller| -|app/assets/javascripts/post.js.coffee |CoffeeScript for the posts controller| +|app/assets/stylesheets/scaffolds.css.scss |Cascading style sheet to make the scaffolded views look better| +|app/assets/stylesheets/posts.css.scss |Cascading style sheet for the posts controller| +|app/assets/javascripts/posts.js.coffee |CoffeeScript for the posts controller| |test/unit/post_test.rb |Unit testing harness for the posts model| |test/functional/posts_controller_test.rb |Functional testing harness for the posts controller| |test/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper| @@ -822,8 +822,8 @@ this layout in your editor and modify the +body+ tag: Blog - <%= stylesheet_link_tag :all %> - <%= javascript_include_tag :defaults %> + <%= stylesheet_link_tag "application" %> + <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> @@ -1084,8 +1084,8 @@ def destroy @post.destroy respond_to do |format| - format.html { redirect_to(posts_url) } - format.json { render :json => {}, :status => :ok } + format.html { redirect_to posts_url } + format.json { head :ok } end end @@ -1454,7 +1454,7 @@ comment to a local variable named the same as the partial, in this case h4. Rendering a Partial Form -Lets also move that new comment section out to it's own partial, again, you +Let's also move that new comment section out to its own partial. Again, you create a file +app/views/comments/_form.html.erb+ and in it you put: @@ -1721,8 +1721,8 @@ This example shows another option of the render helper, being able to pass in local variables, in this case, we want the local variable +form+ in the partial to refer to the +post_form+ object. -We also add a @post.tags.build at the top of this form, this is to make -sure there is a new tag ready to have it's name filled in by the user. If you do +We also add a @post.tags.build at the top of this form. This is to make +sure there is a new tag ready to have its name filled in by the user. If you do not build the new tag, then the form will not appear as there is no new Tag object ready to create. diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb index 3ccbc3a477d12..4c979888b7949 100644 --- a/railties/guides/source/layout.html.erb +++ b/railties/guides/source/layout.html.erb @@ -91,7 +91,7 @@
  3. -
  4. Contribute
  5. +
  6. Contribute
  7. Credits
  8. @@ -129,6 +129,10 @@ <%= link_to 'Ruby on Rails Guides Guidelines', 'ruby_on_rails_guides_guidelines.html' %> for style and conventions.

    +

    + If for whatever reason you spot something to fix but cannot patch it yourself, please + <%= link_to 'open an issue', 'https://github.com/rails/rails/issues' %>. +

    And last but not least, any kind of discussion regarding Ruby on Rails documentation is very welcome in the <%= link_to 'rubyonrails-docs mailing list', 'http://groups.google.com/group/rubyonrails-docs' %>.

    diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile index 8d5985dba8a72..756d3da397604 100644 --- a/railties/guides/source/rails_on_rack.textile +++ b/railties/guides/source/rails_on_rack.textile @@ -207,7 +207,7 @@ config.middleware.clear # config.ru -use MyOwnStackFromStratch +use MyOwnStackFromScratch run ActionController::Dispatcher.new diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 0ca664e4f0561..a48db3b6d2b97 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -37,7 +37,8 @@ def initialize(*) @assets = ActiveSupport::OrderedOptions.new @assets.enabled = false @assets.paths = [] - @assets.precompile = [ /\w+\.(?!js|css).+/, /application.(css|js)$/ ] + @assets.precompile = [ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) }, + /application.(css|js)$/ ] @assets.prefix = "/assets" @assets.version = '' @assets.debug = false diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index b13a6cb24fa81..9e6e914e7a4c8 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -9,7 +9,7 @@ module Rails module Generators class AppBase < Base - DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db ) + DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver ) JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc ) DATABASES.concat(JDBC_DATABASES) @@ -154,12 +154,13 @@ def rails_gemfile_entry end def gem_for_database - # %w( mysql oracle postgresql sqlite3 frontbase ibm_db jdbcmysql jdbcsqlite3 jdbcpostgresql ) + # %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql ) case options[:database] when "oracle" then "ruby-oci8" when "postgresql" then "pg" when "frontbase" then "ruby-frontbase" when "mysql" then "mysql2" + when "sqlserver" then "activerecord-sqlserver-adapter" when "jdbcmysql" then "activerecord-jdbcmysql-adapter" when "jdbcsqlite3" then "activerecord-jdbcsqlite3-adapter" when "jdbcpostgresql" then "activerecord-jdbcpostgresql-adapter" @@ -209,7 +210,7 @@ def assets_gemfile_entry group :assets do gem 'sass-rails', #{options.dev? || options.edge? ? " :git => 'git://github.com/rails/sass-rails.git', :branch => '3-1-stable'" : " ~> 3.1.0".inspect} gem 'coffee-rails', #{options.dev? || options.edge? ? ":git => 'git://github.com/rails/coffee-rails.git', :branch => '3-1-stable'" : "~> 3.1.0".inspect} - gem 'uglifier' + gem 'uglifier', '>= 1.0.3' end GEMFILE end diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index 9afa4eb1890bb..2181164ae52af 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,8 +2,8 @@ module Rails module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - TINY = 0 - PRE = nil + TINY = 1 + PRE = "rc1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/railties/railties.gemspec b/railties/railties.gemspec index b9171288852bd..abe4577dfc7a3 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.bindir = 'bin' - s.executables = ['rails'] + s.executables = [] s.rdoc_options << '--exclude' << '.' diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index a7ab051a8b07d..ec68f9f2daaa1 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- require 'isolation/abstract_unit' require 'active_support/core_ext/kernel/reporting' require 'rack/test' @@ -64,11 +65,49 @@ def app end end + test "precompile application.js and application.css and all other files not ending with .js or .css by default" do + app_file "app/assets/javascripts/application.js", "alert();" + app_file "app/assets/stylesheets/application.css", "body{}" + app_file "app/assets/javascripts/something.min.js", "alert();" + app_file "app/assets/stylesheets/something.min.css", "body{}" + + images_should_compile = ["a.png", "happyface.png", "happy_face.png", "happy.face.png", + "happy-face.png", "happy.happy_face.png", "happy_happy.face.png", + "happy.happy.face.png", "happy", "happy.face", "-happyface", + "-happy.png", "-happy.face.png", "_happyface", "_happy.face.png", + "_happy.png"] + images_should_compile.each do |filename| + app_file "app/assets/images/#{filename}", "happy" + end + + capture(:stdout) do + Dir.chdir(app_path){ `bundle exec rake assets:precompile` } + end + + images_should_compile.each do |filename| + assert File.exists?("#{app_path}/public/assets/#{filename}") + end + assert File.exists?("#{app_path}/public/assets/application.js") + assert File.exists?("#{app_path}/public/assets/application.css") + assert !File.exists?("#{app_path}/public/assets/something.min.js") + assert !File.exists?("#{app_path}/public/assets/something.min.css") + end + + test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do + add_to_config "config.assets.digest = true" + add_to_config "config.action_controller.perform_caching = false" + + ENV["RAILS_ENV"] = "production" + require "#{app_path}/config/environment" + + assert_equal Sprockets::Index, Rails.application.assets.class + end + test "precompile creates a manifest file with all the assets listed" do app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>" app_file "app/assets/javascripts/application.js", "alert();" # digest is default in false, we must enable it for test environment - app_file "config/initializers/compile.rb", "Rails.application.config.assets.digest = true" + add_to_config "config.assets.digest = true" capture(:stdout) do Dir.chdir(app_path){ `bundle exec rake assets:precompile` } @@ -84,10 +123,10 @@ def app test "precompile creates a manifest file in a custom path with all the assets listed" do app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>" app_file "app/assets/javascripts/application.js", "alert();" - FileUtils.mkdir "#{app_path}/shared" - app_file "config/initializers/manifest.rb", "Rails.application.config.assets.manifest = '#{app_path}/shared'" # digest is default in false, we must enable it for test environment - app_file "config/initializers/compile.rb", "Rails.application.config.assets.digest = true" + add_to_config "config.assets.digest = true" + add_to_config "config.assets.manifest = '#{app_path}/shared'" + FileUtils.mkdir "#{app_path}/shared" capture(:stdout) do Dir.chdir(app_path){ `bundle exec rake assets:precompile` } @@ -103,9 +142,9 @@ def app test "the manifest file should be saved by default in the same assets folder" do app_file "app/assets/javascripts/application.js", "alert();" - app_file "config/initializers/manifest.rb", "Rails.application.config.assets.prefix = '/x'" # digest is default in false, we must enable it for test environment - app_file "config/initializers/compile.rb", "Rails.application.config.assets.digest = true" + add_to_config "config.assets.digest = true" + add_to_config "config.assets.prefix = '/x'" capture(:stdout) do Dir.chdir(app_path){ `bundle exec rake assets:precompile` } @@ -119,7 +158,7 @@ def app test "precompile does not append asset digests when config.assets.digest is false" do app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>" app_file "app/assets/javascripts/application.js", "alert();" - app_file "config/initializers/compile.rb", "Rails.application.config.assets.digest = false" + add_to_config "config.assets.digest = false" capture(:stdout) do Dir.chdir(app_path){ `bundle exec rake assets:precompile` } @@ -137,6 +176,7 @@ def app test "assets do not require any assets group gem when manifest file is present" do app_file "app/assets/javascripts/application.js", "alert();" + app_file "config/initializers/serve_static_assets.rb", "Rails.application.config.serve_static_assets = true" ENV["RAILS_ENV"] = "production" capture(:stdout) do @@ -182,7 +222,7 @@ class ::PostsController < ActionController::Base ; end test "assets raise AssetNotPrecompiledError when manifest file is present and requested file isn't precompiled if digest is disabled" do app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'app' %>" - app_file "config/initializers/compile.rb", "Rails.application.config.assets.compile = false" + add_to_config "config.assets.compile = false" app_file "config/routes.rb", <<-RUBY AppTemplate::Application.routes.draw do @@ -209,7 +249,7 @@ class ::PostsController < ActionController::Base ; end test "precompile appends the md5 hash to files referenced with asset_path and run in the provided RAILS_ENV" do app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>" # digest is default in false, we must enable it for test environment - app_file "config/initializers/compile.rb", "Rails.application.config.assets.digest = true" + add_to_config "config.assets.digest = true" # capture(:stdout) do Dir.chdir(app_path){ `bundle exec rake assets:precompile RAILS_ENV=test` } @@ -220,7 +260,7 @@ class ::PostsController < ActionController::Base ; end test "precompile appends the md5 hash to files referenced with asset_path and run in production as default even using RAILS_GROUPS=assets" do app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>" - app_file "config/initializers/compile.rb", "Rails.application.config.assets.compile = true" + add_to_config "config.assets.compile = true" ENV["RAILS_ENV"] = nil capture(:stdout) do @@ -230,6 +270,22 @@ class ::PostsController < ActionController::Base ; end assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file)) end + test "precompile should handle utf8 filenames" do + app_file "app/assets/images/レイルズ.png", "not a image really" + add_to_config "config.assets.precompile = [ /\.png$$/, /application.(css|js)$/ ]" + + capture(:stdout) do + Dir.chdir(app_path){ `bundle exec rake assets:precompile` } + end + + assert File.exists?("#{app_path}/public/assets/レイルズ.png") + + manifest = "#{app_path}/public/assets/manifest.yml" + + assets = YAML.load_file(manifest) + assert_equal "レイルズ.png", assets["レイルズ.png"] + end + test "assets are cleaned up properly" do app_file "public/assets/application.js", "alert();" app_file "public/assets/application.css", "a { color: green; }" @@ -243,6 +299,17 @@ class ::PostsController < ActionController::Base ; end assert_equal 0, files.length, "Expected no assets, but found #{files.join(', ')}" end + test "assets routes are not drawn when compilation is disabled" do + app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();" + add_to_config "config.assets.compile = false" + + ENV["RAILS_ENV"] = "production" + require "#{app_path}/config/environment" + + get "/assets/demo.js" + assert_equal 404, last_response.status + end + test "does not stream session cookies back" do app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();" diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 18846458e9c79..2dcdf14a8c806 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -1,3 +1,4 @@ +# coding:utf-8 require "isolation/abstract_unit" module ApplicationTests @@ -109,6 +110,55 @@ def test_model_and_migration_generator_with_change_syntax assert_match(/AddEmailToUsers: reverted/, output) end + def test_migration_status_when_schema_migrations_table_is_not_present + output = Dir.chdir(app_path){ `rake db:migrate:status` } + assert_equal "Schema migrations table does not exist yet.\n", output + end + + def test_migration_status + Dir.chdir(app_path) do + `rails generate model user username:string password:string` + `rails generate migration add_email_to_users email:string` + end + + Dir.chdir(app_path) { `rake db:migrate`} + output = Dir.chdir(app_path) { `rake db:migrate:status` } + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + + Dir.chdir(app_path) { `rake db:rollback STEP=1` } + output = Dir.chdir(app_path) { `rake db:migrate:status` } + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) + end + + def test_migration_status_after_rollback_and_redo + Dir.chdir(app_path) do + `rails generate model user username:string password:string` + `rails generate migration add_email_to_users email:string` + end + + Dir.chdir(app_path) { `rake db:migrate`} + output = Dir.chdir(app_path) { `rake db:migrate:status` } + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + + Dir.chdir(app_path) { `rake db:rollback STEP=2` } + output = Dir.chdir(app_path) { `rake db:migrate:status` } + + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) + + Dir.chdir(app_path) { `rake db:migrate:redo` } + output = Dir.chdir(app_path) { `rake db:migrate:status` } + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + end + def test_loading_specific_fixtures Dir.chdir(app_path) do `rails generate model user username:string password:string` @@ -125,5 +175,14 @@ def test_loading_specific_fixtures assert_equal 2, ::AppTemplate::Application::Product.count assert_equal 0, ::AppTemplate::Application::User.count end + + def test_scaffold_tests_pass_by_default + content = Dir.chdir(app_path) do + `rails generate scaffold user username:string password:string` + `bundle exec rake db:migrate db:test:clone test` + end + + assert_match(/7 tests, 10 assertions, 0 failures, 0 errors/, content) + end end end diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb index 94dec405a7752..0491fc21747bc 100644 --- a/railties/test/railties/mounted_engine_test.rb +++ b/railties/test/railties/mounted_engine_test.rb @@ -11,13 +11,17 @@ def setup add_to_config("config.action_dispatch.show_exceptions = false") + @simple_plugin = engine "weblog" @plugin = engine "blog" app_file 'config/routes.rb', <<-RUBY AppTemplate::Application.routes.draw do + mount Weblog::Engine, :at => '/', :as => 'weblog' resources :posts match "/engine_route" => "application_generating#engine_route" match "/engine_route_in_view" => "application_generating#engine_route_in_view" + match "/weblog_engine_route" => "application_generating#weblog_engine_route" + match "/weblog_engine_route_in_view" => "application_generating#weblog_engine_route_in_view" match "/url_for_engine_route" => "application_generating#url_for_engine_route" match "/polymorphic_route" => "application_generating#polymorphic_route" match "/application_polymorphic_path" => "application_generating#application_polymorphic_path" @@ -28,6 +32,29 @@ def setup end RUBY + + @simple_plugin.write "lib/weblog.rb", <<-RUBY + module Weblog + class Engine < ::Rails::Engine + end + end + RUBY + + @simple_plugin.write "config/routes.rb", <<-RUBY + Weblog::Engine.routes.draw do + match '/weblog' => "weblogs#index", :as => 'weblogs' + end + RUBY + + @simple_plugin.write "app/controllers/weblogs_controller.rb", <<-RUBY + class WeblogsController < ActionController::Base + def index + render :text => request.url + end + end + RUBY + + @plugin.write "app/models/blog/post.rb", <<-RUBY module Blog class Post @@ -100,6 +127,14 @@ def engine_route_in_view render :inline => "<%= blog.posts_path %>" end + def weblog_engine_route + render :text => weblog.weblogs_path + end + + def weblog_engine_route_in_view + render :inline => "<%= weblog.weblogs_path %>" + end + def url_for_engine_route render :text => blog.url_for(:controller => "blog/posts", :action => "index", :user => "john", :only_path => true) end @@ -192,5 +227,13 @@ def script_name(script_name) get "/application_polymorphic_path" assert_equal "/posts/44", last_response.body end + + test "route path for controller action when engine is mounted at root" do + get "/weblog_engine_route" + assert_equal "/weblog", last_response.body + + get "/weblog_engine_route_in_view" + assert_equal "/weblog", last_response.body + end end end diff --git a/version.rb b/version.rb index 9afa4eb1890bb..2181164ae52af 100644 --- a/version.rb +++ b/version.rb @@ -2,8 +2,8 @@ module Rails module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - TINY = 0 - PRE = nil + TINY = 1 + PRE = "rc1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end
About Us