diff options
author | Ryan Bigg <radarlistener@gmail.com> | 2011-04-05 21:22:38 +1000 |
---|---|---|
committer | Ryan Bigg <radarlistener@gmail.com> | 2011-04-05 21:22:38 +1000 |
commit | 92e6255b58ce445d23580b669dac67d80e64d411 (patch) | |
tree | de3a01091787b30f1bafd462eedcb6210342493a | |
parent | 357578256fb55e32ae87c6fbf22a1c19f59ce264 (diff) | |
parent | ac07da8fc72b7a57fd4a60c0dcb5b777d85f9eb7 (diff) | |
download | rails-92e6255b58ce445d23580b669dac67d80e64d411.tar.gz rails-92e6255b58ce445d23580b669dac67d80e64d411.tar.bz2 rails-92e6255b58ce445d23580b669dac67d80e64d411.zip |
Merge branch 'master' of github.com:lifo/docrails
* 'master' of github.com:lifo/docrails: (57 commits)
Made the defaults section a little more readable and more to the point, giving a overview of the possibilities.
Added information about default values
added .'s to headings in the initialization textile page
s/ERb/ERB/g (part II)
s/ERb/ERB/g
Bump up erubis to 2.7.0
Implicit actions named not_implemented can be rendered
Gem::Specification#has_rdoc= is deprecated since rubygems 1.7.0
default_executable is deprecated since rubygems 1.7.0
Trivial fix to HTTP Digest auth MD5 example
Moved Turn activation/dependency to railties
fix typo
Direct logging of Active Record to STDOUT so it's shown inline with the results in the console [DHH]
Add using Turn with natural language test case names if the library is available (which it will be in Rails 3.1) [DHH]
require turn only for minitest
Use Turn to format all Rails tests and enable the natural language case names
Improve docs.
pass respond_with options to controller render when using a template for api navigation
only try to display an api template in responders if the request is a get or there are no errors
when using respond_with with an invalid resource and custom options, the default response status and error messages should be returned
...
86 files changed, 987 insertions, 244 deletions
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc index 0fa18d751b..9b206fbcc7 100644 --- a/actionmailer/README.rdoc +++ b/actionmailer/README.rdoc @@ -32,7 +32,7 @@ This can be as simple as: end The body of the email is created by using an Action View template (regular -ERb) that has the instance variables that are declared in the mailer action. +ERB) that has the instance variables that are declared in the mailer action. So the corresponding body template for the method above could look like this: @@ -72,6 +72,19 @@ Or you can just chain the methods together like: Notifier.welcome.deliver # Creates the email and sends it immediately +== Setting defaults + +It is possible to set default values that will be used in every method in your Action Mailer class. To implement this functionality, you just call the public class method <tt>default</tt> which you get for free from ActionMailer::Base. This method accepts a Hash as the parameter. You can use any of the headers e-mail messages has, like <tt>:from</tt> as the key. You can also pass in a string as the key, like "Content-Type", but Action Mailer does this out of the box for you, so you wont need to worry about that. Finally it is also possible to pass in a Proc that will get evaluated when it is needed. + +Note that every value you set with this method will get over written if you use the same key in your mailer method. + +Example: + + class Authenticationmailer < ActionMailer::Base + default :from => "awesome@application.com", :subject => Proc.new { "E-mail was generated at #{Time.now}" } + ..... + end + == Receiving emails To receive emails, you need to implement a public instance method called <tt>receive</tt> that takes an diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index 2ae85f8b57..ee02cf6945 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -17,8 +17,6 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.requirements << 'none' - s.has_rdoc = true - s.add_dependency('actionpack', version) s.add_dependency('mail', '~> 2.2.15') end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index a90a7b37f7..25e2d27a01 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,52 @@ *Rails 3.1.0 (unreleased)* +* Implicit actions named not_implemented can be rendered [Santiago Pastorino] + +* Wildcard route will always matching the optional format segment by default. For example if you have this route: + + map '*pages' => 'pages#show' + + by requesting '/foo/bar.json', your `params[:pages]` will be equals to "foo/bar" with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `:format => false` like this: + + map '*pages' => 'pages#show', :format => false + +* Added Base.http_basic_authenticate_with to do simple http basic authentication with a single class method call [DHH] + + class PostsController < ApplicationController + USER_NAME, PASSWORD = "dhh", "secret" + + before_filter :authenticate, :except => [ :index ] + + def index + render :text => "Everyone can see me!" + end + + def edit + render :text => "I'm only accessible if you know the password" + end + + private + def authenticate + authenticate_or_request_with_http_basic do |user_name, password| + user_name == USER_NAME && password == PASSWORD + end + end + end + + ..can now be written as + + class PostsController < ApplicationController + http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index + + def index + render :text => "Everyone can see me!" + end + + def edit + render :text => "I'm only accessible if you know the password" + end + end + * Allow you to add `force_ssl` into controller to force browser to transfer data via HTTPS protocol on that particular controller. You can also specify `:only` or `:except` to specific it to particular action. [DHH and Prem Sichanugrist] * Allow FormHelper#form_for to specify the :method as a direct option instead of through the :html hash [DHH] diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc index 3661d27d51..b900962d32 100644 --- a/actionpack/README.rdoc +++ b/actionpack/README.rdoc @@ -19,7 +19,7 @@ It consists of several modules: * Action View, which handles view template lookup and rendering, and provides view helpers that assist when building HTML forms, Atom feeds and more. - Template formats that Action View handles are ERb (embedded Ruby, typically + Template formats that Action View handles are ERB (embedded Ruby, typically used to inline short Ruby snippets inside HTML), XML Builder and RJS (dynamically generated JavaScript from Ruby code). @@ -57,7 +57,7 @@ A short rundown of some of the major features: {Learn more}[link:classes/ActionController/Base.html] -* ERb templates (static content mixed with dynamic output from ruby) +* ERB templates (static content mixed with dynamic output from ruby) <% for post in @posts %> Title: <%= post.title %> diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index f6bc5e0d37..d3c66800d9 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -17,8 +17,6 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.requirements << 'none' - s.has_rdoc = true - s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) s.add_dependency('rack-cache', '~> 1.0.0') @@ -26,7 +24,7 @@ Gem::Specification.new do |s| s.add_dependency('i18n', '~> 0.5.0') s.add_dependency('rack', '~> 1.2.1') s.add_dependency('rack-test', '~> 0.5.7') - s.add_dependency('rack-mount', '~> 0.6.13') + s.add_dependency('rack-mount', '~> 0.7.1') s.add_dependency('tzinfo', '~> 0.3.23') - s.add_dependency('erubis', '~> 2.6.6') + s.add_dependency('erubis', '~> 2.7.0') end diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index c384fd0978..0951267fea 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -1,3 +1,4 @@ +require 'erubis' require 'active_support/configurable' require 'active_support/descendants_tracker' require 'active_support/core_ext/module/anonymous' @@ -18,6 +19,7 @@ module AbstractController include ActiveSupport::Configurable extend ActiveSupport::DescendantsTracker + undef_method :not_implemented class << self attr_reader :abstract alias_method :abstract?, :abstract @@ -128,20 +130,23 @@ module AbstractController self.class.action_methods end - private + # Returns true if the name can be considered an action. This can + # be overridden in subclasses to modify the semantics of what + # can be considered an action. + # + # For instance, this is overriden by ActionController to add + # the implicit rendering feature. + # + # ==== Parameters + # * <tt>name</tt> - The name of an action to be tested + # + # ==== Returns + # * <tt>TrueClass</tt>, <tt>FalseClass</tt> + def action_method?(name) + self.class.action_methods.include?(name) + end - # Returns true if the name can be considered an action. This can - # be overridden in subclasses to modify the semantics of what - # can be considered an action. - # - # ==== Parameters - # * <tt>name</tt> - The name of an action to be tested - # - # ==== Returns - # * <tt>TrueClass</tt>, <tt>FalseClass</tt> - def action_method?(name) - self.class.action_methods.include?(name) - end + private # Call the action. Override this in a subclass to modify the # behavior around processing an action. This, and not #process, @@ -160,8 +165,8 @@ module AbstractController # If the action name was not found, but a method called "action_missing" # was found, #method_for_action will return "_handle_action_missing". # This method calls #action_missing with the current action name. - def _handle_action_missing - action_missing(@_action_name) + def _handle_action_missing(*args) + action_missing(@_action_name, *args) end # Takes an action name and returns the name of the method that will diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index 1943ca4436..95992c2698 100644 --- a/actionpack/lib/abstract_controller/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb @@ -14,7 +14,7 @@ module AbstractController # Override AbstractController::Base's process_action to run the # process_action callbacks around the normal behavior. def process_action(method_name, *args) - run_callbacks(:process_action, action_name) do + run_callbacks(:process_action, method_name) do super end end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index e6523e56d2..5f9e082cd3 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -105,7 +105,7 @@ module ActionController # == Renders # # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering - # of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured. + # of a template. Included in the Action Pack is the Action View, which enables rendering of ERB templates. It's automatically configured. # The controller passes objects to the view by assigning instance variables: # # def show @@ -128,7 +128,7 @@ module ActionController # end # end # - # Read more about writing ERb and Builder templates in ActionView::Base. + # Read more about writing ERB and Builder templates in ActionView::Base. # # == Redirects # diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 39c804d707..1d6df89007 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -8,9 +8,7 @@ module ActionController # === Simple \Basic example # # class PostsController < ApplicationController - # USER_NAME, PASSWORD = "dhh", "secret" - # - # before_filter :authenticate, :except => [ :index ] + # http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index # # def index # render :text => "Everyone can see me!" @@ -19,15 +17,7 @@ module ActionController # def edit # render :text => "I'm only accessible if you know the password" # end - # - # private - # def authenticate - # authenticate_or_request_with_http_basic do |user_name, password| - # user_name == USER_NAME && password == PASSWORD - # end - # end - # end - # + # end # # === Advanced \Basic example # @@ -77,7 +67,7 @@ module ActionController # class PostsController < ApplicationController # REALM = "SuperSecret" # USERS = {"dhh" => "secret", #plain text password - # "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password + # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password # # before_filter :authenticate, :except => [:index] # @@ -115,6 +105,18 @@ module ActionController extend self module ControllerMethods + extend ActiveSupport::Concern + + module ClassMethods + def http_basic_authenticate_with(options = {}) + before_filter(options.except(:name, :password, :realm)) do + authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password| + name == options[:name] && password == options[:password] + end + end + end + end + def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure) authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm) end @@ -378,7 +380,6 @@ module ActionController # # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] module Token - extend self module ControllerMethods @@ -458,6 +459,5 @@ module ActionController controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized end end - end end diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb index cfa7004048..3ec0c4c6a4 100644 --- a/actionpack/lib/action_controller/metal/implicit_render.rb +++ b/actionpack/lib/action_controller/metal/implicit_render.rb @@ -1,21 +1,21 @@ module ActionController module ImplicitRender - def send_action(*) - ret = super - default_render unless response_body - ret + def send_action(method, *args) + if respond_to?(method, true) + ret = super + default_render unless response_body + ret + else + default_render + end end - def default_render - render + def default_render(*args) + render(*args) end - def method_for_action(action_name) - super || begin - if template_exists?(action_name.to_s, _prefixes) - "default_render" - end - end + def action_method?(action_name) + super || template_exists?(action_name.to_s, _prefixes) end end end diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index a2e06fe0a6..6469e24d36 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -189,7 +189,7 @@ module ActionController #:nodoc: raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given? if response = retrieve_response_from_mimes(mimes, &block) - response.call + response.call(nil) end end @@ -222,6 +222,9 @@ module ActionController #:nodoc: # is quite simple (it just needs to respond to call), you can even give # a proc to it. # + # In order to use respond_with, first you need to declare the formats your + # controller responds to in the class level with a call to <tt>respond_to</tt>. + # def respond_with(*resources, &block) raise "In order to use respond_with, first you need to declare the formats your " << "controller responds to in the class level" if self.class.mimes_for_respond_to.empty? @@ -259,7 +262,7 @@ module ActionController #:nodoc: # def retrieve_response_from_mimes(mimes=nil, &block) mimes ||= collect_mimes_from_class_level - collector = Collector.new(mimes) { default_render } + collector = Collector.new(mimes) { |options| default_render(options || {}) } block.call(collector) if block_given? if format = request.negotiate_mime(collector.order) diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index 4b45413cf8..59a3621f72 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -77,6 +77,37 @@ module ActionController #:nodoc: # # respond_with(@project, :manager, @task) # + # === Custom options + # + # <code>respond_with</code> also allow you to pass options that are forwarded + # to the underlying render call. Those options are only applied success + # scenarios. For instance, you can do the following in the create method above: + # + # def create + # @project = Project.find(params[:project_id]) + # @task = @project.comments.build(params[:task]) + # flash[:notice] = 'Task was successfully created.' if @task.save + # respond_with(@project, @task, :status => 201) + # end + # + # This will return status 201 if the task was saved with success. If not, + # it will simply ignore the given options and return status 422 and the + # resource errors. To customize the failure scenario, you can pass a + # a block to <code>respond_with</code>: + # + # def create + # @project = Project.find(params[:project_id]) + # @task = @project.comments.build(params[:task]) + # respond_with(@project, @task, :status => 201) do |format| + # if @task.save + # flash[:notice] = 'Task was successfully created.' + # else + # format.html { render "some_special_template" } + # end + # end + # end + # + # Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>. class Responder attr_reader :controller, :request, :format, :resource, :resources, :options @@ -131,7 +162,11 @@ module ActionController #:nodoc: # responds to :to_format and display it. # def to_format - default_render + if get? || !has_errors? + default_render + else + display_errors + end rescue ActionView::MissingTemplate => e api_behavior(e) end @@ -155,8 +190,6 @@ module ActionController #:nodoc: if get? display resource - elsif has_errors? - display resource.errors, :status => :unprocessable_entity elsif post? display resource, :status => :created, :location => api_location elsif has_empty_resource_definition? @@ -185,7 +218,7 @@ module ActionController #:nodoc: # controller. # def default_render - @default_response.call + @default_response.call(options) end # Display is just a shortcut to render a resource with the current format. @@ -209,6 +242,10 @@ module ActionController #:nodoc: controller.render given_options.merge!(options).merge!(format => resource) end + def display_errors + controller.render format => resource.errors, :status => :unprocessable_entity + end + # Check whether the resource has errors. # def has_errors? diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 14c424f24b..35be0b3a27 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -104,10 +104,16 @@ module ActionDispatch @options.reverse_merge!(:controller => /.+?/) end + # Add a constraint for wildcard route to make it non-greedy and match the + # optional format part of the route by default + if path.match(/\*([^\/]+)$/) && @options[:format] != false + @options.reverse_merge!(:"#{$1}" => /.+?/) + end + if @options[:format] == false @options.delete(:format) path - elsif path.include?(":format") || path.end_with?('/') || path.match(/^\/?\*/) + elsif path.include?(":format") || path.end_with?('/') path else "#{path}(.:format)" diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index 11e8c63fa0..b760db42e2 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -46,7 +46,7 @@ module ActionDispatch expected_options.stringify_keys! msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>", request.path_parameters, expected_options, expected_options.diff(request.path_parameters)) - assert_block(msg) { request.path_parameters == expected_options } + assert_equal(expected_options, request.path_parameters, msg) end # Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+. @@ -84,11 +84,11 @@ module ActionDispatch found_extras = options.reject {|k, v| ! extra_keys.include? k} msg = build_message(message, "found extras <?>, not <?>", found_extras, extras) - assert_block(msg) { found_extras == extras } + assert_equal(extras, found_extras, msg) msg = build_message(message, "The generated path <?> did not match <?>", generated_path, expected_path) - assert_block(msg) { expected_path == generated_path } + assert_equal(expected_path, generated_path, msg) end # Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index ab8c6259c5..5519103627 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -8,13 +8,13 @@ require 'action_view/log_subscriber' module ActionView #:nodoc: # = Action View Base # - # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb + # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERB # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used. # If the template file has a <tt>.rjs</tt> extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator. # - # == ERb + # == ERB # - # You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the + # You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the # following loop for names: # # <b>Names of all the people</b> @@ -23,7 +23,7 @@ module ActionView #:nodoc: # <% end %> # # The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this - # is not just a usage suggestion. Regular output functions like print or puts won't work with ERb templates. So this would be wrong: + # is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong: # # <%# WRONG %> # Hi, Mr. <% puts "Frodo" %> @@ -81,7 +81,7 @@ module ActionView #:nodoc: # # == Builder # - # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An XmlMarkup object + # Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object # named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension. # # Here are some basic examples: diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index db9d7a08ff..96e5722252 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -4,7 +4,7 @@ module ActionView # = Action View Atom Feed Helpers module Helpers #:nodoc: module AtomFeedHelper - # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERb or any other + # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other # template languages). # # Full usage example: diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index c88bd1efd5..9ac7dff1ec 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -14,7 +14,7 @@ module ActionView # variable. You can then use this variable anywhere in your templates or layout. # # ==== Examples - # The capture method can be used in ERb templates... + # The capture method can be used in ERB templates... # # <% @greeting = capture do %> # Welcome to my shiny new web page! The date and time is diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 18e303778c..506db24dc2 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -584,7 +584,7 @@ module ActionView # Works like update_page but wraps the generated JavaScript in a # <tt>\<script></tt> tag. Use this to include generated JavaScript in an - # ERb template. See JavaScriptGenerator for more information. + # ERB template. See JavaScriptGenerator for more information. # # +html_options+ may be a hash of <tt>\<script></tt> attributes to be # passed to ActionView::Helpers::JavaScriptHelper#javascript_tag. diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 6496397c31..de75488e72 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -186,7 +186,7 @@ module ActionView # link_to "Profiles", :controller => "profiles" # # => <a href="/profiles">Profiles</a> # - # You can use a block as well if your link target is hard to fit into the name parameter. ERb example: + # You can use a block as well if your link target is hard to fit into the name parameter. ERB example: # # <%= link_to(@profile) do %> # <strong><%= @profile.name %></strong> -- <span>Check it out!</span> diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index a36837afc8..0443b1b0f5 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -55,7 +55,7 @@ module ActionView class ERB # Specify trim mode for the ERB compiler. Defaults to '-'. - # See ERb documentation for suitable values. + # See ERB documentation for suitable values. class_attribute :erb_trim_mode self.erb_trim_mode = '-' diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 01c650a494..bd3e13e6fa 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -6,6 +6,8 @@ class HttpBasicAuthenticationTest < ActionController::TestCase before_filter :authenticate_with_request, :only => :display before_filter :authenticate_long_credentials, :only => :show + http_basic_authenticate_with :name => "David", :password => "Goliath", :only => :search + def index render :text => "Hello Secret" end @@ -17,6 +19,10 @@ class HttpBasicAuthenticationTest < ActionController::TestCase def show render :text => 'Only for loooooong credentials' end + + def search + render :text => 'All inline' + end private @@ -104,6 +110,16 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert assigns(:logged_in) assert_equal 'Definitely Maybe', @response.body end + + test "authenticate with class method" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials('David', 'Goliath') + get :search + assert_response :success + + @request.env['HTTP_AUTHORIZATION'] = encode_credentials('David', 'WRONG!') + get :search + assert_response :unauthorized + end private diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 5debf96232..41f80d0784 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -558,6 +558,15 @@ class RespondWithController < ActionController::Base respond_with(resource, :location => "http://test.host/", :status => :created) end + def using_invalid_resource_with_template + respond_with(resource) + end + + def using_options_with_template + @customer = resource + respond_with(@customer, :status => 123, :location => "http://test.host/") + end + def using_resource_with_responder responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" } respond_with(resource, :responder => responder) @@ -953,6 +962,54 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal 201, @response.status end + def test_using_resource_with_status_and_location_with_invalid_resource + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + + @request.accept = "text/xml" + + post :using_resource_with_status_and_location + assert_equal errors.to_xml, @response.body + assert_equal 422, @response.status + assert_equal nil, @response.location + + put :using_resource_with_status_and_location + assert_equal errors.to_xml, @response.body + assert_equal 422, @response.status + assert_equal nil, @response.location + end + + def test_using_invalid_resource_with_template + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + + @request.accept = "text/xml" + + post :using_invalid_resource_with_template + assert_equal errors.to_xml, @response.body + assert_equal 422, @response.status + assert_equal nil, @response.location + + put :using_invalid_resource_with_template + assert_equal errors.to_xml, @response.body + assert_equal 422, @response.status + assert_equal nil, @response.location + end + + def test_using_options_with_template + @request.accept = "text/xml" + + post :using_options_with_template + assert_equal "<customer-name>david</customer-name>", @response.body + assert_equal 123, @response.status + assert_equal "http://test.host/", @response.location + + put :using_options_with_template + assert_equal "<customer-name>david</customer-name>", @response.body + assert_equal 123, @response.status + assert_equal "http://test.host/", @response.location + end + def test_using_resource_with_responder get :using_resource_with_responder assert_equal "Resource name is david", @response.body diff --git a/actionpack/test/controller/new_base/render_implicit_action_test.rb b/actionpack/test/controller/new_base/render_implicit_action_test.rb index 9f69d20329..3bb3016fdb 100644 --- a/actionpack/test/controller/new_base/render_implicit_action_test.rb +++ b/actionpack/test/controller/new_base/render_implicit_action_test.rb @@ -3,8 +3,9 @@ require 'abstract_unit' module RenderImplicitAction class SimpleController < ::ApplicationController self.view_paths = [ActionView::FixtureResolver.new( - "render_implicit_action/simple/hello_world.html.erb" => "Hello world!", - "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!" + "render_implicit_action/simple/hello_world.html.erb" => "Hello world!", + "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!", + "render_implicit_action/simple/not_implemented.html.erb" => "Not Implemented" )] def hello_world() end @@ -24,5 +25,18 @@ module RenderImplicitAction assert_body "Hello hyphen-ated!" assert_status 200 end + + test "render an action called not_implemented" do + get "/render_implicit_action/simple/not_implemented" + + assert_body "Not Implemented" + assert_status 200 + end + + test "action_method? returns true for implicit actions" do + assert SimpleController.new.action_method?(:hello_world) + assert SimpleController.new.action_method?(:"hyphen-ated") + assert SimpleController.new.action_method?(:not_implemented) + end end end diff --git a/actionpack/test/action_dispatch/routing/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb index e21b271907..b6c08ffc33 100644 --- a/actionpack/test/action_dispatch/routing/mapper_test.rb +++ b/actionpack/test/dispatch/mapper_test.rb @@ -25,6 +25,10 @@ module ActionDispatch def conditions routes.map { |x| x[1] } end + + def requirements + routes.map { |x| x[2] } + end end def test_initialize @@ -50,8 +54,34 @@ module ActionDispatch def test_map_wildcard fakeset = FakeSet.new mapper = Mapper.new fakeset - mapper.match '/*path', :to => 'pages#show', :as => :page + mapper.match '/*path', :to => 'pages#show' + assert_equal '/*path(.:format)', fakeset.conditions.first[:path_info] + assert_equal(/.+?/, fakeset.requirements.first[:path]) + end + + def test_map_wildcard_with_other_element + fakeset = FakeSet.new + mapper = Mapper.new fakeset + mapper.match '/*path/foo/:bar', :to => 'pages#show' + assert_equal '/*path/foo/:bar(.:format)', fakeset.conditions.first[:path_info] + assert_nil fakeset.requirements.first[:path] + end + + def test_map_wildcard_with_multiple_wildcard + fakeset = FakeSet.new + mapper = Mapper.new fakeset + mapper.match '/*foo/*bar', :to => 'pages#show' + assert_equal '/*foo/*bar(.:format)', fakeset.conditions.first[:path_info] + assert_nil fakeset.requirements.first[:foo] + assert_equal(/.+?/, fakeset.requirements.first[:bar]) + end + + def test_map_wildcard_with_format_false + fakeset = FakeSet.new + mapper = Mapper.new fakeset + mapper.match '/*path', :to => 'pages#show', :format => false assert_equal '/*path', fakeset.conditions.first[:path_info] + assert_nil fakeset.requirements.first[:path] end end end diff --git a/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb b/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb new file mode 100644 index 0000000000..bf5869ed22 --- /dev/null +++ b/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb @@ -0,0 +1 @@ +<content>I should not be displayed</content>
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb b/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb new file mode 100644 index 0000000000..b313017913 --- /dev/null +++ b/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb @@ -0,0 +1 @@ +<customer-name><%= @customer.name %></customer-name>
\ No newline at end of file diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index aca2fef170..fd1f824a39 100644 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -2720,11 +2720,11 @@ class DateHelperTest < ActionView::TestCase end def test_time_tag_pubdate_option - assert_match /<time.*pubdate="pubdate">.*<\/time>/, time_tag(Time.now, :pubdate => true) + assert_match(/<time.*pubdate="pubdate">.*<\/time>/, time_tag(Time.now, :pubdate => true)) end def test_time_tag_with_given_text - assert_match /<time.*>Right now<\/time>/, time_tag(Time.now, 'Right now') + assert_match(/<time.*>Right now<\/time>/, time_tag(Time.now, 'Right now')) end def test_time_tag_with_different_format diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index fec9c7ff8b..9f80673bb8 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -17,8 +17,6 @@ Gem::Specification.new do |s| s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' - s.has_rdoc = true - s.add_dependency('activesupport', version) s.add_dependency('builder', '~> 3.0.0') s.add_dependency('i18n', '~> 0.5.0') diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index f3ec406ec8..22ca3efa2b 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -294,8 +294,8 @@ module ActiveModel type = options.delete(:message) if options[:message].is_a?(Symbol) defaults = @base.class.lookup_ancestors.map do |klass| - [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.attributes.#{attribute}.#{type}", - :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.#{type}" ] + [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}", + :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ] end defaults << options.delete(:message) diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index eb9b847509..315ccd40e9 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -4,7 +4,7 @@ require 'active_support/core_ext/module/introspection' module ActiveModel class Name < String - attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key + attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key, :i18n_key alias_method :cache_key, :collection def initialize(klass, namespace = nil) @@ -20,6 +20,7 @@ module ActiveModel @partial_path = "#{@collection}/#{@element}".freeze @param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze + @i18n_key = self.underscore.to_sym end # Transform the model name into a more humane format, using I18n. By default, @@ -33,7 +34,7 @@ module ActiveModel @klass.respond_to?(:i18n_scope) defaults = @klass.lookup_ancestors.map do |klass| - klass.model_name.underscore.to_sym + klass.model_name.i18n_key end defaults << options[:default] if options[:default] @@ -44,9 +45,10 @@ module ActiveModel end private - def _singularize(str) - ActiveSupport::Inflector.underscore(str).tr('/', '_') - end + + def _singularize(string, replacement='_') + ActiveSupport::Inflector.underscore(string).tr('/', replacement) + end end # == Active Model Naming @@ -62,6 +64,9 @@ module ActiveModel # BookCover.model_name # => "BookCover" # BookCover.model_name.human # => "Book cover" # + # BookCover.model_name.i18n_key # => "book_cover" + # BookModule::BookCover.model_name.i18n_key # => "book_module.book_cover" + # # Providing the functionality that ActiveModel::Naming provides in your object # is required to pass the Active Model Lint test. So either extending the provided # method below, or rolling your own is required. diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index dbb76244e4..920a133159 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -44,7 +44,7 @@ module ActiveModel # Specify +options+ with additional translating options. def human_attribute_name(attribute, options = {}) defaults = lookup_ancestors.map do |klass| - :"#{self.i18n_scope}.attributes.#{klass.model_name.underscore}.#{attribute}" + :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}" end defaults << :"attributes.#{attribute}" diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 6be46349fb..e536d2b408 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,17 @@ *Rails 3.1.0 (unreleased)* +* ConnectionManagement middleware is changed to clean up the connection pool + after the rack body has been flushed. + +* Added an update_column method on ActiveRecord. This new method updates a given attribute on an object, skipping validations and callbacks. + It is recommended to use #update_attribute unless you are sure you do not want to execute any callback, including the modification of + the updated_at column. It should not be called on new records. + Example: + + User.first.update_column(:name, "sebastian") # => true + + [Sebastian Martinez] + * Associations with a :through option can now use *any* association as the through or source association, including other associations which have a :through option and has_and_belongs_to_many associations diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index b1df24844a..c3cd76a714 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -17,7 +17,6 @@ Gem::Specification.new do |s| s.files = Dir['CHANGELOG', 'README.rdoc', 'examples/**/*', 'lib/**/*'] s.require_path = 'lib' - s.has_rdoc = true s.extra_rdoc_files = %w( README.rdoc ) s.rdoc_options.concat ['--main', 'README.rdoc'] diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index dfcb116392..1d2e8667e4 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -34,8 +34,7 @@ module ActiveRecord when :destroy target.destroy when :nullify - target.send("#{reflection.foreign_key}=", nil) - target.save(:validations => false) + target.update_attribute(reflection.foreign_key, nil) end end end diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb index 3279e56e7d..2b1d888a9a 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb @@ -22,7 +22,7 @@ module ActiveRecord end def aliased_table - Arel::Nodes::TableAlias.new aliased_table_name, table + Arel::Nodes::TableAlias.new table, aliased_table_name end def ==(other) diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index fcdd31ddea..5f06452247 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -17,6 +17,11 @@ module ActiveRecord @primary_key ||= reset_primary_key end + # Returns a quoted version of the primary key name, used to construct SQL statements. + def quoted_primary_key + @quoted_primary_key ||= connection.quote_column_name(primary_key) + end + def reset_primary_key #:nodoc: key = self == base_class ? get_primary_key(base_class.name) : base_class.primary_key @@ -43,7 +48,12 @@ module ActiveRecord end attr_accessor :original_primary_key - attr_writer :primary_key + + # Attribute writer for the primary key column + def primary_key=(value) + @quoted_primary_key = nil + @primary_key = value + end # Sets the name of the primary key column to use to the given value, # or (if the value is nil or false) to the value returned by the given @@ -53,6 +63,7 @@ module ActiveRecord # set_primary_key "sysid" # end def set_primary_key(value = nil, &block) + @quoted_primary_key = nil @primary_key ||= '' self.original_primary_key = @primary_key value &&= value.to_s diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index 3c4dab304e..7661676f8c 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -32,6 +32,7 @@ module ActiveRecord @attributes[attr_name] = value end end + alias_method :raw_write_attribute, :write_attribute private # Handle *= for method_missing. diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b778b0c0f0..fe81c7dc2f 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -437,9 +437,10 @@ module ActiveRecord #:nodoc: self._attr_readonly = [] class << self # Class methods - delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped + delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped + delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped delegate :find_each, :find_in_batches, :to => :scoped - delegate :select, :group, :order, :except, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped + delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped # Executes a custom SQL query against your database and returns all the results. The results will 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 4297c26413..b4db1eed18 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -151,6 +151,12 @@ module ActiveRecord @reserved_connections[current_connection_id] ||= checkout end + # Check to see if there is an active connection in this connection + # pool. + def active_connection? + @reserved_connections.key? current_connection_id + end + # Signal that the thread is finished with the current connection. # #release_connection releases the connection-thread association # and returns the connection to the pool. @@ -346,6 +352,12 @@ module ActiveRecord @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec) end + # Returns true if there are any active connections among the connection + # pools that the ConnectionHandler is managing. + def active_connections? + connection_pools.values.any? { |pool| pool.active_connection? } + end + # Returns any connections in use by the current thread back to the pool, # and also returns connections to the pool cached by threads that are no # longer alive. @@ -405,18 +417,40 @@ module ActiveRecord end class ConnectionManagement + class Proxy # :nodoc: + attr_reader :body, :testing + + def initialize(body, testing = false) + @body = body + @testing = testing + end + + def each(&block) + body.each(&block) + end + + def close + body.close if body.respond_to?(:close) + + # Don't return connection (and perform implicit rollback) if + # this request is a part of integration test + ActiveRecord::Base.clear_active_connections! unless testing + end + end + def initialize(app) @app = app end def call(env) - @app.call(env) - ensure - # Don't return connection (and perform implicit rollback) if - # this request is a part of integration test - unless env.key?("rack.test") - ActiveRecord::Base.clear_active_connections! - end + testing = env.key?('rack.test') + + status, headers, body = @app.call(env) + + [status, headers, Proxy.new(body, testing)] + rescue + ActiveRecord::Base.clear_active_connections! unless testing + raise end end end 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 d88720c8bf..bcd3abc08d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -116,7 +116,11 @@ module ActiveRecord connection_handler.remove_connection(klass) end - delegate :clear_active_connections!, :clear_reloadable_connections!, + def clear_active_connections! + connection_handler.clear_active_connections! + end + + delegate :clear_reloadable_connections!, :clear_all_connections!,:verify_active_connections!, :to => :connection_handler end end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index ae61d6ce94..32229a8410 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -222,7 +222,7 @@ module ActiveRecord # SCHEMA STATEMENTS ======================================== - def tables(name = nil) #:nodoc: + def tables(name = 'SCHEMA') #:nodoc: sql = <<-SQL SELECT name FROM sqlite_master @@ -350,7 +350,7 @@ module ActiveRecord end def table_structure(table_name) - structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})").to_hash + structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? structure end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index d523c643ba..0939ec2626 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -173,10 +173,10 @@ class FixturesFileNotFound < StandardError; end # traversed in the database to create the fixture hash and/or instance variables. This is expensive for # large sets of fixtured data. # -# = Dynamic fixtures with ERb +# = Dynamic fixtures with ERB # # Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can -# mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like: +# mix ERB in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like: # # <% for i in 1..1000 %> # fix_<%= i %>: @@ -186,7 +186,7 @@ class FixturesFileNotFound < StandardError; end # # This will create 1000 very simple YAML fixtures. # -# Using ERb, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>. +# Using ERB, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>. # This is however a feature to be used with some caution. The point of fixtures are that they're # stable units of predictable sample data. If you feel that you need to inject dynamic values, then # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index afadbf03ef..d31e321440 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -23,6 +23,9 @@ module ActiveRecord return unless logger.debug? payload = event.payload + + return if 'SCHEMA' == payload[:name] + name = '%s (%.1fms)' % [payload[:name], event.duration] sql = payload[:sql].squeeze(' ') binds = nil diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 3377a5934b..a916c88348 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -104,17 +104,33 @@ module ActiveRecord became end + # Updates a single attribute and saves the record. + # This is especially useful for boolean flags on existing records. Also note that + # + # * Validation is skipped. + # * Callbacks are invoked. + # * updated_at/updated_on column is updated if that column is available. + # * Updates all the attributes that are dirty in this object. + # + def update_attribute(name, value) + name = name.to_s + raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name) + send("#{name}=", value) + save(:validate => false) + end + # Updates a single attribute of an object, without calling save. # # * Validation is skipped. # * Callbacks are skipped. - # * updated_at/updated_on column is not updated in any case. + # * updated_at/updated_on column is not updated if that column is available. # def update_column(name, value) name = name.to_s raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name) - send("#{name}=", value) - self.class.update_all({ name => value }, self.class.primary_key => id) + raise ActiveRecordError, "can not update on a new record object" unless persisted? + raw_write_attribute(name, value) + self.class.update_all({ name => value }, self.class.primary_key => id) == 1 end # Updates the attributes of the model from the passed-in hash and saves the @@ -154,7 +170,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def increment!(attribute, by = 1) - increment(attribute, by).update_column(attribute, self[attribute]) + increment(attribute, by).update_attribute(attribute, self[attribute]) end # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1). @@ -171,7 +187,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def decrement!(attribute, by = 1) - decrement(attribute, by).update_column(attribute, self[attribute]) + decrement(attribute, by).update_attribute(attribute, self[attribute]) end # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So @@ -188,7 +204,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def toggle!(attribute) - toggle(attribute).update_column(attribute, self[attribute]) + toggle(attribute).update_attribute(attribute, self[attribute]) end # Reloads the attributes of this object from the database. diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 8e545f9cad..896daf516e 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -12,7 +12,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, :primary_key, :to => :klass + delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass attr_reader :table, :klass, :loaded attr_accessor :extensions diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index bf5a60f458..d52b84179f 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -83,7 +83,7 @@ module ActiveRecord private def batch_order - "#{table_name}.#{primary_key} ASC" + "#{quoted_table_name}.#{quoted_primary_key} ASC" end end end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index c1842b1a96..869eebfa34 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -196,24 +196,22 @@ module ActiveRecord end def execute_simple_calculation(operation, column_name, distinct) #:nodoc: - column = aggregate_column(column_name) - # Postgresql doesn't like ORDER BY when there are no GROUP BY relation = except(:order) - select_value = operation_over_aggregate_column(column, operation, distinct) - relation.select_values = [select_value] + if operation == "count" && (relation.limit_value || relation.offset_value) + # Shortcut when limit is zero. + return 0 if relation.limit_value == 0 - query_builder = relation.arel + query_builder = build_count_subquery(relation, column_name, distinct) + else + column = aggregate_column(column_name) - if operation == "count" - limit = relation.limit_value - offset = relation.offset_value + select_value = operation_over_aggregate_column(column, operation, distinct) - unless limit && offset - query_builder.limit = nil - query_builder.offset = nil - end + relation.select_values = [select_value] + + query_builder = relation.arel end type_cast_calculated_value(@klass.connection.select_value(query_builder.to_sql), column_for(column_name), operation) @@ -312,5 +310,18 @@ module ActiveRecord select if select !~ /(,|\*)/ end end + + def build_count_subquery(relation, column_name, distinct) + column_alias = Arel.sql('count_column') + subquery_alias = Arel.sql('subquery_for_count') + + aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias) + relation.select_values = [aliased_column] + subquery = relation.arel.as(subquery_alias) + + sm = Arel::SelectManager.new relation.engine + select_value = operation_over_aggregate_column(column_alias, 'count', distinct) + sm.project(select_value).from(subquery) + end end end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 563843f3cc..8fa315bdf3 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -183,7 +183,9 @@ module ActiveRecord def exists?(id = nil) id = id.id if ActiveRecord::Base === id - relation = select("1").limit(1) + join_dependency = construct_join_dependency_for_association_find + relation = construct_relation_for_association_find(join_dependency) + relation = relation.except(:select).select("1").limit(1) case id when Array, Hash @@ -192,14 +194,13 @@ module ActiveRecord relation = relation.where(table[primary_key].eq(id)) if id end - relation.first ? true : false + connection.select_value(relation.to_sql) ? true : false end protected def find_with_associations - including = (@eager_load_values + @includes_values).uniq - join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, []) + join_dependency = construct_join_dependency_for_association_find relation = construct_relation_for_association_find(join_dependency) rows = connection.select_all(relation.to_sql, 'SQL', relation.bind_values) join_dependency.instantiate(rows) @@ -207,6 +208,11 @@ module ActiveRecord [] end + def construct_join_dependency_for_association_find + including = (@eager_load_values + @includes_values).uniq + ActiveRecord::Associations::JoinDependency.new(@klass, including, []) + end + def construct_relation_for_association_calculations including = (@eager_load_values + @includes_values).uniq join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, arel.froms.first) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9470e7c6c5..02b7056989 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -62,6 +62,10 @@ module ActiveRecord relation end + def reorder(*args) + except(:order).order(args) + end + def joins(*args) return self if args.compact.blank? diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index dc0e0da4c5..6620464d6a 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -83,4 +83,14 @@ class EachTest < ActiveRecord::TestCase Post.find_in_batches(:batch_size => post_count + 1) {|batch| assert_kind_of Array, batch } end end + + def test_find_in_batches_should_quote_batch_order + c = Post.connection + assert_sql(/ORDER BY #{c.quote_table_name('posts')}.#{c.quote_column_name('id')}/) do + Post.find_in_batches(:batch_size => 1) do |batch| + assert_kind_of Array, batch + assert_kind_of Post, batch.first + end + end + end end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index c97f1ae634..654c4c9010 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -65,7 +65,7 @@ class CalculationsTest < ActiveRecord::TestCase c = Account.sum(:credit_limit, :group => :firm_id) [1,6,2].each { |firm_id| assert c.keys.include?(firm_id) } end - + def test_should_group_by_multiple_fields c = Account.count(:all, :group => ['firm_id', :credit_limit]) [ [nil, 50], [1, 50], [6, 50], [6, 55], [9, 53], [2, 60] ].each { |firm_and_limit| assert c.keys.include?(firm_and_limit) } @@ -109,27 +109,42 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal [2, 6], c.keys.compact end - def test_limit_with_offset_is_kept + def test_limit_should_apply_before_count + accounts = Account.limit(3).where('firm_id IS NOT NULL') + + assert_equal 3, accounts.count(:firm_id) + assert_equal 3, accounts.select(:firm_id).count + end + + def test_count_should_shortcut_with_limit_zero + accounts = Account.limit(0) + + assert_no_queries { assert_equal 0, accounts.count } + end + + def test_limit_is_kept return if current_adapter?(:OracleAdapter) - queries = assert_sql { Account.limit(1).offset(1).count } + queries = assert_sql { Account.limit(1).count } assert_equal 1, queries.length assert_match(/LIMIT/, queries.first) - assert_match(/OFFSET/, queries.first) end - def test_offset_without_limit_removes_offset + def test_offset_is_kept + return if current_adapter?(:OracleAdapter) + queries = assert_sql { Account.offset(1).count } assert_equal 1, queries.length - assert_no_match(/LIMIT/, queries.first) - assert_no_match(/OFFSET/, queries.first) + assert_match(/OFFSET/, queries.first) end - def test_limit_without_offset_removes_limit - queries = assert_sql { Account.limit(1).count } + def test_limit_with_offset_is_kept + return if current_adapter?(:OracleAdapter) + + queries = assert_sql { Account.limit(1).offset(1).count } assert_equal 1, queries.length - assert_no_match(/LIMIT/, queries.first) - assert_no_match(/OFFSET/, queries.first) + assert_match(/LIMIT/, queries.first) + assert_match(/OFFSET/, queries.first) end def test_no_limit_no_offset diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb new file mode 100644 index 0000000000..abf317768f --- /dev/null +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -0,0 +1,33 @@ +require "cases/helper" + +module ActiveRecord + module ConnectionAdapters + class ConnectionHandlerTest < ActiveRecord::TestCase + def setup + @handler = ConnectionHandler.new + @handler.establish_connection 'america', Base.connection_pool.spec + @klass = Struct.new(:name).new('america') + end + + def test_retrieve_connection + assert @handler.retrieve_connection(@klass) + end + + def test_active_connections? + assert !@handler.active_connections? + assert @handler.retrieve_connection(@klass) + assert @handler.active_connections? + @handler.clear_active_connections! + assert !@handler.active_connections? + end + + def test_retrieve_connection_pool_with_ar_base + assert_nil @handler.retrieve_connection_pool(ActiveRecord::Base) + end + + def test_retrieve_connection_pool + assert_not_nil @handler.retrieve_connection_pool(@klass) + end + end + end +end diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb index c535119972..85871aebdf 100644 --- a/activerecord/test/cases/connection_management_test.rb +++ b/activerecord/test/cases/connection_management_test.rb @@ -1,25 +1,82 @@ require "cases/helper" -class ConnectionManagementTest < ActiveRecord::TestCase - def setup - @env = {} - @app = stub('App') - @management = ActiveRecord::ConnectionAdapters::ConnectionManagement.new(@app) - - @connections_cleared = false - ActiveRecord::Base.stubs(:clear_active_connections!).with { @connections_cleared = true } - end +module ActiveRecord + module ConnectionAdapters + class ConnectionManagementTest < ActiveRecord::TestCase + class App + attr_reader :calls + def initialize + @calls = [] + end - test "clears active connections after each call" do - @app.expects(:call).with(@env) - @management.call(@env) - assert @connections_cleared - end + def call(env) + @calls << env + [200, {}, ['hi mom']] + end + end + + def setup + @env = {} + @app = App.new + @management = ConnectionManagement.new(@app) + + # make sure we have an active connection + assert ActiveRecord::Base.connection + assert ActiveRecord::Base.connection_handler.active_connections? + end + + def test_app_delegation + manager = ConnectionManagement.new(@app) + + manager.call @env + assert_equal [@env], @app.calls + end + + def test_connections_are_active_after_call + @management.call(@env) + assert ActiveRecord::Base.connection_handler.active_connections? + end + + def test_body_responds_to_each + _, _, body = @management.call(@env) + bits = [] + body.each { |bit| bits << bit } + assert_equal ['hi mom'], bits + end + + def test_connections_are_cleared_after_body_close + _, _, body = @management.call(@env) + body.close + assert !ActiveRecord::Base.connection_handler.active_connections? + end + + def test_active_connections_are_not_cleared_on_body_close_during_test + @env['rack.test'] = true + _, _, body = @management.call(@env) + body.close + assert ActiveRecord::Base.connection_handler.active_connections? + end + + def test_connections_closed_if_exception + app = Class.new(App) { def call(env); raise; end }.new + explosive = ConnectionManagement.new(app) + assert_raises(RuntimeError) { explosive.call(@env) } + assert !ActiveRecord::Base.connection_handler.active_connections? + end + + def test_connections_not_closed_if_exception_and_test + @env['rack.test'] = true + app = Class.new(App) { def call(env); raise; end }.new + explosive = ConnectionManagement.new(app) + assert_raises(RuntimeError) { explosive.call(@env) } + assert ActiveRecord::Base.connection_handler.active_connections? + end - test "doesn't clear active connections when running in a test case" do - @env['rack.test'] = true - @app.expects(:call).with(@env) - @management.call(@env) - assert !@connections_cleared + test "doesn't clear active connections when running in a test case" do + @env['rack.test'] = true + @management.call(@env) + assert ActiveRecord::Base.connection_handler.active_connections? + end + end end end diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 7ac14fa8d6..f92f4e62c5 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -18,6 +18,14 @@ module ActiveRecord end end + def test_active_connection? + assert !@pool.active_connection? + assert @pool.connection + assert @pool.active_connection? + @pool.release_connection + assert !@pool.active_connection? + end + def test_pool_caches_columns columns = @pool.columns['posts'] assert_equal columns, @pool.columns['posts'] diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index b75482a956..b1ce846218 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -487,8 +487,7 @@ class DirtyTest < ActiveRecord::TestCase assert !pirate.previous_changes.key?('created_on') pirate = Pirate.find_by_catchphrase("Ahoy!") - pirate.catchphrase = "Ninjas suck!" - pirate.save(:validations => false) + pirate.update_attribute(:catchphrase, "Ninjas suck!") assert_equal 2, pirate.previous_changes.size assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes['catchphrase'] diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 543981b4a0..3c242667eb 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -74,6 +74,11 @@ class FinderTest < ActiveRecord::TestCase end end + def test_exists_does_not_instantiate_records + Developer.expects(:instantiate).never + Developer.exists? + end + def test_find_by_array_of_one_id assert_kind_of(Array, Topic.find([ 1 ])) assert_equal(1, Topic.find([ 1 ]).length) @@ -203,6 +208,14 @@ class FinderTest < ActiveRecord::TestCase end end + def test_model_class_responds_to_first_bang + assert Topic.first! + Topic.delete_all + assert_raises ActiveRecord::RecordNotFound do + Topic.first! + end + end + def test_last_bang_present assert_nothing_raised do assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").last! @@ -215,6 +228,14 @@ class FinderTest < ActiveRecord::TestCase end end + def test_model_class_responds_to_last_bang + assert_equal topics(:fourth), Topic.last! + assert_raises ActiveRecord::RecordNotFound do + Topic.delete_all + Topic.last! + end + end + def test_unexisting_record_exception_handling assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1).parent diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index cbaaca764b..8ebde933b4 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -22,6 +22,33 @@ class LogSubscriberTest < ActiveRecord::TestCase ActiveRecord::Base.logger = logger end + def test_schema_statements_are_ignored + event = Struct.new(:duration, :payload) + + logger = Class.new(ActiveRecord::LogSubscriber) { + attr_accessor :debugs + + def initialize + @debugs = [] + super + end + + def debug message + @debugs << message + end + }.new + assert_equal 0, logger.debugs.length + + logger.sql(event.new(0, { :sql => 'hi mom!' })) + assert_equal 1, logger.debugs.length + + logger.sql(event.new(0, { :sql => 'hi mom!', :name => 'foo' })) + assert_equal 2, logger.debugs.length + + logger.sql(event.new(0, { :sql => 'hi mom!', :name => 'SCHEMA' })) + assert_equal 2, logger.debugs.length + end + def test_basic_query_logging Developer.all wait diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index fb050c3e52..9b20ea08de 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -64,7 +64,7 @@ class NamedScopeTest < ActiveRecord::TestCase assert Reply.scopes.include?(:base) assert_equal Reply.find(:all), Reply.base end - + def test_classes_dont_inherit_subclasses_scopes assert !ActiveRecord::Base.scopes.include?(:base) end @@ -246,6 +246,12 @@ class NamedScopeTest < ActiveRecord::TestCase assert_no_queries { assert topics.any? } end + def test_model_class_should_respond_to_any + assert Topic.any? + Topic.delete_all + assert !Topic.any? + end + def test_many_should_not_load_results topics = Topic.base assert_queries(2) do @@ -280,6 +286,15 @@ class NamedScopeTest < ActiveRecord::TestCase assert Topic.base.many? end + def test_model_class_should_respond_to_many + Topic.delete_all + assert !Topic.many? + Topic.create! + assert !Topic.many? + Topic.create! + assert Topic.many? + end + def test_should_build_on_top_of_scope topic = Topic.approved.build({}) assert topic.approved diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 68d861c9c2..9aa13f04cd 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -327,6 +327,68 @@ class PersistencesTest < ActiveRecord::TestCase assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } end + def test_update_attribute + assert !Topic.find(1).approved? + Topic.find(1).update_attribute("approved", true) + assert Topic.find(1).approved? + + Topic.find(1).update_attribute(:approved, false) + assert !Topic.find(1).approved? + end + + def test_update_attribute_for_readonly_attribute + minivan = Minivan.find('m1') + assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') } + end + + # This test is correct, but it is hard to fix it since + # update_attribute trigger simply call save! that triggers + # all callbacks. + # def test_update_attribute_with_one_changed_and_one_updated + # t = Topic.order('id').limit(1).first + # title, author_name = t.title, t.author_name + # t.author_name = 'John' + # t.update_attribute(:title, 'super_title') + # assert_equal 'John', t.author_name + # assert_equal 'super_title', t.title + # assert t.changed?, "topic should have changed" + # assert t.author_name_changed?, "author_name should have changed" + # assert !t.title_changed?, "title should not have changed" + # assert_nil t.title_change, 'title change should be nil' + # assert_equal ['author_name'], t.changed + # + # t.reload + # assert_equal 'David', t.author_name + # assert_equal 'super_title', t.title + # end + + def test_update_attribute_with_one_updated + t = Topic.first + title = t.title + t.update_attribute(:title, 'super_title') + assert_equal 'super_title', t.title + assert !t.changed?, "topic should not have changed" + assert !t.title_changed?, "title should not have changed" + assert_nil t.title_change, 'title change should be nil' + + t.reload + assert_equal 'super_title', t.title + end + + def test_update_attribute_for_updated_at_on + developer = Developer.find(1) + prev_month = Time.now.prev_month + + developer.update_attribute(:updated_at, prev_month) + assert_equal prev_month, developer.updated_at + + developer.update_attribute(:salary, 80001) + assert_not_equal prev_month, developer.updated_at + + developer.reload + assert_not_equal prev_month, developer.updated_at + end + def test_update_column topic = Topic.find(1) topic.update_column("approved", true) @@ -340,6 +402,35 @@ class PersistencesTest < ActiveRecord::TestCase assert !topic.approved? end + def test_update_column_should_not_use_setter_method + dev = Developer.find(1) + dev.instance_eval { def salary=(value); write_attribute(:salary, value * 2); end } + + dev.update_column(:salary, 80000) + assert_equal 80000, dev.salary + + dev.reload + assert_equal 80000, dev.salary + end + + def test_update_column_should_raise_exception_if_new_record + topic = Topic.new + assert_raises(ActiveRecord::ActiveRecordError) { topic.update_column("approved", false) } + end + + def test_update_column_should_not_leave_the_object_dirty + topic = Topic.find(1) + topic.update_attribute("content", "Have a nice day") + + topic.reload + topic.update_column(:content, "You too") + assert_equal [], topic.changed + + topic.reload + topic.update_column("content", "Have a nice day") + assert_equal [], topic.changed + end + def test_update_column_with_model_having_primary_key_other_than_id minivan = Minivan.find('m1') new_name = 'sebavan' @@ -366,7 +457,7 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal prev_month, developer.updated_at developer.reload - assert_equal prev_month, developer.updated_at + assert_equal prev_month.to_i, developer.updated_at.to_i end def test_update_column_with_one_changed_and_one_updated @@ -378,7 +469,6 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal 'super_title', t.title assert t.changed?, "topic should have changed" assert t.author_name_changed?, "author_name should have changed" - assert t.title_changed?, "title should have changed" t.reload assert_equal author_name, t.author_name diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 63d8c7d1c1..05a41d8a0a 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -136,4 +136,13 @@ class PrimaryKeysTest < ActiveRecord::TestCase assert_nil ActiveRecord::Base.connection.primary_key('developers_projects') end end + + def test_quoted_primary_key_after_set_primary_key + k = Class.new( ActiveRecord::Base ) + assert_equal k.connection.quote_column_name("id"), k.quoted_primary_key + k.primary_key = "foo" + assert_equal k.connection.quote_column_name("foo"), k.quoted_primary_key + k.set_primary_key "bar" + assert_equal k.connection.quote_column_name("bar"), k.quoted_primary_key + end end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index 7369aaea1d..30a783d5a2 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -429,9 +429,9 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end - def test_except_and_order_overrides_default_scope_order + def test_reorder_overrides_default_scope_order expected = Developer.order('name DESC').collect { |dev| dev.name } - received = DeveloperOrderedBySalary.except(:order).order('name DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.reorder('name DESC').collect { |dev| dev.name } assert_equal expected, received end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 4c5c871251..fc9df8c7a3 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -151,6 +151,12 @@ class RelationTest < ActiveRecord::TestCase assert_equal topics(:fourth).title, topics.first.title end + def test_finding_with_reorder + topics = Topic.order('author_name').order('title').reorder('id').all + topics_titles = topics.map{ |t| t.title } + assert_equal ['The First Topic', 'The Second Topic of the day', 'The Third Topic of the day', 'The Fourth Topic of the day'], topics_titles + end + def test_finding_with_order_and_take entrants = Entrant.order("id ASC").limit(2).to_a @@ -666,6 +672,34 @@ class RelationTest < ActiveRecord::TestCase assert_no_queries { assert_equal 9, best_posts.size } end + def test_size_with_limit + posts = Post.limit(10) + + assert_queries(1) { assert_equal 10, posts.size } + assert ! posts.loaded? + + best_posts = posts.where(:comments_count => 0) + best_posts.to_a # force load + assert_no_queries { assert_equal 9, best_posts.size } + end + + def test_size_with_zero_limit + posts = Post.limit(0) + + assert_no_queries { assert_equal 0, posts.size } + assert ! posts.loaded? + + posts.to_a # force load + assert_no_queries { assert_equal 0, posts.size } + end + + def test_empty_with_zero_limit + posts = Post.limit(0) + + assert_no_queries { assert_equal true, posts.empty? } + assert ! posts.loaded? + end + def test_count_complex_chained_relations posts = Post.select('comments_count').where('id is not null').group("author_id").where("comments_count > 0") diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 08bbd14d38..ceb1452afd 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -113,8 +113,7 @@ class TimestampTest < ActiveRecord::TestCase pet = Pet.first owner = pet.owner - owner.happy_at = 3.days.ago - owner.save + owner.update_attribute(:happy_at, 3.days.ago) previously_owner_updated_at = owner.updated_at pet.name = "I'm a parrot" diff --git a/activeresource/activeresource.gemspec b/activeresource/activeresource.gemspec index a71168722b..c2fd707e9b 100644 --- a/activeresource/activeresource.gemspec +++ b/activeresource/activeresource.gemspec @@ -17,7 +17,6 @@ Gem::Specification.new do |s| s.files = Dir['CHANGELOG', 'README.rdoc', 'examples/**/*', 'lib/**/*'] s.require_path = 'lib' - s.has_rdoc = true s.extra_rdoc_files = %w( README.rdoc ) s.rdoc_options.concat ['--main', 'README.rdoc'] diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index df7f68fecf..eaecb30090 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -16,6 +16,4 @@ Gem::Specification.new do |s| s.files = Dir['CHANGELOG', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' - - s.has_rdoc = true 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 addd4dab95..c27cbc37c5 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -9,7 +9,7 @@ class ERB # A utility method for escaping HTML tag characters. # This method is also aliased as <tt>h</tt>. # - # In your ERb templates, use this method to escape any unsafe content. For example: + # In your ERB templates, use this method to escape any unsafe content. For example: # <%=h @person.name %> # # ==== Example: diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index fb52fc7083..8d6c27e381 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -5,16 +5,9 @@ require 'active_support/testing/deprecation' require 'active_support/testing/declarative' require 'active_support/testing/pending' require 'active_support/testing/isolation' +require 'active_support/testing/mochaing' require 'active_support/core_ext/kernel/reporting' -begin - silence_warnings { require 'mocha' } -rescue LoadError - # Fake Mocha::ExpectationError so we can rescue it in #run. Bleh. - Object.const_set :Mocha, Module.new - Mocha.const_set :ExpectationError, Class.new(StandardError) -end - module ActiveSupport class TestCase < ::Test::Unit::TestCase if defined? MiniTest diff --git a/activesupport/lib/active_support/testing/mochaing.rb b/activesupport/lib/active_support/testing/mochaing.rb new file mode 100644 index 0000000000..4ad75a6681 --- /dev/null +++ b/activesupport/lib/active_support/testing/mochaing.rb @@ -0,0 +1,7 @@ +begin + silence_warnings { require 'mocha' } +rescue LoadError + # Fake Mocha::ExpectationError so we can rescue it in #run. Bleh. + Object.const_set :Mocha, Module.new + Mocha.const_set :ExpectationError, Class.new(StandardError) +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb index 3d119e2fba..feac7bc347 100644 --- a/activesupport/lib/active_support/testing/pending.rb +++ b/activesupport/lib/active_support/testing/pending.rb @@ -11,32 +11,36 @@ module ActiveSupport @@at_exit = false def pending(description = "", &block) - if description.is_a?(Symbol) - is_pending = $tags[description] - return block.call unless is_pending - end + if defined?(::MiniTest) + skip(description.blank? ? nil : description) + else + if description.is_a?(Symbol) + is_pending = $tags[description] + return block.call unless is_pending + end - if block_given? - failed = false + if block_given? + failed = false - begin - block.call - rescue Exception - failed = true - end + begin + block.call + rescue Exception + failed = true + end - flunk("<#{description}> did not fail.") unless failed - end + flunk("<#{description}> did not fail.") unless failed + end - caller[0] =~ (/(.*):(.*):in `(.*)'/) - @@pending_cases << "#{$3} at #{$1}, line #{$2}" - print "P" + caller[0] =~ (/(.*):(.*):in `(.*)'/) + @@pending_cases << "#{$3} at #{$1}, line #{$2}" + print "P" - @@at_exit ||= begin - at_exit do - puts "\nPending Cases:" - @@pending_cases.each do |test_case| - puts test_case + @@at_exit ||= begin + at_exit do + puts "\nPending Cases:" + @@pending_cases.each do |test_case| + puts test_case + end end end end diff --git a/rails.gemspec b/rails.gemspec index 98b5f46554..4ee814c507 100644 --- a/rails.gemspec +++ b/rails.gemspec @@ -17,7 +17,6 @@ Gem::Specification.new do |s| s.bindir = 'bin' s.executables = ['rails'] - s.default_executable = 'rails' s.add_dependency('activesupport', version) s.add_dependency('actionpack', version) diff --git a/railties/CHANGELOG b/railties/CHANGELOG index c1e0a214d2..f159247308 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,9 @@ *Rails 3.1.0 (unreleased)* +* Add using Turn with natural language test case names for test_help.rb when running with minitest (Ruby 1.9.2+) [DHH] + +* Direct logging of Active Record to STDOUT so it's shown inline with the results in the console [DHH] + * Added `config.force_ssl` configuration which loads Rack::SSL middleware and force all requests to be under HTTPS protocol [DHH, Prem Sichanugrist, and Josh Peek] * Added `rails plugin new` command which generates rails plugin with gemspec, tests and dummy application for testing [Piotr Sarnacki] diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile index cfd71ad287..d0b3ee6bfc 100644 --- a/railties/guides/source/action_view_overview.textile +++ b/railties/guides/source/action_view_overview.textile @@ -1295,7 +1295,7 @@ end h5. update_page_tag -Works like update_page but wraps the generated JavaScript in a +script+ tag. Use this to include generated JavaScript in an ERb template. +Works like update_page but wraps the generated JavaScript in a +script+ tag. Use this to include generated JavaScript in an ERB template. h4. PrototypeHelper::JavaScriptGenerator::GeneratorMethods diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index 2c5d9e67e3..f3a10b8b92 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -962,6 +962,26 @@ Client.exists? The above returns +false+ if the +clients+ table is empty and +true+ otherwise. +You can also use +any?+ and +many?+ to check for existence on a model or relation. + +<ruby> +# via a model +Post.any? +Post.many? + +# via a named scope +Post.recent.any? +Post.recent.many? + +# via a relation +Post.where(:published => true).any? +Post.where(:published => true).many? + +# via an association +Post.first.categories.any? +Post.first.categories.many? +</ruby> + h3. Calculations This section uses count as an example method in this preamble, but the options described apply to all sub-sections. diff --git a/railties/guides/source/api_documentation_guidelines.textile b/railties/guides/source/api_documentation_guidelines.textile index 7433507866..e22ffa4c04 100644 --- a/railties/guides/source/api_documentation_guidelines.textile +++ b/railties/guides/source/api_documentation_guidelines.textile @@ -29,7 +29,7 @@ Documentation has to be concise but comprehensive. Explore and document edge cas The proper names of Rails components have a space in between the words, like "Active Support". +ActiveRecord+ is a Ruby module, whereas Active Record is an ORM. All Rails documentation should consistently refer to Rails components by their proper name, and if in your next blog post or presentation you remember this tidbit and take it into account that'd be phenomenal. -Spell names correctly: Arel, Test::Unit, RSpec, HTML, MySQL, JavaScript, ERb. When in doubt, please have a look at some authoritative source like their official documentation. +Spell names correctly: Arel, Test::Unit, RSpec, HTML, MySQL, JavaScript, ERB. When in doubt, please have a look at some authoritative source like their official documentation. Use the article "an" for "SQL", as in "an SQL statement". Also "an SQLite database". diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile index 581fece1ab..f3e8d880df 100644 --- a/railties/guides/source/command_line.textile +++ b/railties/guides/source/command_line.textile @@ -484,7 +484,7 @@ end We take whatever args are supplied, save them to an instance variable, and literally copying from the Rails source, implement a +manifest+ method, which calls +record+ with a block, and we: * Check there's a *public* directory. You bet there is. -* Run the ERb template called "tutorial.erb". +* Run the ERB template called "tutorial.erb". * Save it into "Rails.root/public/tutorial.txt". * Pass in the arguments we saved through the +:assigns+ parameter. diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 0cbbe1f389..7c01f01b24 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -478,8 +478,7 @@ The next line in +config/application.rb+ is: require 'rails/all' </ruby> -h4 +railties/lib/rails/all.rb+ - +h4. +railties/lib/rails/all.rb+ This file is responsible for requiring all the individual parts of Rails like so: @@ -591,7 +590,7 @@ h4. +activesupport/lib/active_support/deprecation/behaviors.rb+ This file defines the behavior of the +ActiveSupport::Deprecation+ module, setting up the +DEFAULT_BEHAVIORS+ hash constant which contains the three defaults to outputting deprecation warnings: +:stderr+, +:log+ and +:notify+. This file begins by requiring +activesupport/notifications+ and +activesupport/core_ext/array/wrap+. -h4 +activesupport/lib/active_support/notifications.rb+ +h4. +activesupport/lib/active_support/notifications.rb+ TODO: document +ActiveSupport::Notifications+. diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index c447fd911a..58b75b9a1d 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -557,6 +557,18 @@ match '*a/foo/*b' => 'test#index' would match +zoo/woo/foo/bar/baz+ with +params[:a]+ equals +"zoo/woo"+, and +params[:b]+ equals +"bar/baz"+. +NOTE: Starting from Rails 3.1, wildcard route will always matching the optional format segment by default. For example if you have this route: + +<ruby> +map '*pages' => 'pages#show' +</ruby> + +NOTE: By requesting +"/foo/bar.json"+, your +params[:pages]+ will be equals to +"foo/bar"+ with the request format of JSON. If you want the old 3.0.x behavior back, you could supply +:format => false+ like this: + +<ruby> +map '*pages' => 'pages#show', :format => false +</ruby> + h4. Redirection You can redirect any path to another path using the +redirect+ helper in your router: diff --git a/railties/guides/source/ruby_on_rails_guides_guidelines.textile b/railties/guides/source/ruby_on_rails_guides_guidelines.textile index 6576758856..8e55780dca 100644 --- a/railties/guides/source/ruby_on_rails_guides_guidelines.textile +++ b/railties/guides/source/ruby_on_rails_guides_guidelines.textile @@ -10,10 +10,10 @@ Guides are written in "Textile":http://www.textism.com/tools/textile/. There's c h3. Prologue -Each guide should start with motivational text at the top. That's the little introduction in the blue area. The prologue should tell the readers what's the guide about, and what will they learn. See for example the "Routing Guide":routing.html. +Each guide should start with motivational text at the top (that's the little introduction in the blue area.) The prologue should tell the reader what the guide is about, and what they will learn. See for example the "Routing Guide":routing.html. h3. Titles - + The title of every guide uses +h2+, guide sections use +h3+, subsections +h4+, etc. Capitalize all words except for internal articles, prepositions, conjunctions, and forms of the verb to be: @@ -23,7 +23,7 @@ h5. Middleware Stack is an Array h5. When are Objects Saved? </plain> -Use same typography as in regular text: +Use the same typography as in regular text: <plain> h6. The +:content_type+ Option @@ -42,13 +42,13 @@ Those guidelines apply also to guides. h3. HTML Generation -To generate all the guides just cd into the +railties+ directory and execute +To generate all the guides, just +cd+ into the +railties+ directory and execute: <plain> bundle exec rake generate_guides </plain> -You'll need the gems erubis, i18n, and RedCloth. +(You may need to run +bundle install+ first to install the required gems.) To process +my_guide.textile+ and nothing else use the +ONLY+ environment variable: @@ -56,13 +56,13 @@ To process +my_guide.textile+ and nothing else use the +ONLY+ environment variab bundle exec rake generate_guides ONLY=my_guide </plain> -Although by default guides that have not been modified are not processed, so +ONLY+ is rarely needed in practice. +By default, guides that have not been modified are not processed, so +ONLY+ is rarely needed in practice. To force process of all the guides, pass +ALL=1+. -It is also recommended that you work with +WARNINGS=1+, this detects duplicate IDs and warns about broken internal links. +It is also recommended that you work with +WARNINGS=1+. This detects duplicate IDs and warns about broken internal links. -If you want to generate guides in languages other than English, you can keep them in a separate directory under +source+ (eg. <tt>source/es</tt>) and use the +LANGUAGE+ environment variable. +If you want to generate guides in languages other than English, you can keep them in a separate directory under +source+ (eg. <tt>source/es</tt>) and use the +LANGUAGE+ environment variable: <plain> rake generate_guides LANGUAGE=es @@ -70,7 +70,7 @@ rake generate_guides LANGUAGE=es h3. HTML Validation -Please do validate the generated HTML with +Please validate the generated HTML with: <plain> rake validate_guides @@ -80,4 +80,5 @@ Particularly, titles get an ID generated from their content and this often leads h3. Changelog +* March 31, 2011: grammar tweaks by "Josiah Ivey":http://twitter.com/josiahivey * October 5, 2010: ported from the docrails wiki and revised by "Xavier Noria":credits.html#fxn diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index d937f30609..2809c6d076 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -79,9 +79,9 @@ steve: Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are separated by a blank space. You can place comments in a fixture file by using the # character in the first column. -h5. ERb'in It Up +h5. ERB'in It Up -ERb allows you to embed ruby code within templates. Both the YAML and CSV fixture formats are pre-processed with ERb when you load fixtures. This allows you to use Ruby to help you generate some sample data. +ERB allows you to embed ruby code within templates. Both the YAML and CSV fixture formats are pre-processed with ERB when you load fixtures. This allows you to use Ruby to help you generate some sample data. <erb> <% earth_size = 20 %> @@ -391,7 +391,7 @@ There are a bunch of different types of assertions you can use. Here's the compl |+assert_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is true.| |+assert_not_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is false.| |+assert_match( regexp, string, [msg] )+ |Ensures that a string matches the regular expression.| -|+assert_no_match( regexp, string, [msg] )+ |Ensures that a string doesn't matches the regular expression.| +|+assert_no_match( regexp, string, [msg] )+ |Ensures that a string doesn't match the regular expression.| |+assert_in_delta( expecting, actual, delta, [msg] )+ |Ensures that the numbers +expecting+ and +actual+ are within +delta+ of each other.| |+assert_throws( symbol, [msg] ) { block }+ |Ensures that the given block throws the symbol.| |+assert_raise( exception1, exception2, ... ) { block }+ |Ensures that the given block raises one of the given exceptions.| diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb index de2f190ad5..2b7faf9715 100644 --- a/railties/lib/rails/commands/console.rb +++ b/railties/lib/rails/commands/console.rb @@ -34,6 +34,10 @@ module Rails exit end end + + if defined?(ActiveRecord) + ActiveRecord::Base.logger = Logger.new(STDERR) + end if options[:sandbox] puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})" diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 4fc23fe277..ee265366ff 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -360,11 +360,11 @@ module Rails def isolate_namespace(mod) engine_name(generate_railtie_name(mod)) - name = engine_name - self.routes.default_scope = {:module => name} + self.routes.default_scope = { :module => ActiveSupport::Inflector.underscore(mod.name) } self.isolated = true unless mod.respond_to?(:_railtie) + name = engine_name _railtie = self mod.singleton_class.instance_eval do define_method(:_railtie) do diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index ab7ed4eb9e..a2eaf7a6fb 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -1,5 +1,6 @@ require 'digest/md5' require 'active_support/secure_random' +require 'active_support/core_ext/string/strip' require 'rails/version' unless defined?(Rails::VERSION) require 'rbconfig' require 'open-uri' @@ -112,30 +113,38 @@ module Rails end def database_gemfile_entry - options[:skip_active_record] ? "" : "gem '#{gem_for_database}'" + entry = options[:skip_active_record] ? "" : "gem '#{gem_for_database}'" + if options[:database] == 'mysql' + if options.dev? || options.edge? + entry += ", :git => 'git://github.com/brianmario/mysql2.git'" + else + entry += "\n# gem 'mysql2', :git => 'git://github.com/brianmario/mysql2.git'" + end + end + entry end def rails_gemfile_entry if options.dev? - <<-GEMFILE -gem 'rails', :path => '#{Rails::Generators::RAILS_DEV_PATH}' -gem 'arel', :git => 'git://github.com/rails/arel.git' -gem "rack", :git => "git://github.com/rack/rack.git" + <<-GEMFILE.strip_heredoc + gem 'rails', :path => '#{Rails::Generators::RAILS_DEV_PATH}' + gem 'arel', :git => 'git://github.com/rails/arel.git' + gem 'rack', :git => 'git://github.com/rack/rack.git' GEMFILE elsif options.edge? - <<-GEMFILE -gem 'rails', :git => 'git://github.com/rails/rails.git' -gem 'arel', :git => 'git://github.com/rails/arel.git' -gem "rack", :git => "git://github.com/rack/rack.git" + <<-GEMFILE.strip_heredoc + gem 'rails', :git => 'git://github.com/rails/rails.git' + gem 'arel', :git => 'git://github.com/rails/arel.git' + gem 'rack', :git => 'git://github.com/rack/rack.git' GEMFILE else - <<-GEMFILE -gem 'rails', '#{Rails::VERSION::STRING}' + <<-GEMFILE.strip_heredoc + gem 'rails', '#{Rails::VERSION::STRING}' -# Bundle edge Rails instead: -# gem 'rails', :git => 'git://github.com/rails/rails.git' -# gem 'arel', :git => 'git://github.com/rails/arel.git' -# gem "rack", :git => "git://github.com/rack/rack.git" + # Bundle edge Rails instead: + # gem 'rails', :git => 'git://github.com/rails/rails.git' + # gem 'arel', :git => 'git://github.com/rails/arel.git' + # gem 'rack', :git => 'git://github.com/rack/rack.git' GEMFILE end end diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb index 591fd6f6bd..302206bbcd 100644 --- a/railties/lib/rails/source_annotation_extractor.rb +++ b/railties/lib/rails/source_annotation_extractor.rb @@ -11,7 +11,7 @@ # # Annotations are looked for in comments and modulus whitespace they have to # start with the tag optionally followed by a colon. Everything up to the end -# of the line (or closing ERb comment tag) is considered to be their text. +# of the line (or closing ERB comment tag) is considered to be their text. class SourceAnnotationExtractor class Annotation < Struct.new(:line, :tag, :text) @@ -99,4 +99,4 @@ class SourceAnnotationExtractor puts end end -end
\ No newline at end of file +end diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index 00029e627e..b9f7bdc2eb 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -13,6 +13,14 @@ if defined?(Test::Unit::Util::BacktraceFilter) && ENV['BACKTRACE'].nil? Test::Unit::Util::BacktraceFilter.module_eval { include Rails::BacktraceFilterForTestUnit } end +if defined?(MiniTest) + require 'turn' + + if MiniTest::Unit.respond_to?(:use_natural_language_case_names=) + MiniTest::Unit.use_natural_language_case_names = true + end +end + if defined?(ActiveRecord) require 'active_record/test_case' diff --git a/railties/railties.gemspec b/railties/railties.gemspec index c51fe856be..b1eda71c7f 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -17,11 +17,11 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.rdoc_options << '--exclude' << '.' - s.has_rdoc = false s.add_dependency('rake', '>= 0.8.7') s.add_dependency('thor', '~> 0.14.4') s.add_dependency('rack-ssl', '~> 1.3.2') + s.add_dependency('turn', '~> 0.8.2') s.add_dependency('activesupport', version) s.add_dependency('actionpack', version) end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 0ce00db3c4..20797a2b0c 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -559,6 +559,45 @@ module RailtiesTest assert_match /name="post\[title\]"/, last_response.body end + test "isolated engine should set correct route module prefix for nested namespace" do + @plugin.write "lib/bukkits.rb", <<-RUBY + module Bukkits + module Awesome + class Engine < ::Rails::Engine + isolate_namespace Bukkits::Awesome + end + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + AppTemplate::Application.routes.draw do + mount Bukkits::Awesome::Engine => "/bukkits", :as => "bukkits" + end + RUBY + + @plugin.write "config/routes.rb", <<-RUBY + Bukkits::Awesome::Engine.routes.draw do + match "/foo" => "foo#index" + end + RUBY + + @plugin.write "app/controllers/bukkits/awesome/foo_controller.rb", <<-RUBY + class Bukkits::Awesome::FooController < ActionController::Base + def index + render :text => "ok" + end + end + RUBY + + add_to_config("config.action_dispatch.show_exceptions = false") + + boot_rails + + get("/bukkits/foo") + assert_equal "ok", last_response.body + end + test "loading seed data" do @plugin.write "db/seeds.rb", <<-RUBY Bukkits::Engine.config.bukkits_seeds_loaded = true |