diff options
75 files changed, 677 insertions, 247 deletions
@@ -99,3 +99,6 @@ if ENV['ORACLE_ENHANCED_PATH'] || ENV['ORACLE_ENHANCED'] gem "activerecord-oracle_enhanced-adapter", :git => "git://github.com/rsim/oracle-enhanced.git" end end + +# A gem necessary for ActiveRecord tests with IBM DB +gem "ibm_db" if ENV['IBM_DB'] diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index c3ff677529..e8c619723f 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -56,6 +56,46 @@ *Rails 3.1.1 (unreleased)* +* javascript_path and stylesheet_path now refer to /assets if asset pipelining +is on. [Santiago Pastorino] + +* button_to support form option. Now you're able to pass for example +'data-type' => 'json'. [ihower] + +* image_path and image_tag should use /assets if asset pipelining is turned +on. Closes #3126 [Santiago Pastorino and christos] + +* Avoid use of existing precompiled assets during rake assets:precompile run. +Closes #3119 [Guillermo Iguaran] + +* Copy assets to nondigested filenames too [Santiago Pastorino] + +* Give precedence to `config.digest = false` over the existence of +manifest.yml asset digests [christos] + +* escape options for the stylesheet_link_tag method [Alexey Vakhov] + +* Re-launch assets:precompile task using (Rake.)ruby instead of Kernel.exec so +it works on Windows [cablegram] + +* env var passed to process shouldn't be modified in process method. [Santiago +Pastorino] + +* `rake assets:precompile` loads the application but does not initialize +it. + + To the app developer, this means configuration add in + config/initializers/* will not be executed. + + Plugins developers need to special case their initializers that are + meant to be run in the assets group by adding :group => :assets. [José Valim] + +* Sprockets uses config.assets.prefix for asset_path [asee] + +* FileStore key_file_path properly limit filenames to 255 characters. [phuibonhoa] + +* Fix Hash#to_query edge case with html_safe strings. [brainopia] + * 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] diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index f1b7966b9c..96d583730a 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -16,16 +16,16 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.requirements << 'none' - s.add_dependency('activesupport', version) - s.add_dependency('activemodel', version) - s.add_dependency('rack-cache', '~> 1.0.3') - s.add_dependency('builder', '~> 3.0.0') - s.add_dependency('i18n', '~> 0.6') - s.add_dependency('rack', '~> 1.3.2') - s.add_dependency('rack-test', '~> 0.6.1') - s.add_dependency('journey', '~> 1.0.0') - s.add_dependency('sprockets', '~> 2.0.0') - s.add_dependency('erubis', '~> 2.7.0') + s.add_dependency('activesupport', version) + s.add_dependency('activemodel', version) + s.add_dependency('rack-cache', '~> 1.1') + s.add_dependency('builder', '~> 3.0.0') + s.add_dependency('i18n', '~> 0.6') + s.add_dependency('rack', '~> 1.3.2') + s.add_dependency('rack-test', '~> 0.6.1') + s.add_dependency('journey', '~> 1.0.0') + s.add_dependency('sprockets', '~> 2.0.2') + s.add_dependency('erubis', '~> 2.7.0') - s.add_development_dependency('tzinfo', '~> 0.3.29') + s.add_development_dependency('tzinfo', '~> 0.3.29') end diff --git a/actionpack/lib/abstract_controller/asset_paths.rb b/actionpack/lib/abstract_controller/asset_paths.rb index b104d34fb5..c2a6809f58 100644 --- a/actionpack/lib/abstract_controller/asset_paths.rb +++ b/actionpack/lib/abstract_controller/asset_paths.rb @@ -3,7 +3,8 @@ module AbstractController extend ActiveSupport::Concern included do - config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir, :stylesheets_dir + config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir, + :stylesheets_dir, :default_asset_host_protocol end end end diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index ab2c532859..41fdc11196 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -120,8 +120,6 @@ module AbstractController view_renderer.render(view_context, options) end - private - DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w( @_action_name @_response_body @_formats @_prefixes @_config @_view_context_class @_view_renderer @_lookup_context @@ -139,6 +137,8 @@ module AbstractController hash end + private + # Normalize args and options. # :api: private def _normalize_render(*args, &block) diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index a83fa74795..6913c1ef4a 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -333,9 +333,21 @@ module ActionController module ClassMethods # Sets the controller class name. Useful if the name can't be inferred from test class. - # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>. + # Normalizes +controller_class+ before using. Examples: + # + # tests WidgetController + # tests :widget + # tests 'widget' + # def tests(controller_class) - self.controller_class = controller_class + case controller_class + when String, Symbol + self.controller_class = "#{controller_class.to_s.underscore}_controller".camelize.constantize + when Class + self.controller_class = controller_class + else + raise ArgumentError, "controller class must be a String, Symbol, or Class" + end end def controller_class=(new_class) @@ -352,9 +364,7 @@ module ActionController end def determine_default_controller_class(name) - name.sub(/Test$/, '').constantize - rescue NameError - nil + name.sub(/Test$/, '').safe_constantize end def prepare_controller_class(new_class) diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb index eaefdc0f15..af06bffa16 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb @@ -1,4 +1,5 @@ require 'set' +require 'cgi' require 'active_support/core_ext/class/attribute' module HTML diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index caa1decb9e..170c68f3e0 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -64,14 +64,16 @@ module ActionDispatch end def host_or_subdomain_and_domain(options) - return options[:host] unless options[:subdomain] || options[:domain] + return options[:host] if options[:subdomain].nil? && options[:domain].nil? tld_length = options[:tld_length] || @@tld_length host = "" - host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)) - host << "." - host << (options[:domain] || extract_domain(options[:host], tld_length)) + unless options[:subdomain] == false + host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)) + host << "." + end + host << (options[:domain] || extract_domain(options[:host], tld_length)) host end end diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index 30048cd48a..8fc8dc191b 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -116,9 +116,10 @@ module ActionDispatch # If <tt>:only_path</tt> is false, this option must be # provided either explicitly, or via +default_url_options+. # * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+ - # to split the domain from the host. - # * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+ # to split the subdomain from the host. + # If false, removes all subdomains from the host part of the link. + # * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+ + # to split the domain from the host. # * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if # <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to # <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1. diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb index f668b81b45..b08ff41950 100644 --- a/actionpack/lib/action_dispatch/testing/test_process.rb +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -5,12 +5,7 @@ require 'active_support/core_ext/hash/indifferent_access' module ActionDispatch module TestProcess def assigns(key = nil) - assigns = {}.with_indifferent_access - @controller.instance_variable_names.each do |ivar| - next if ActionController::Base.protected_instance_variables.include?(ivar) - assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar) - end - + assigns = @controller.view_assigns.with_indifferent_access key.nil? ? assigns : assigns[key] end diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb index cf30ad7e57..1d16e34df6 100644 --- a/actionpack/lib/action_view/asset_paths.rb +++ b/actionpack/lib/action_view/asset_paths.rb @@ -16,8 +16,6 @@ module ActionView # roots. Rewrite the asset path for cache-busting asset ids. Include # asset host, if configured, with the correct request protocol. # - # When include_host is true and the asset host does not specify the protocol - # the protocol parameter specifies how the protocol will be added. # 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) @@ -25,11 +23,10 @@ module ActionView source = source.to_s return source if is_uri?(source) - 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 = rewrite_host_and_protocol(source, options[:protocol]) source end @@ -89,9 +86,7 @@ module ActionView end def default_protocol - protocol = @config.action_controller.default_asset_host_protocol if @config.action_controller.present? - protocol ||= @config.default_asset_host_protocol - protocol || (has_request? ? :request : :relative) + @config.default_asset_host_protocol || (has_request? ? :request : :relative) end def invalid_asset_host!(help_message) @@ -120,19 +115,11 @@ module ActionView end def relative_url_root - if config.action_controller.present? - config.action_controller.relative_url_root - else - config.relative_url_root - end + config.relative_url_root end def asset_host_config - if config.action_controller.present? - config.action_controller.asset_host - else - config.asset_host - end + config.asset_host end # Returns the current request if one exists. diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index acd5e46e33..0c2e1aa3a9 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -279,6 +279,7 @@ module ActionView # processed normally, otherwise no action is taken. # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the # submit behavior. By default this behavior is an ajax submit. + # * <tt>:form</tt> - This hash will be form attributes # * <tt>:form_class</tt> - This controls the class of the form within which the submit button will # be placed # @@ -295,6 +296,12 @@ module ActionView # # </form>" # # + # <%= button_to "Create", :action => "create", :remote => true, :form => { "data-type" => "json" } %> + # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json"> + # # <div><input value="Create" type="submit" /></div> + # # </form>" + # + # # <%= button_to "Delete Image", { :action => "delete", :id => @image.id }, # :confirm => "Are you sure?", :method => :delete %> # # => "<form method="post" action="/images/delete/1" class="button_to"> @@ -324,10 +331,11 @@ module ActionView end form_method = method.to_s == 'get' ? 'get' : 'post' - form_class = html_options.delete('form_class') || 'button_to' - + form_options = html_options.delete('form') || {} + form_options[:class] ||= html_options.delete('form_class') || 'button_to' + remote = html_options.delete('remote') - + request_token_tag = '' if form_method == 'post' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) @@ -340,8 +348,10 @@ module ActionView html_options.merge!("type" => "submit", "value" => name) - ("<form method=\"#{form_method}\" action=\"#{ERB::Util.html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"#{ERB::Util.html_escape(form_class)}\"><div>" + - method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe + form_options.merge!(:method => form_method, :action => url) + form_options.merge!("data-remote" => "true") if remote + + "#{tag(:form, form_options, true)}<div>#{method_tag}#{tag("input", html_options)}#{request_token_tag}</div></form>".html_safe end diff --git a/actionpack/lib/sprockets/assets.rake b/actionpack/lib/sprockets/assets.rake index e29661e4e7..a5145080c2 100644 --- a/actionpack/lib/sprockets/assets.rake +++ b/actionpack/lib/sprockets/assets.rake @@ -1,60 +1,95 @@ +require "fileutils" + namespace :assets do + def ruby_rake_task(task) + env = ENV['RAILS_ENV'] || 'production' + groups = ENV['RAILS_GROUPS'] || 'assets' + args = [$0, task,"RAILS_ENV=#{env}","RAILS_GROUPS=#{groups}"] + args << "--trace" if Rake.application.options.trace + ruby *args + end + + # We are currently running with no explicit bundler group + # and/or no explicit environment - we have to reinvoke rake to + # execute this task. + def invoke_or_reboot_rake_task(task) + if ENV['RAILS_GROUPS'].to_s.empty? || ENV['RAILS_ENV'].to_s.empty? + ruby_rake_task task + else + Rake::Task[task].invoke + end + end + desc "Compile all the assets named in config.assets.precompile" task :precompile do - # We need to do this dance because RAILS_GROUPS is used - # too early in the boot process and changing here is already too late. - if ENV["RAILS_GROUPS"].to_s.empty? || ENV["RAILS_ENV"].to_s.empty? - ENV["RAILS_GROUPS"] ||= "assets" - ENV["RAILS_ENV"] ||= "production" - ruby $0, *ARGV - else - require "fileutils" - Rake::Task["tmp:cache:clear"].invoke - Rake::Task["assets:environment"].invoke + invoke_or_reboot_rake_task "assets:precompile:all" + end + namespace :precompile do + def internal_precompile(digest=nil) unless Rails.application.config.assets.enabled - raise "Cannot precompile assets if sprockets is disabled. Please set config.assets.enabled to true" + warn "Cannot precompile assets if sprockets is disabled. Please set config.assets.enabled to true" + exit end - # Ensure that action view is loaded and the appropriate sprockets hooks get executed - ActionView::Base + # Ensure that action view is loaded and the appropriate + # sprockets hooks get executed + _ = ActionView::Base config = Rails.application.config config.assets.compile = true - config.assets.digest = false if ENV["RAILS_ASSETS_NONDIGEST"] - - env = Rails.application.assets - - # Always compile files and avoid use of existing precompiled assets - config.assets.compile = true + config.assets.digest = digest unless digest.nil? config.assets.digests = {} - target = File.join(Rails.public_path, config.assets.prefix) - static_compiler = Sprockets::StaticCompiler.new(env, target, :digest => config.assets.digest) + env = Rails.application.assets + target = File.join(Rails.public_path, config.assets.prefix) + compiler = Sprockets::StaticCompiler.new(env, + target, + config.assets.precompile, + :manifest_path => config.assets.manifest, + :digest => config.assets.digest, + :manifest => digest.nil?) + compiler.compile + end - manifest = static_compiler.precompile(config.assets.precompile) - manifest_path = config.assets.manifest || target - FileUtils.mkdir_p(manifest_path) + task :all do + Rake::Task["assets:precompile:primary"].invoke + # We need to reinvoke in order to run the secondary digestless + # asset compilation run - a fresh Sprockets environment is + # required in order to compile digestless assets as the + # environment has already cached the assets on the primary + # run. + ruby_rake_task "assets:precompile:nondigest" if Rails.application.config.assets.digest + end - unless ENV["RAILS_ASSETS_NONDIGEST"] - File.open("#{manifest_path}/manifest.yml", 'wb') do |f| - YAML.dump(manifest, f) - end - ENV["RAILS_ASSETS_NONDIGEST"] = "true" - ruby $0, *ARGV - end + task :primary => ["assets:environment", "tmp:cache:clear"] do + internal_precompile + end + + task :nondigest => ["assets:environment", "tmp:cache:clear"] do + internal_precompile(false) end end desc "Remove compiled assets" - task :clean => ['assets:environment', 'tmp:cache:clear'] do - config = Rails.application.config - public_asset_path = File.join(Rails.public_path, config.assets.prefix) - rm_rf public_asset_path, :secure => true + task :clean do + invoke_or_reboot_rake_task "assets:clean:all" + end + + namespace :clean do + task :all => ["assets:environment", "tmp:cache:clear"] do + config = Rails.application.config + public_asset_path = File.join(Rails.public_path, config.assets.prefix) + rm_rf public_asset_path, :secure => true + end end task :environment do - Rails.application.initialize!(:assets) - Sprockets::Bootstrap.new(Rails.application).run + if Rails.application.config.assets.initialize_on_precompile + Rake::Task["environment"].invoke + else + Rails.application.initialize!(:assets) + Sprockets::Bootstrap.new(Rails.application).run + end end end diff --git a/actionpack/lib/sprockets/helpers.rb b/actionpack/lib/sprockets/helpers.rb index a952a55c5e..fee48386e0 100644 --- a/actionpack/lib/sprockets/helpers.rb +++ b/actionpack/lib/sprockets/helpers.rb @@ -1,5 +1,6 @@ module Sprockets module Helpers - autoload :RailsHelper, "sprockets/helpers/rails_helper" + autoload :RailsHelper, "sprockets/helpers/rails_helper" + autoload :IsolatedHelper, "sprockets/helpers/isolated_helper" end end diff --git a/actionpack/lib/sprockets/helpers/isolated_helper.rb b/actionpack/lib/sprockets/helpers/isolated_helper.rb new file mode 100644 index 0000000000..3adb928c45 --- /dev/null +++ b/actionpack/lib/sprockets/helpers/isolated_helper.rb @@ -0,0 +1,13 @@ +module Sprockets + module Helpers + module IsolatedHelper + def controller + nil + end + + def config + Rails.application.config.action_controller + end + end + end +end diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb index c8c6c3ddd9..f866bc626e 100644 --- a/actionpack/lib/sprockets/helpers/rails_helper.rb +++ b/actionpack/lib/sprockets/helpers/rails_helper.rb @@ -8,9 +8,6 @@ module Sprockets def asset_paths @asset_paths ||= begin - config = self.config if respond_to?(:config) - config ||= Rails.application.config - controller = self.controller if respond_to?(:controller) paths = RailsHelper::AssetPaths.new(config, controller) paths.asset_environment = asset_environment paths.asset_digests = asset_digests @@ -65,6 +62,16 @@ module Sprockets end alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route + def javascript_path(source) + asset_path(source) + end + alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with an javascript_path named route + + def stylesheet_path(source) + asset_path(source) + end + alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with an stylesheet_path named route + private def debug_assets? compile_assets? && (Rails.application.config.assets.debug || params[:debug_assets]) diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index 6b67fb1d2d..3d330bd91a 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -1,3 +1,5 @@ +require "action_controller/railtie" + module Sprockets autoload :Bootstrap, "sprockets/bootstrap" autoload :Helpers, "sprockets/helpers" @@ -8,13 +10,13 @@ module Sprockets # TODO: Get rid of config.assets.enabled class Railtie < ::Rails::Railtie - config.default_asset_host_protocol = :relative + config.action_controller.default_asset_host_protocol = :relative rake_tasks do load "sprockets/assets.rake" end - initializer "sprockets.environment", :group => :assets do |app| + initializer "sprockets.environment", :group => :all do |app| config = app.config next unless config.assets.enabled @@ -41,8 +43,8 @@ module Sprockets ActiveSupport.on_load(:action_view) do include ::Sprockets::Helpers::RailsHelper - app.assets.context_class.instance_eval do + include ::Sprockets::Helpers::IsolatedHelper include ::Sprockets::Helpers::RailsHelper end end diff --git a/actionpack/lib/sprockets/static_compiler.rb b/actionpack/lib/sprockets/static_compiler.rb index 4a0078be46..32a9d66e6e 100644 --- a/actionpack/lib/sprockets/static_compiler.rb +++ b/actionpack/lib/sprockets/static_compiler.rb @@ -2,41 +2,50 @@ require 'fileutils' module Sprockets class StaticCompiler - attr_accessor :env, :target, :digest + attr_accessor :env, :target, :paths - def initialize(env, target, options = {}) + def initialize(env, target, paths, options = {}) @env = env @target = target + @paths = paths @digest = options.key?(:digest) ? options.delete(:digest) : true + @manifest = options.key?(:manifest) ? options.delete(:manifest) : true + @manifest_path = options.delete(:manifest_path) || target end - def precompile(paths) - Rails.application.config.assets.digest = digest + def compile manifest = {} - env.each_logical_path do |logical_path| - next unless precompile_path?(logical_path, paths) + next unless compile_path?(logical_path) if asset = env.find_asset(logical_path) - manifest[logical_path] = compile(asset) + manifest[logical_path] = write_asset(asset) end end - manifest + write_manifest(manifest) if @manifest end - def compile(asset) - asset_path = digest_asset(asset) - filename = File.join(target, asset_path) - FileUtils.mkdir_p File.dirname(filename) - asset.write_to(filename) - asset.write_to("#{filename}.gz") if filename.to_s =~ /\.(css|js)$/ - asset_path + def write_manifest(manifest) + FileUtils.mkdir_p(@manifest_path) + File.open("#{@manifest_path}/manifest.yml", 'wb') do |f| + YAML.dump(manifest, f) + end + end + + def write_asset(asset) + path_for(asset).tap do |path| + filename = File.join(target, path) + FileUtils.mkdir_p File.dirname(filename) + asset.write_to(filename) + asset.write_to("#{filename}.gz") if filename.to_s =~ /\.(css|js)$/ + end end - def precompile_path?(logical_path, paths) + def compile_path?(logical_path) paths.each do |path| - if path.is_a?(Regexp) + case path + when Regexp return true if path.match(logical_path) - elsif path.is_a?(Proc) + when Proc return true if path.call(logical_path) else return true if File.fnmatch(path.to_s, logical_path) @@ -45,8 +54,8 @@ module Sprockets false end - def digest_asset(asset) - digest ? asset.digest_path : asset.logical_path + def path_for(asset) + @digest ? asset.digest_path : asset.logical_path end end end diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index cba3aded2f..b64e275363 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -146,6 +146,17 @@ XML end end + class ViewAssignsController < ActionController::Base + def test_assigns + @foo = "foo" + render :nothing => true + end + + def view_assigns + { "bar" => "bar" } + end + end + def test_raw_post_handling params = ActiveSupport::OrderedHash[:page, {:name => 'page name'}, 'some key', 123] post :render_raw_post, params.dup @@ -256,6 +267,15 @@ XML assert_equal "foo", assigns["foo"] end + def test_view_assigns + @controller = ViewAssignsController.new + process :test_assigns + assert_equal nil, assigns(:foo) + assert_equal nil, assigns[:foo] + assert_equal "bar", assigns(:bar) + assert_equal "bar", assigns[:bar] + end + def test_assert_tag_tag process :test_html_output @@ -754,6 +774,22 @@ class CrazyNameTest < ActionController::TestCase end end +class CrazySymbolNameTest < ActionController::TestCase + tests :content + + def test_set_controller_class_using_symbol + assert_equal ContentController, self.class.controller_class + end +end + +class CrazyStringNameTest < ActionController::TestCase + tests 'content' + + def test_set_controller_class_using_string + assert_equal ContentController, self.class.controller_class + end +end + class NamedRoutesControllerTest < ActionController::TestCase tests ContentController diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 484e996f31..11ced2df2a 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -67,6 +67,20 @@ module AbstractController ) end + def test_subdomain_may_be_removed + add_host! + assert_equal('http://basecamphq.com/c/a/i', + W.new.url_for(:subdomain => false, :controller => 'c', :action => 'a', :id => 'i') + ) + end + + def test_multiple_subdomains_may_be_removed + W.default_url_options[:host] = 'mobile.www.api.basecamphq.com' + assert_equal('http://basecamphq.com/c/a/i', + W.new.url_for(:subdomain => false, :controller => 'c', :action => 'a', :id => 'i') + ) + end + def test_domain_may_be_changed add_host! assert_equal('http://www.37signals.com/c/a/i', diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 060bcfb5ec..a611252b31 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -15,6 +15,7 @@ class RequestTest < ActiveSupport::TestCase assert_equal 'http://www.example.com', url_for assert_equal 'http://api.example.com', url_for(:subdomain => 'api') + assert_equal 'http://example.com', url_for(:subdomain => false) assert_equal 'http://www.ror.com', url_for(:domain => 'ror.com') assert_equal 'http://api.ror.co.uk', url_for(:host => 'www.ror.co.uk', :subdomain => 'api', :tld_length => 2) assert_equal 'http://www.example.com:8080', url_for(:port => 8080) diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb index b1e6db7b34..08b66fec8b 100644 --- a/actionpack/test/template/sprockets_helper_test.rb +++ b/actionpack/test/template/sprockets_helper_test.rb @@ -28,7 +28,6 @@ class SprocketsHelperTest < ActionView::TestCase application = Struct.new(:config, :assets).new(config, @assets) Rails.stubs(:application).returns(application) @config = config - @config.action_controller ||= ActiveSupport::InheritableOptions.new @config.perform_caching = true @config.assets.digest = true @config.assets.compile = true @@ -38,6 +37,10 @@ class SprocketsHelperTest < ActionView::TestCase "http://www.example.com" end + def config + @controller ? @controller.config : @config + end + test "asset_path" do assert_match %r{/assets/logo-[0-9a-f]+.png}, asset_path("logo.png") @@ -75,8 +78,9 @@ class SprocketsHelperTest < ActionView::TestCase end test "with a simple asset host the url should default to protocol relative" do + @controller.config.default_asset_host_protocol = :relative @controller.config.asset_host = "assets-%d.example.com" - assert_match %r{//assets-\d.example.com/assets/logo-[0-9a-f]+.png}, + assert_match %r{^//assets-\d.example.com/assets/logo-[0-9a-f]+.png}, asset_path("logo.png") end @@ -88,10 +92,11 @@ class SprocketsHelperTest < ActionView::TestCase end test "With a proc asset host that returns no protocol the url should be protocol relative" do + @controller.config.default_asset_host_protocol = :relative @controller.config.asset_host = Proc.new do |asset| "assets-999.example.com" end - assert_match %r{//assets-999.example.com/assets/logo-[0-9a-f]+.png}, + assert_match %r{^//assets-999.example.com/assets/logo-[0-9a-f]+.png}, asset_path("logo.png") end @@ -114,7 +119,7 @@ class SprocketsHelperTest < ActionView::TestCase test "stylesheets served without a controller in scope cannot access the request" do @controller = nil - @config.action_controller.asset_host = Proc.new do |asset, request| + @config.asset_host = Proc.new do |asset, request| fail "This should not have been called." end assert_raises ActionController::RoutingError do @@ -134,11 +139,27 @@ class SprocketsHelperTest < ActionView::TestCase path_to_image("logo.png") end + test "javascript_path" do + assert_match %r{/assets/application-[0-9a-f]+.js}, + javascript_path("application.js") + + assert_match %r{/assets/application-[0-9a-f]+.js}, + path_to_javascript("application.js") + end + + test "stylesheet_path" do + assert_match %r{/assets/application-[0-9a-f]+.css}, + stylesheet_path("application.css") + + assert_match %r{/assets/application-[0-9a-f]+.css}, + path_to_stylesheet("application.css") + end + test "stylesheets served without a controller in do not use asset hosts when the default protocol is :request" do @controller = nil - @config.action_controller.asset_host = "assets-%d.example.com" - @config.action_controller.default_asset_host_protocol = :request - @config.action_controller.perform_caching = true + @config.asset_host = "assets-%d.example.com" + @config.default_asset_host_protocol = :request + @config.perform_caching = true assert_match %r{/assets/logo-[0-9a-f]+.png}, asset_path("logo.png") @@ -152,12 +173,12 @@ class SprocketsHelperTest < ActionView::TestCase 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" + @config.relative_url_root = "/collaboration/hieraki" assert_equal "/collaboration/hieraki/images/logo.gif", asset_path("/images/logo.gif") end - test "javascript path" do + test "javascript path through asset_path" do assert_match %r{/assets/application-[0-9a-f]+.js}, asset_path(:application, :ext => "js") @@ -202,7 +223,7 @@ class SprocketsHelperTest < ActionView::TestCase javascript_include_tag(:application) end - test "stylesheet path" do + test "stylesheet path through asset_path" do 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", :ext => "css") diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index dbac2e1fc0..bc45fabf34 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -88,6 +88,10 @@ class UrlHelperTest < ActiveSupport::TestCase ) end + def test_button_to_with_remote_and_form_options + assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\" data-remote=\"true\" data-type=\"json\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :remote => true, :form => { :class => "custom-class", "data-type" => "json" } ) + end + def test_button_to_with_remote_and_javascript_confirm assert_dom_equal( "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>", diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index a756b9f205..7bc3f997b5 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -78,7 +78,8 @@ module ActiveModel attribute_names -= Array.wrap(except).map(&:to_s) end - hash = attributes.slice(*attribute_names) + hash = {} + attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) } method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) } method_names.each { |n| hash[n] = send(n) } @@ -95,13 +96,33 @@ module ActiveModel end private + + # Hook method defining how an attribute value should be retrieved for + # serialization. By default this is assumed to be an instance named after + # the attribute. Override this method in subclasses should you need to + # retrieve the value for a given attribute differently: + # + # class MyClass + # include ActiveModel::Validations + # + # def initialize(data = {}) + # @data = data + # end + # + # def read_attribute_for_serialization(key) + # @data[key] + # end + # end + # + alias :read_attribute_for_serialization :send + # Add associations specified via the <tt>:include</tt> option. # # Expects a block that takes as arguments: # +association+ - name of the association # +records+ - the association record(s) to be serialized # +opts+ - options for the association records - def serializable_add_includes(options = {}) + def serializable_add_includes(options = {}) #:nodoc: return unless include = options[:include] unless include.is_a?(Hash) diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb index 29bcdeae67..1ec915d245 100644 --- a/activemodel/test/cases/serialization_test.rb +++ b/activemodel/test/cases/serialization_test.rb @@ -77,12 +77,12 @@ class SerializationTest < ActiveModel::TestCase assert_equal expected , @user.serializable_hash(:methods => [:bar]) end - def test_should_not_call_methods_for_attributes - def @user.name + def test_should_use_read_attribute_for_serialization + def @user.read_attribute_for_serialization(n) "Jon" end - expected = { "name" => "David" } + expected = { "name" => "Jon" } assert_equal expected, @user.serializable_hash(:only => :name) end diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb index 40fdcf20ca..a754d610b9 100644 --- a/activemodel/test/cases/serializers/json_serialization_test.rb +++ b/activemodel/test/cases/serializers/json_serialization_test.rb @@ -206,4 +206,14 @@ class JsonSerializationTest < ActiveModel::TestCase assert_no_match %r{"preferences":}, json end + test "custom as_json options should be extendible" do + def @contact.as_json(options = {}); super(options.merge(:only => [:name])); end + json = @contact.to_json + + assert_match %r{"name":"Konata Izumi"}, json + assert_no_match %r{"created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}}, json + assert_no_match %r{"awesome":}, json + assert_no_match %r{"preferences":}, json + end + end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index a54526dd41..f974b5d237 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,28 @@ -Wed Sep 7 15:25:02 2011 Aaron Patterson <aaron@tenderlovemaking.com> +*Rails 3.1.1 (unreleased)* + +* Add deprecation for the preload_associations method. Fixes #3022. + + [Jon Leighton] + +* Don't require a DB connection when loading a model that uses set_primary_key. GH #2807. + + [Jon Leighton] + +* Fix using select() with a habtm association, e.g. Person.friends.select(:name). GH #3030 and + #2923. + + [Hendy Tanata] + +* Fix belongs_to polymorphic with custom primary key on target. GH #3104. + + [Jon Leighton] + +* CollectionProxy#replace should change the DB records rather than just mutating the array. + Fixes #3020. + + [Jon Leighton] + +* 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. diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index a404a5edd7..36d7f4ad11 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -14,6 +14,8 @@ module ActiveRecord # primary_key_prefix_type setting, though. def primary_key @primary_key ||= reset_primary_key + raise ActiveRecord::UnknownPrimaryKey.new(self) unless @primary_key + @primary_key end # Returns a quoted version of the primary key name, used to construct SQL statements. @@ -29,6 +31,11 @@ module ActiveRecord key end + def primary_key? #:nodoc: + @primary_key ||= reset_primary_key + !@primary_key.nil? + end + def get_primary_key(base_name) #:nodoc: return 'id' unless base_name && !base_name.blank? diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 4174e4da09..8566ecad14 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -40,7 +40,7 @@ module ActiveRecord define_read_method(attr_name, attr_name, columns_hash[attr_name]) end - if attr_name == primary_key && attr_name != "id" + if primary_key? && attr_name == primary_key && attr_name != "id" define_read_method('id', attr_name, columns_hash[attr_name]) end end @@ -63,7 +63,7 @@ module ActiveRecord cast_code = column.type_cast_code('v') access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}" - unless attr_name.to_s == self.primary_key.to_s + unless primary_key? && attr_name.to_s == primary_key.to_s access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ") end @@ -107,7 +107,7 @@ module ActiveRecord def _read_attribute(attr_name) attr_name = attr_name.to_s - attr_name = self.class.primary_key if attr_name == 'id' + attr_name = self.class.primary_key? && self.class.primary_key if attr_name == 'id' value = @attributes[attr_name] unless value.nil? if column = column_for_attribute(attr_name) diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index e9cdb130db..4db6d71ba6 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -18,7 +18,7 @@ module ActiveRecord end end - if attr_name == primary_key && attr_name != "id" + if primary_key? && attr_name == primary_key && attr_name != "id" generated_attribute_methods.module_eval("alias :id= :'#{primary_key}='") end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 78159d13d4..137b4c6534 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -708,7 +708,7 @@ module ActiveRecord #:nodoc: # Returns an array of column objects for the table associated with this class. def columns if defined?(@primary_key) - connection_pool.primary_keys[table_name] ||= primary_key + connection_pool.primary_keys[table_name] ||= @primary_key end connection_pool.columns[table_name] @@ -953,7 +953,7 @@ module ActiveRecord #:nodoc: # objects of different types from the same table. def instantiate(record) sti_class = find_sti_class(record[inheritance_column]) - record_id = sti_class.primary_key && record[sti_class.primary_key] + record_id = sti_class.primary_key? && record[sti_class.primary_key] if ActiveRecord::IdentityMap.enabled? && record_id if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number? @@ -1941,8 +1941,9 @@ MSG # The primary key and inheritance column can never be set by mass-assignment for security reasons. def self.attributes_protected_by_default - default = [ primary_key, inheritance_column ] - default << 'id' unless primary_key.eql? 'id' + default = [ inheritance_column ] + default << primary_key if primary_key? + default << 'id' unless primary_key? && primary_key == 'id' default end diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index ad7d8cd63c..8262b60f6e 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -169,4 +169,18 @@ module ActiveRecord @errors = errors end end + + # Raised when a model attempts to fetch its primary key from the database, but the table + # has no primary key declared. + class UnknownPrimaryKey < ActiveRecordError + attr_reader :model + + def initialize(model) + @model = model + end + + def message + "Unknown primary key for table #{model.table_name} in model #{model}." + end + end end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 6f1ec7f9b3..97af15c9e8 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -622,7 +622,7 @@ module ActiveRecord private def primary_key_name - @primary_key_name ||= model_class && model_class.primary_key + @primary_key_name ||= model_class && model_class.primary_key? && model_class.primary_key end def has_primary_key_column? diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 5e65e46a7d..b5dadb929d 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -314,7 +314,7 @@ module ActiveRecord new_id = self.class.unscoped.insert attributes_values - self.id ||= new_id if self.class.primary_key + self.id ||= new_id if self.class.primary_key? IdentityMap.add(self) if IdentityMap.enabled? @new_record = false diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index b3316fd1a2..f4a813d704 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -341,7 +341,7 @@ db_namespace = namespace :db do namespace :schema do desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR' - task :dump => :load_config do + task :dump => [:environment, :load_config] do require 'active_record/schema_dumper' filename = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb" File.open(filename, "w:utf-8") do |file| diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 1929a808ed..120ff0cac6 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -212,8 +212,8 @@ module ActiveRecord end # klass option is necessary to support loading polymorphic associations - def association_primary_key(klass = self.klass) - options[:primary_key] || klass.primary_key + def association_primary_key(klass = nil) + options[:primary_key] || (klass || self.klass).primary_key end def active_record_primary_key diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index ecefaa633c..bf61d79a2c 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -13,7 +13,7 @@ module ActiveRecord # These are explicitly delegated to improve performance (avoids method_missing) delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a - delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :column_hash,:to => :klass + delegate :table_name, :quoted_table_name, :primary_key, :primary_key?, :quoted_primary_key, :connection, :column_hash,:to => :klass attr_reader :table, :klass, :loaded attr_accessor :extensions, :default_scoped @@ -36,7 +36,7 @@ module ActiveRecord def insert(values) primary_key_value = nil - if primary_key && Hash === values + if primary_key? && Hash === values primary_key_value = values[values.keys.find { |k| k.name == primary_key }] @@ -70,7 +70,7 @@ module ActiveRecord conn.insert( im, 'SQL', - primary_key, + primary_key? && primary_key, primary_key_value, nil, binds) diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index ae97a3f3ca..d4870dd3f2 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -303,7 +303,7 @@ module ActiveRecord # Save the new record state and id of a record so it can be restored later if a transaction fails. def remember_transaction_record_state #:nodoc @_start_transaction_state ||= {} - @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key) + @_start_transaction_state[:id] = id if self.class.primary_key? unless @_start_transaction_state.include?(:new_record) @_start_transaction_state[:new_record] = @new_record end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index d1d02c25d5..34d90cc395 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -651,7 +651,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase end def test_habtm_selects_all_columns_by_default - assert_equal Project.column_names, developers(:david).projects.first.attributes.keys + assert_equal Project.column_names.sort, developers(:david).projects.first.attributes.keys.sort end def test_habtm_respects_select_query_method diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index fdfbcbefac..cddd2a6f8c 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -486,7 +486,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_default_select - assert_equal Comment.column_names, posts(:welcome).comments.first.attributes.keys + assert_equal Comment.column_names.sort, posts(:welcome).comments.first.attributes.keys.sort end def test_select_query_method diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb index e03ed33591..814476ce73 100644 --- a/activerecord/test/cases/attribute_methods/read_test.rb +++ b/activerecord/test/cases/attribute_methods/read_test.rb @@ -24,6 +24,10 @@ module ActiveRecord def self.primary_key end + def self.primary_key? + false + end + def self.columns column_names.map { FakeColumn.new(name) } end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 12c1cfb30e..77fd1d2fad 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -97,10 +97,6 @@ class BasicsTest < ActiveRecord::TestCase assert pk.primary, 'nick should be primary key' end - def test_primary_key_with_no_id - assert_nil Edge.primary_key - end - unless current_adapter?(:PostgreSQLAdapter,:OracleAdapter,:SQLServerAdapter) def test_limit_with_comma assert_nothing_raised do diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 489c7d8310..58badd6266 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -5,6 +5,7 @@ require 'models/subscriber' require 'models/movie' require 'models/keyboard' require 'models/mixed_case_monkey' +require 'models/edge' class PrimaryKeysTest < ActiveRecord::TestCase fixtures :topics, :subscribers, :movies, :mixed_case_monkeys @@ -161,4 +162,17 @@ class PrimaryKeysTest < ActiveRecord::TestCase assert_equal 'foo', model.primary_key end + + def test_no_primary_key_raises + assert_raises(ActiveRecord::UnknownPrimaryKey) do + Edge.primary_key + end + + begin + Edge.primary_key + rescue ActiveRecord::UnknownPrimaryKey => e + assert e.message.include?('edges') + assert e.message.include?('Edge') + end + end end diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 0a48f418b1..ca9d88fbd5 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -244,6 +244,7 @@ class ReflectionTest < ActiveRecord::TestCase # Normal association assert_equal "id", Author.reflect_on_association(:posts).association_primary_key.to_s assert_equal "name", Author.reflect_on_association(:essay).association_primary_key.to_s + assert_equal "name", Essay.reflect_on_association(:writer).association_primary_key.to_s # Through association (uses the :primary_key option from the source reflection) assert_equal "nick", Author.reflect_on_association(:subscribers).association_primary_key.to_s diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml index 8c1a45430e..f450efd839 100644 --- a/activerecord/test/config.example.yml +++ b/activerecord/test/config.example.yml @@ -37,11 +37,13 @@ connections: db2: arunit: + adapter: ibm_db host: localhost username: arunit password: arunit database: arunit arunit2: + adapter: ibm_db host: localhost username: arunit password: arunit diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 1ffd83b91d..03c4cc5b9e 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -1384,6 +1384,10 @@ module ActiveResource private + def read_attribute_for_serialization(n) + attributes[n] + end + # 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)) diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index d923204dde..592fca96a4 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -238,8 +238,11 @@ module ActiveResource def digest_auth_header(http_method, uri) params = extract_params_from_response + request_uri = uri.path + request_uri << "?#{uri.query}" if uri.query + ha1 = Digest::MD5.hexdigest("#{@user}:#{params['realm']}:#{@password}") - ha2 = Digest::MD5.hexdigest("#{http_method.to_s.upcase}:#{uri.path}") + ha2 = Digest::MD5.hexdigest("#{http_method.to_s.upcase}:#{request_uri}") params.merge!('cnonce' => client_nonce) request_digest = Digest::MD5.hexdigest([ha1, params['nonce'], "0", params['cnonce'], params['qop'], ha2].join(":")) diff --git a/activeresource/test/cases/authorization_test.rb b/activeresource/test/cases/authorization_test.rb index 69ef9a2821..17cd9b30fc 100644 --- a/activeresource/test/cases/authorization_test.rb +++ b/activeresource/test/cases/authorization_test.rb @@ -131,6 +131,12 @@ class AuthorizationTest < Test::Unit::TestCase assert_equal blank_digest_auth_header("/people/2.json", "fad396f6a34aeba28e28b9b96ddbb671"), authorization_header['Authorization'] end + def test_authorization_header_with_query_string_if_auth_type_is_digest + @authenticated_conn.auth_type = :digest + authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.json?only=name')) + assert_equal blank_digest_auth_header("/people/2.json?only=name", "f8457b0b5d21b6b80737a386217afb24"), authorization_header['Authorization'] + end + def test_get david = decode(@authenticated_conn.get("/people/2.json")) assert_equal "David", david["name"] diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 3f516d4808..b431041b76 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -161,6 +161,7 @@ module ActiveSupport end def search_dir(dir, &callback) + return if !File.exist?(dir) Dir.foreach(dir) do |d| next if d == "." || d == ".." name = File.join(dir, d) diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb index 544e63132d..43134b4314 100644 --- a/activesupport/lib/active_support/core_ext/range/conversions.rb +++ b/activesupport/lib/active_support/core_ext/range/conversions.rb @@ -7,7 +7,7 @@ class Range # # ==== Example # - # [1..100].to_formatted_s # => "1..100" + # (1..100).to_formatted_s # => "1..100" def to_formatted_s(format = :default) if formatter = RANGE_FORMATS[format] formatter.call(first, last) 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 2daf4016cd..5d7f74bb65 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -75,7 +75,7 @@ end module ActiveSupport #:nodoc: class SafeBuffer < String - UNSAFE_STRING_METHODS = ["capitalize", "chomp", "chop", "delete", "downcase", "gsub", "lstrip", "next", "reverse", "rstrip", "slice", "squeeze", "strip", "sub", "succ", "swapcase", "tr", "tr_s", "upcase"].freeze + UNSAFE_STRING_METHODS = ["capitalize", "chomp", "chop", "delete", "downcase", "gsub", "lstrip", "next", "reverse", "rstrip", "slice", "squeeze", "strip", "sub", "succ", "swapcase", "tr", "tr_s", "upcase", "prepend"].freeze alias_method :original_concat, :concat private :original_concat @@ -142,16 +142,18 @@ module ActiveSupport #:nodoc: end UNSAFE_STRING_METHODS.each do |unsafe_method| - class_eval <<-EOT, __FILE__, __LINE__ + 1 - def #{unsafe_method}(*args, &block) # def capitalize(*args, &block) - to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block) - end # end - - def #{unsafe_method}!(*args) # def capitalize!(*args) - @dirty = true # @dirty = true - super # super - end # end - EOT + if 'String'.respond_to?(unsafe_method) + class_eval <<-EOT, __FILE__, __LINE__ + 1 + def #{unsafe_method}(*args, &block) # def capitalize(*args, &block) + to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block) + end # end + + def #{unsafe_method}!(*args) # def capitalize!(*args) + @dirty = true # @dirty = true + super # super + end # end + EOT + end end protected diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index a15a06d0e4..372dd69212 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -9,7 +9,7 @@ class Time class << self # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances def ===(other) - other.is_a?(::Time) + super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone)) end # Return the number of days in the given month. diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 67698c1cff..469ae69258 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -38,7 +38,7 @@ module ActiveSupport attr_reader :options def initialize(options = nil) - @options = options + @options = options || {} @seen = Set.new end @@ -59,7 +59,7 @@ module ActiveSupport def options_for(value) if value.is_a?(Array) || value.is_a?(Hash) # hashes and arrays need to get encoder in the options, so that they can detect circular references - (options || {}).merge(:encoder => self) + options.merge(:encoder => self) else options end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index b692a41312..cb5362525f 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -566,6 +566,14 @@ class FileStoreTest < ActiveSupport::TestCase assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE} assert_equal 'B', File.basename(path) end + + # If nothing has been stored in the cache, there is a chance the cache directory does not yet exist + # Ensure delete_matched gracefully handles this case + def test_delete_matched_when_cache_directory_does_not_exist + assert_nothing_raised(Exception) do + ActiveSupport::Cache::FileStore.new('/test/cache/directory').delete_matched(/does_not_exist/) + end + end end class MemoryStoreTest < ActiveSupport::TestCase diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index c4c4381957..ab9be4b18b 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -764,7 +764,10 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase def test_case_equality assert Time === Time.utc(2000) assert Time === ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) + assert Time === Class.new(Time).utc(2000) assert_equal false, Time === DateTime.civil(2000) + assert_equal false, Class.new(Time) === Time.utc(2000) + assert_equal false, Class.new(Time) === ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) end def test_all_day diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb index bf4f5265ef..c28ffa50f2 100644 --- a/activesupport/test/rescuable_test.rb +++ b/activesupport/test/rescuable_test.rb @@ -70,7 +70,7 @@ class CoolStargate < Stargate end -class RescueableTest < Test::Unit::TestCase +class RescuableTest < Test::Unit::TestCase def setup @stargate = Stargate.new @cool_stargate = CoolStargate.new diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 54eef0473c..187dd2428f 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.2.0 (unreleased)* +* Default options to `rails new` can be set in ~/.railsrc [Guillermo Iguaran] + * Added destroy alias to Rails engines. [Guillermo Iguaran] * Added destroy alias for Rails command line. This allows the following: `rails d model post`. [Andrey Ognevsky] @@ -13,7 +15,10 @@ *Rails 3.1.1 -* `rake assets:precompile` loads the application but does not initialize it. +* Add jquery-rails to Gemfile of plugins, test/dummy app needs it. Closes #3091. [Santiago Pastorino] + +* Add config.assets.initialize_on_precompile which, when set to false, forces + `rake assets:precompile` to load the application but does not initialize it. To the app developer, this means configuration add in config/initializers/* will not be executed. diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 9346a408e0..781b9001b6 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -1192,13 +1192,11 @@ h4. Creating Observers For example, imagine a +User+ model where we want to send an email every time a new user is created. Because sending emails is not directly related to our model's purpose, we should create an observer to contain the code implementing this functionality. -Rails can create the initial code of the observers in a simple way. For instance, given a model +User+, the command - <shell> $ rails generate observer User </shell> -generates file +app/models/user_observer.rb+ containing the observer class +UserObserver+: +generates +app/models/user_observer.rb+ containing the observer class +UserObserver+: <ruby> class UserObserver < ActiveRecord::Observer diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile index b343e8d01d..7795b297f3 100644 --- a/railties/guides/source/asset_pipeline.textile +++ b/railties/guides/source/asset_pipeline.textile @@ -152,15 +152,15 @@ Images can also be organized into subdirectories if required, and they can be ac h5. CSS and ERB -If you add an +erb+ extension to a CSS asset, making it something such as +application.css.erb+, then helpers like +image_path+ are available in your CSS rules: +If you add an +erb+ extension to a CSS asset, making it something such as +application.css.erb+, then helpers like +asset_path+ are available in your CSS rules: <plain> -.class { background-image: url(<%= image_path 'image.png' %>) } +.class { background-image: url(<%= asset_path 'image.png' %>) } </plain> This writes the path to the particular asset being referenced. In this example, it would make sense to have an image in one of the asset load paths, such as +app/assets/images/image.png+, which would be referenced here. If this image is already available in +public/assets+ as a fingerprinted file, then that path is referenced. -If you want to use a "css data URI":http://en.wikipedia.org/wiki/Data_URI_scheme -- a method of embedding the image data directly into the CSS file -- you can use the +asset_data_uri+ helper. +If you want to use a "data URI":http://en.wikipedia.org/wiki/Data_URI_scheme -- a method of embedding the image data directly into the CSS file -- you can use the +asset_data_uri+ helper. <plain> #logo { background: url(<%= asset_data_uri 'logo.png' %>) } @@ -186,27 +186,28 @@ 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: -<plain> +<erb> $('#logo').attr({ src: "<%= asset_path('logo.png') %>" }); -</plain> +</erb> 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): +Similarly, you can use the +asset_path+ helper in CoffeeScript files with +erb+ extension (eg. +application.js.coffee.erb+): <plain> -$('#logo').attr src: "<% asset_path('logo.png') %>" +$('#logo').attr src: "<%= asset_path('logo.png') %>" </plain> h4. Manifest Files and Directives -Sprockets uses manifest files to determine which assets to include and serve. These manifest files contain _directives_ -- instructions that tell Sprockets which files to require in order to build a single CSS or JavaScript file. With these directives, Sprockets loads the files specified, processes them if necessary, concatenates them into one single file and then compresses them (if +Rails.application.config.assets.compress+ is set to +true+). By serving one file rather than many, the load time of pages are greatly reduced as there are fewer requests to make. +Sprockets uses manifest files to determine which assets to include and serve. These manifest files contain _directives_ -- instructions that tell Sprockets which files to require in order to build a single CSS or JavaScript file. With these directives, Sprockets loads the files specified, processes them if necessary, concatenates them into one single file and then compresses them (if +Rails.application.config.assets.compress+ is true). By serving one file rather than many, the load time of pages are greatly reduced as there are fewer requests to make. For example, in the default Rails application there's a +app/assets/javascripts/application.js+ file which contains the following lines: <plain> +// ... //= require jquery //= require jquery_ujs //= require_tree . @@ -214,9 +215,11 @@ For example, in the default Rails application there's a +app/assets/javascripts/ In JavaScript files, the directives begin with +//=+. In this case, the file is using the +require+ and the +require_tree+ directives. The +require+ directive is used to tell Sprockets the files that you wish to require. Here, you are requiring the files +jquery.js+ and +jquery_ujs.js+ that are available somewhere in the search path for Sprockets. You need not supply the extensions explicitly. Sprockets assumes you are requiring a +.js+ file when done from within a +.js+ file. -NOTE. In Rails 3.1, the +jquery.js+ and +jquery_ujs.js+ files are located inside the +vendor/assets/javascripts+ directory contained within the +jquery-rails+ gem. +NOTE. In Rails 3.1 the +jquery-rails+ gem provides the +jquery.js+ and +jquery_ujs.js+ files via the asset pipeline. You won't see them in the application tree. -The +require_tree .+ directive tells Sprockets to include _all_ JavaScript files in this directory into the output. Only a path relative to the file can be specified. There is also a +require_directory+ directive which includes all JavaScript files only in the directory specified (no nesting). +The +require_tree+ directive tells Sprockets to recursively include _all_ JavaScript files in this directory into the output. Only a path relative to the manifest file can be specified. There is also a +require_directory+ directive which includes all JavaScript files only in the directory specified (no nesting). + +Directives are processed top to bottom, but the order in which files are included by +require_tree+ is unspecified. You should not rely on any particular order among those. If you need to ensure some particular JavaScript ends up above some other, require it before in the manifest. Note that the family of +require+ directives prevents files from being included twice in the output. There's also a default +app/assets/stylesheets/application.css+ file which contains these lines: @@ -233,7 +236,7 @@ In this example +require_self+ is used. This puts the CSS contained within the f You can have as many manifest files as you need. For example the +admin.css+ and +admin.js+ manifest could contain the JS and CSS files that are used for the admin section of an application. -For some assets (like CSS) the compiled order is important. You can specify individual files and they are compiled in the order specified: +The same remarks about ordering made above apply. In particular, you can specify individual files and they are compiled in the order specified: <plain> /* ... @@ -246,19 +249,19 @@ For some assets (like CSS) the compiled order is important. You can specify indi h4. Preprocessing -The file extensions used on an asset determine what preprocessing is applied. When a controller or a scaffold is generated with the default Rails gemset, a CoffeeScript file and a SCSS file are generated in place of a regular JavaScript and CSS file. The example used before was a controller called "projects", which generated an +app/assets/javascripts/projects.js.coffee+ and a +app/assets/stylesheets/projects.css.scss+ file. +The file extensions used on an asset determine what preprocessing is applied. When a controller or a scaffold is generated with the default Rails gemset, a CoffeeScript file and a SCSS file are generated in place of a regular JavaScript and CSS file. The example used before was a controller called "projects", which generated an +app/assets/javascripts/projects.js.coffee+ and an +app/assets/stylesheets/projects.css.scss+ file. When these files are requested, they are processed by the processors provided by the +coffee-script+ and +sass-rails+ gems and then sent back to the browser as JavaScript and CSS respectively. -Additional layers of pre-processing can be requested by adding other extensions, where each extension is processed in a right-to-left manner. These should be used in the order the processing should be applied. For example, a stylesheet called +app/assets/stylesheets/projects.css.scss.erb+ is first processed as ERB, then SCSS and finally served as CSS. The same applies to a JavaScript file -- +app/assets/javascripts/projects.js.coffee.erb+ is processed as ERB, CoffeeScript and served as JavaScript. +Additional layers of preprocessing can be requested by adding other extensions, where each extension is processed in a right-to-left manner. These should be used in the order the processing should be applied. For example, a stylesheet called +app/assets/stylesheets/projects.css.scss.erb+ is first processed as ERB, then SCSS and finally served as CSS. The same applies to a JavaScript file -- +app/assets/javascripts/projects.js.coffee.erb+ is processed as ERB, CoffeeScript, and served as JavaScript. -Keep in mind that the order of these pre-processors is important. For example, if you called your JavaScript file +app/assets/javascripts/projects.js.erb.coffee+ then it is processed with the CoffeeScript interpreter first, which wouldn't understand ERB and therefore you would run into problems. +Keep in mind that the order of these preprocessors is important. For example, if you called your JavaScript file +app/assets/javascripts/projects.js.erb.coffee+ then it would be processed with the CoffeeScript interpreter first, which wouldn't understand ERB and therefore you would run into problems. h3. In Development In development mode assets are served as separate files in the order they are specified in the manifest file. -This manifest +application.js+: +This manifest +app/assets/javascripts/application.js+: <plain> //= require core @@ -269,45 +272,42 @@ This manifest +application.js+: would generate this HTML: <html> -<script src='/assets/core.js?body=1'></script> -<script src='/assets/projects.js?body=1'></script> -<script src='/assets/tickets.js?body=1'></script> +<script src="/assets/core.js?body=1" type="text/javascript"></script> +<script src="/assets/projects.js?body=1" type="text/javascript"></script> +<script src="/assets/tickets.js?body=1" type="text/javascript"></script> </html> The +body+ param is required by Sprockets. h4. Turning Debugging off -You can turn off debug mode by updating +development.rb+ to include: +You can turn off debug mode by updating +config/environments/development.rb+ to include: -<erb> +<ruby> config.assets.debug = false -</erb> +</ruby> -When debug mode is off Sprockets will concatenate and run the necessary preprocessors on all files, generating the following HTML: +When debug mode is off Sprockets concatenates and runs the necessary preprocessors on all files. With debug mode turned off the manifest above would generate instead: <html> -<script src='/assets/application.js'></script> +<script src="/assets/application.js" type="text/javascript"></script> </html> -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. +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. -You can put +?debug_assets=true+ or +?debug_assets=1+ at the end of a URL to enable debug mode on-demand, and this will render individual tags for each file. This is useful for tracking down exact line numbers when debugging. - -Debug can also be set in the Rails helper methods: +Debug mode can also be enabled in the Rails helper methods: <erb> <%= stylesheet_link_tag "application", :debug => true %> <%= javascript_include_tag "application", :debug => true %> </erb> -The +:debug+ option is ignored if the debug mode is off. +The +:debug+ option is redundant if debug mode is on. You could potentially also enable compression in development mode as a sanity check, and disable it on-demand as required for debugging. - h3. In Production In the production environment Rails uses the fingerprinting scheme outlined above. By default it is assumed that assets have been precompiled and will be served as static assets by your web server. @@ -346,6 +346,15 @@ The rake task is: bundle exec rake assets:precompile </plain> +For faster asset precompiles, you can partially load your application by setting ++config.assets.initialize_on_precompile+ to false, though in that case templates +cannot see application objects or methods. *Heroku requires this to be false.* + +WARNING: If you set +config.assets.initialize_on_precompile+ to false, be sure to +test +rake assets:precompile+ locally before deploying. It may expose bugs where +your assets reference application objects or methods, since those are still +in scope in development mode regardless of the value of this flag. + Capistrano (v2.8.0 and above) has a recipe to handle this in deployment. Add the following line to +Capfile+: <erb> diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 6812b6b9fe..bf6104b96b 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -705,7 +705,7 @@ $ rails console TIP: The default console will make changes to your database. You can instead open a console that will roll back any changes you make by using <tt>rails console ---sandbox</tt> . +--sandbox</tt>. After the console loads, you can use it to work with your application's models: @@ -1074,7 +1074,7 @@ In the +update+ action, Rails first uses the +:id+ parameter passed back from the edit view to locate the database record that's being edited. The +update_attributes+ call then takes the +post+ parameter (a hash) from the request and applies it to this record. If all goes well, the user is redirected to the -post's +show+ action. If there are any problems, it's back to the +edit+ action to +post's +show+ action. If there are any problems, it redirects back to the +edit+ action to correct them. h4. Destroying a Post @@ -1115,8 +1115,7 @@ models and controllers. To create the new model, run this command in your terminal: <shell> -$ rails generate model Comment commenter:string body:text \ -> post:references +$ rails generate model Comment commenter:string body:text post:references </shell> This command will generate four files: @@ -1520,7 +1519,7 @@ defined it as an instance variable. h3. Deleting Comments -Another important feature of a blog is being able to delete SPAM comments. To do +Another important feature of a blog is being able to delete spam comments. To do this, we need to implement a link of some sort in the view and a +DELETE+ action in the +CommentsController+. diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index a73655d130..9c92d567d3 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -658,7 +658,7 @@ In many ways this is exactly what it is. This file is created by inspecting the There is however a trade-off: +db/schema.rb+ cannot express database specific items such as foreign key constraints, triggers, or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this, then you should set the schema format to +:sql+. -Instead of using Active Record's schema dumper, the database's structure will be dumped using a tool specific the RDBMS of the database (via the +db:structure:dump+ Rake task) into +db/#{Rails.env}_structure.sql+. For example, for the PostgreSQL RDBMS, the +pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading these schemas is simply a question of executing the SQL statements they contain. By definition, this will create a perfect copy of the database's structure. Using the +:sql+ schema format will, however, prevent loading the schema into a RDBMS other than the one used to create it. +Instead of using Active Record's schema dumper, the database's structure will be dumped using a tool specific to the database (via the +db:structure:dump+ Rake task) into +db/#{Rails.env}_structure.sql+. For example, for the PostgreSQL RDBMS, the +pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading these schemas is simply a question of executing the SQL statements they contain. By definition, this will create a perfect copy of the database's structure. Using the +:sql+ schema format will, however, prevent loading the schema into a RDBMS other than the one used to create it. h4. Schema Dumps and Source Control diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 0a9f1e8388..bf18402b60 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -596,6 +596,8 @@ match "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" } </ruby> +Please note that this redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browser or proxy server will cache this type of redirect, make the old page inaccessible. + In all of these cases, if you don't provide the leading host (+http://www.example.com+), Rails will take those details from the current request. h4. Routing to Rack Applications diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 2e412147d3..cbb2d23238 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -91,7 +91,7 @@ module Rails @routes_reloader ||= RoutesReloader.new end - def initialize!(group=nil) + def initialize!(group=:default) raise "Application has been already initialized." if @initialized run_initializers(group, self) @initialized = true diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index c363e53c10..448521d2f0 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -37,19 +37,20 @@ module Rails @cache_store = [ :file_store, "#{root}/tmp/cache/" ] @assets = ActiveSupport::OrderedOptions.new - @assets.enabled = false - @assets.paths = [] - @assets.precompile = [ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) }, - /(?:\/|\\|\A)application\.(css|js)$/ ] - @assets.prefix = "/assets" - @assets.version = '' - @assets.debug = false - @assets.compile = true - @assets.digest = false - @assets.manifest = nil - @assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/" ] - @assets.js_compressor = nil - @assets.css_compressor = nil + @assets.enabled = false + @assets.paths = [] + @assets.precompile = [ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) }, + /(?:\/|\\|\A)application\.(css|js)$/ ] + @assets.prefix = "/assets" + @assets.version = '' + @assets.debug = false + @assets.compile = true + @assets.digest = false + @assets.manifest = nil + @assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/" ] + @assets.js_compressor = nil + @assets.css_compressor = nil + @assets.initialize_on_precompile = true end def compiled_asset_path diff --git a/railties/lib/rails/commands/application.rb b/railties/lib/rails/commands/application.rb index 1cf23a8b92..60d1aed73a 100644 --- a/railties/lib/rails/commands/application.rb +++ b/railties/lib/rails/commands/application.rb @@ -9,6 +9,14 @@ if ARGV.first != "new" ARGV[0] = "--help" else ARGV.shift + railsrc = File.join(File.expand_path("~"), ".railsrc") + if File.exist?(railsrc) + extra_args_string = File.open(railsrc).read + extra_args = extra_args_string.split(/\n+/).map {|l| l.split}.flatten + puts "Using #{extra_args.join(" ")} from #{railsrc}" + ARGV << extra_args + ARGV.flatten! + end end require 'rubygems' if ARGV.include?("--dev") diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 0e1e719596..2d25273050 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -542,7 +542,7 @@ module Rails require environment if environment end - initializer :append_assets_path, :group => :assets do |app| + initializer :append_assets_path, :group => :all do |app| app.config.assets.paths.unshift(*paths["vendor/assets"].existent_directories) app.config.assets.paths.unshift(*paths["lib/assets"].existent_directories) app.config.assets.paths.unshift(*paths["app/assets"].existent_directories) diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 575f4bb106..b26839644e 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -90,7 +90,7 @@ module Rails append_file "Gemfile", "\ngroup #{name} do\n", :force => true @in_group = true - instance_eval &block + instance_eval(&block) @in_group = false append_file "Gemfile", "end\n", :force => true @@ -252,7 +252,7 @@ module Rails # def rake(command, options={}) log :rake, command - env = options[:env] || 'development' + env = options[:env] || ENV["RAILS_ENV"] || 'development' sudo = options[:sudo] && RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ ? 'sudo ' : '' in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", :verbose => false) } end diff --git a/railties/lib/rails/generators/rails/app/USAGE b/railties/lib/rails/generators/rails/app/USAGE index 9e7a78d132..691095f33f 100644 --- a/railties/lib/rails/generators/rails/app/USAGE +++ b/railties/lib/rails/generators/rails/app/USAGE @@ -2,6 +2,12 @@ Description: The 'rails new' command creates a new Rails application with a default directory structure and configuration at the path you specify. + You can specify extra command-line arguments to be used every time + 'rails new' runs in the .railsrc configuration file in your home directory. + + Note that the arguments specified in the .railsrc file don't affect the + defaults values shown above in this help message. + Example: rails new ~/Code/Ruby/weblog diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 4c1da0a5a5..04d5b55c69 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -10,6 +10,7 @@ module Rails attr_reader :name, :block def initialize(name, context, options, &block) + options[:group] ||= :default @name, @context, @options, @block = name, context, options, block end @@ -48,10 +49,10 @@ module Rails end end - def run_initializers(group=nil, *args) + def run_initializers(group=:default, *args) return if instance_variable_defined?(:@ran) initializers.tsort.each do |initializer| - initializer.run(*args) if group.nil? || initializer.belongs_to?(group) + initializer.run(*args) if initializer.belongs_to?(group) end @ran = true end diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index 68f566274d..8d0d8cacac 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -24,7 +24,7 @@ if defined?(MiniTest) end end -if defined?(ActiveRecord) +if defined?(ActiveRecord::Base) require 'active_record/test_case' class ActiveSupport::TestCase diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index 118ffff44b..63427c7792 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -22,13 +22,13 @@ module ApplicationTests end def precompile! - capture(:stdout) do + quietly do Dir.chdir(app_path){ `bundle exec rake assets:precompile` } end end test "assets routes have higher priority" do - app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();" + app_file "app/assets/javascripts/demo.js.erb", "a = <%= image_path('rails.png').inspect %>;" app_file 'config/routes.rb', <<-RUBY AppTemplate::Application.routes.draw do @@ -39,7 +39,7 @@ module ApplicationTests require "#{app_path}/config/environment" get "/assets/demo.js" - assert_match "alert()", last_response.body + assert_equal 'a = "/assets/rails.png";', last_response.body.strip end test "assets do not require compressors until it is used" do @@ -244,14 +244,16 @@ module ApplicationTests assert_match(/app.js isn't precompiled/, last_response.body) end - test "precompile appends the md5 hash to files referenced with asset_path and run in the provided RAILS_ENV" do + test "precompile properly refers files referenced with asset_path and 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 - add_to_config "config.assets.digest = true" + add_to_env_config "test", "config.assets.digest = true" - # capture(:stdout) do + quietly do Dir.chdir(app_path){ `bundle exec rake assets:precompile RAILS_ENV=test` } - # end + end + file = Dir["#{app_path}/public/assets/application.css"].first + assert_match(/\/assets\/rails\.png/, File.read(file)) file = Dir["#{app_path}/public/assets/application-*.css"].first assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file)) end @@ -279,7 +281,7 @@ module ApplicationTests add_to_config "config.assets.compile = true" ENV["RAILS_ENV"] = nil - capture(:stdout) do + quietly do Dir.chdir(app_path){ `bundle exec rake assets:precompile RAILS_GROUPS=assets` } end file = Dir["#{app_path}/public/assets/application-*.css"].first @@ -304,7 +306,7 @@ module ApplicationTests app_file "public/assets/application.css", "a { color: green; }" app_file "public/assets/subdir/broken.png", "not really an image file" - capture(:stdout) do + quietly do Dir.chdir(app_path){ `bundle exec rake assets:clean` } end @@ -395,7 +397,62 @@ module ApplicationTests assert_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js\?body=1" type="text\/javascript"><\/script>/, last_response.body) end + test "assets can access model information when precompiling" do + app_file "app/models/post.rb", "class Post; end" + app_file "app/assets/javascripts/application.js", "//= require_tree ." + app_file "app/assets/javascripts/xmlhr.js.erb", "<%= Post.name %>" + + add_to_config "config.assets.digest = false" + precompile! + assert_equal "Post;\n", File.read("#{app_path}/public/assets/application.js") + end + + test "assets can't access model information when precompiling if not initializing the app" do + app_file "app/models/post.rb", "class Post; end" + app_file "app/assets/javascripts/application.js", "//= require_tree ." + app_file "app/assets/javascripts/xmlhr.js.erb", "<%= defined?(Post) || :NoPost %>" + + add_to_config "config.assets.digest = false" + add_to_config "config.assets.initialize_on_precompile = false" + + precompile! + assert_equal "NoPost;\n", File.read("#{app_path}/public/assets/application.js") + end + + test "enhancements to assets:precompile should only run once" do + app_file "lib/tasks/enhance.rake", "Rake::Task['assets:precompile'].enhance { puts 'enhancement' }" + output = precompile! + assert_equal 1, output.scan("enhancement").size + end + + test "digested assets are not mistakenly removed" do + app_file "app/assets/application.js", "alert();" + add_to_config "config.assets.compile = true" + add_to_config "config.assets.digest = true" + + quietly do + Dir.chdir(app_path){ `bundle exec rake assets:clean assets:precompile` } + end + + files = Dir["#{app_path}/public/assets/application-*.js"] + assert_equal 1, files.length, "Expected digested application.js asset to be generated, but none found" + end + + test "digested assets are removed from configured path" do + app_file "public/production_assets/application.js", "alert();" + add_to_env_config "production", "config.assets.prefix = 'production_assets'" + + ENV["RAILS_ENV"] = nil + quietly do + Dir.chdir(app_path){ `bundle exec rake assets:clean` } + end + + files = Dir["#{app_path}/public/production_assets/application.js"] + assert_equal 0, files.length, "Expected application.js asset to be removed, but still exists" + end + private + def app_with_assets_in_view app_file "app/assets/javascripts/application.js", "//= require_tree ." app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }" diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 448982f9de..97ad47ac14 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -306,7 +306,7 @@ module ApplicationTests require "#{app_path}/config/environment" require "mail" - ActionMailer::Base + _ = ActionMailer::Base assert_equal [::MyMailInterceptor], ::Mail.send(:class_variable_get, "@@delivery_interceptors") end @@ -319,7 +319,7 @@ module ApplicationTests require "#{app_path}/config/environment" require "mail" - ActionMailer::Base + _ = ActionMailer::Base assert_equal [::MyMailInterceptor, ::MyOtherMailInterceptor], ::Mail.send(:class_variable_get, "@@delivery_interceptors") end @@ -332,7 +332,7 @@ module ApplicationTests require "#{app_path}/config/environment" require "mail" - ActionMailer::Base + _ = ActionMailer::Base assert_equal [::MyMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers") end @@ -345,7 +345,7 @@ module ApplicationTests require "#{app_path}/config/environment" require "mail" - ActionMailer::Base + _ = ActionMailer::Base assert_equal [::MyMailObserver, ::MyOtherMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers") end diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb index e656ada3c0..050a2161ae 100644 --- a/railties/test/application/middleware/cache_test.rb +++ b/railties/test/application/middleware/cache_test.rb @@ -31,6 +31,10 @@ module ApplicationTests $last_modified ||= Time.now.utc render_conditionally(:last_modified => $last_modified) end + + def keeps_if_modified_since + render :text => request.headers['If-Modified-Since'] + end private def render_conditionally(headers) if stale?(headers.merge(:public => !params[:private])) @@ -47,6 +51,16 @@ module ApplicationTests RUBY end + def test_cache_keeps_if_modified_since + simple_controller + expected = "Wed, 30 May 1984 19:43:31 GMT" + + get "/expires/keeps_if_modified_since", {}, "HTTP_IF_MODIFIED_SINCE" => expected + + assert_equal 200, last_response.status + assert_equal expected, last_response.body, "cache should have kept If-Modified-Since" + end + def test_cache_is_disabled_in_dev_mode simple_controller app("development") diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 94e9abb3cc..e621f7f6f7 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -189,6 +189,22 @@ class ActionsTest < Rails::Generators::TestCase action :rake, 'log:clear', :env => 'production' end + def test_rake_with_rails_env_variable_should_run_rake_command_in_env + generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', :verbose => false) + old_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production" + action :rake, 'log:clear' + ensure + ENV["RAILS_ENV"] = old_env + end + + def test_env_option_should_win_over_rails_env_variable_when_running_rake + generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', :verbose => false) + old_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "staging" + action :rake, 'log:clear', :env => 'production' + ensure + ENV["RAILS_ENV"] = old_env + end + def test_rake_with_sudo_option_should_run_rake_command_with_sudo generator.expects(:run).once.with('sudo rake log:clear RAILS_ENV=development', :verbose => false) action :rake, 'log:clear', :sudo => true diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb index 1dbcc249ab..c84c7f204c 100644 --- a/railties/test/initializable_test.rb +++ b/railties/test/initializable_test.rb @@ -209,7 +209,7 @@ module InitializableTests $arr = [] instance = Instance.new instance.run_initializers - assert_equal [1, 2, 3, 4], $arr + assert_equal [2, 3, 4], $arr end test "running locals with groups" do @@ -223,7 +223,7 @@ module InitializableTests class WithArgsTest < ActiveSupport::TestCase test "running initializers with args" do $with_arg = nil - WithArgs.new.run_initializers(nil, 'foo') + WithArgs.new.run_initializers(:default, 'foo') assert_equal 'foo', $with_arg end end |