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
  ...
This commit is contained in:
Ryan Bigg 2011-04-05 21:22:38 +10:00
commit 92e6255b58
86 changed files with 994 additions and 251 deletions

@ -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

@ -17,8 +17,6 @@
s.require_path = 'lib'
s.requirements << 'none'
s.has_rdoc = true
s.add_dependency('actionpack', version)
s.add_dependency('mail', '~> 2.2.15')
end

@ -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]

@ -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 %>

@ -17,8 +17,6 @@
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 @@
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

@ -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 @@ class Base
include ActiveSupport::Configurable
extend ActiveSupport::DescendantsTracker
undef_method :not_implemented
class << self
attr_reader :abstract
alias_method :abstract?, :abstract
@ -128,20 +130,23 @@ def action_methods
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 @@ def process_action(method_name, *args)
# 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

@ -14,7 +14,7 @@ module Callbacks
# 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

@ -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
#

@ -8,9 +8,7 @@ module HttpAuthentication
# === 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 HttpAuthentication
# 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 HttpAuthentication
# 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 Basic
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 @@ def opaque(secret_key)
#
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
module Token
extend self
module ControllerMethods
@ -458,6 +459,5 @@ def authentication_request(controller, realm)
controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
end
end
end
end

@ -1,21 +1,21 @@
module ActionController
module ImplicitRender
def send_action(*)
ret = super
default_render unless response_body
ret
end
def default_render
render
end
def method_for_action(action_name)
super || begin
if template_exists?(action_name.to_s, _prefixes)
"default_render"
end
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(*args)
render(*args)
end
def action_method?(action_name)
super || template_exists?(action_name.to_s, _prefixes)
end
end
end

@ -189,7 +189,7 @@ def respond_to(*mimes, &block)
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 @@ def respond_to(*mimes, &block)
# 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 @@ def collect_mimes_from_class_level #: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)

@ -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 @@ def to_html
# 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 @@ def api_behavior(error)
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 @@ def resource_location
# 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 @@ def display(resource, given_options={})
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?

@ -104,10 +104,16 @@ def normalize_path(path)
@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)"

@ -46,7 +46,7 @@ def assert_recognizes(expected_options, path, extras={}, message=nil)
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 @@ def assert_generates(expected_path, options, defaults={}, extras = {}, message=n
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

@ -8,13 +8,13 @@
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:

@ -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:

@ -14,7 +14,7 @@ module CaptureHelper
# 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

@ -584,7 +584,7 @@ def update_page(&block)
# 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.

@ -186,7 +186,7 @@ def url_for(options = {})
# 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>

@ -55,7 +55,7 @@ def add_postamble(src)
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 = '-'

@ -6,6 +6,8 @@ class DummyController < ActionController::Base
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 @@ def display
def show
render :text => 'Only for loooooong credentials'
end
def search
render :text => 'All inline'
end
private
@ -104,6 +110,16 @@ def authenticate_long_credentials
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

@ -558,6 +558,15 @@ def using_resource_with_status_and_location
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 @@ def test_using_resource_with_status_and_location
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

@ -3,8 +3,9 @@
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 @@ class RenderImplicitActionTest < Rack::TestCase
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

@ -25,6 +25,10 @@ def add_route(*args)
def conditions
routes.map { |x| x[1] }
end
def requirements
routes.map { |x| x[2] }
end
end
def test_initialize
@ -50,8 +54,34 @@ def test_map_more_slashes
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

@ -0,0 +1 @@
<content>I should not be displayed</content>

@ -0,0 +1 @@
<customer-name><%= @customer.name %></customer-name>

@ -2720,11 +2720,11 @@ def test_time_tag_with_time
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

@ -17,8 +17,6 @@
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')

@ -294,8 +294,8 @@ def generate_message(attribute, type = :invalid, options = {})
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)

@ -4,7 +4,7 @@
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 @@ def initialize(klass, namespace = nil)
@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 @@ def human(options={})
@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 @@ def human(options={})
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 @@ def _singularize(str)
# 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.

@ -44,7 +44,7 @@ def lookup_ancestors
# 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}"

@ -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

@ -17,7 +17,6 @@
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']

@ -34,8 +34,7 @@ def delete(method = options[:dependent])
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

@ -22,7 +22,7 @@ def initialize(active_record)
end
def aliased_table
Arel::Nodes::TableAlias.new aliased_table_name, table
Arel::Nodes::TableAlias.new table, aliased_table_name
end
def ==(other)

@ -17,6 +17,11 @@ def primary_key
@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 @@ def get_primary_key(base_name) #:nodoc:
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 @@ def get_primary_key(base_name) #:nodoc:
# 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

@ -32,6 +32,7 @@ def write_attribute(attr_name, value)
@attributes[attr_name] = value
end
end
alias_method :raw_write_attribute, :write_attribute
private
# Handle *= for method_missing.

@ -437,9 +437,10 @@ class Base
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

@ -151,6 +151,12 @@ def connection
@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 @@ def establish_connection(name, spec)
@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 @@ def retrieve_connection_pool(klass)
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

@ -116,7 +116,11 @@ def remove_connection(klass = self)
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

@ -222,7 +222,7 @@ def rollback_db_transaction #:nodoc:
# SCHEMA STATEMENTS ========================================
def tables(name = nil) #:nodoc:
def tables(name = 'SCHEMA') #:nodoc:
sql = <<-SQL
SELECT name
FROM sqlite_master
@ -350,7 +350,7 @@ def select(sql, name = nil, binds = []) #:nodoc:
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

@ -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

@ -23,6 +23,9 @@ def sql(event)
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

@ -104,17 +104,33 @@ def becomes(klass)
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 @@ def increment(attribute, by = 1)
# 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 @@ def decrement(attribute, by = 1)
# 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 @@ def toggle(attribute)
# 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.

@ -12,7 +12,7 @@ class Relation
# 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

@ -83,7 +83,7 @@ def find_in_batches(options = {})
private
def batch_order
"#{table_name}.#{primary_key} ASC"
"#{quoted_table_name}.#{quoted_primary_key} ASC"
end
end
end

@ -196,24 +196,22 @@ def operation_over_aggregate_column(column, operation, distinct)
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 @@ def select_for_count
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

@ -183,7 +183,9 @@ def all(*args)
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 @@ def exists?(id = nil)
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 @@ def find_with_associations
[]
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)

@ -62,6 +62,10 @@ def order(*args)
relation
end
def reorder(*args)
except(:order).order(args)
end
def joins(*args)
return self if args.compact.blank?

@ -83,4 +83,14 @@ def test_find_in_batches_shouldnt_excute_query_unless_needed
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

@ -65,7 +65,7 @@ def test_should_group_by_field
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,6 +109,35 @@ def test_should_limit_calculation_with_offset
assert_equal [2, 6], c.keys.compact
end
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).count }
assert_equal 1, queries.length
assert_match(/LIMIT/, queries.first)
end
def test_offset_is_kept
return if current_adapter?(:OracleAdapter)
queries = assert_sql { Account.offset(1).count }
assert_equal 1, queries.length
assert_match(/OFFSET/, queries.first)
end
def test_limit_with_offset_is_kept
return if current_adapter?(:OracleAdapter)
@ -118,20 +147,6 @@ def test_limit_with_offset_is_kept
assert_match(/OFFSET/, queries.first)
end
def test_offset_without_limit_removes_offset
queries = assert_sql { Account.offset(1).count }
assert_equal 1, queries.length
assert_no_match(/LIMIT/, queries.first)
assert_no_match(/OFFSET/, queries.first)
end
def test_limit_without_offset_removes_limit
queries = assert_sql { Account.limit(1).count }
assert_equal 1, queries.length
assert_no_match(/LIMIT/, queries.first)
assert_no_match(/OFFSET/, queries.first)
end
def test_no_limit_no_offset
queries = assert_sql { Account.count }
assert_equal 1, queries.length

@ -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

@ -1,25 +1,82 @@
require "cases/helper"
class ConnectionManagementTest < ActiveRecord::TestCase
def setup
@env = {}
@app = stub('App')
@management = ActiveRecord::ConnectionAdapters::ConnectionManagement.new(@app)
module ActiveRecord
module ConnectionAdapters
class ConnectionManagementTest < ActiveRecord::TestCase
class App
attr_reader :calls
def initialize
@calls = []
end
@connections_cleared = false
ActiveRecord::Base.stubs(:clear_active_connections!).with { @connections_cleared = true }
end
def call(env)
@calls << env
[200, {}, ['hi mom']]
end
end
test "clears active connections after each call" do
@app.expects(:call).with(@env)
@management.call(@env)
assert @connections_cleared
end
def setup
@env = {}
@app = App.new
@management = ConnectionManagement.new(@app)
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
# 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
@management.call(@env)
assert ActiveRecord::Base.connection_handler.active_connections?
end
end
end
end

@ -18,6 +18,14 @@ def setup
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']

@ -487,8 +487,7 @@ def test_previous_changes
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']

@ -74,6 +74,11 @@ def test_exists_with_scoped_include
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 @@ def test_first_bang_missing
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 @@ def test_last_bang_missing
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

@ -22,6 +22,33 @@ def set_logger(logger)
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

@ -64,7 +64,7 @@ def test_subclasses_inherit_scopes
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 @@ def test_any_should_not_fire_query_if_scope_loaded
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 @@ def test_many_should_return_true_if_more_than_one
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

@ -327,6 +327,68 @@ def test_destroy_record_with_associations
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 @@ def test_update_column
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 @@ def test_update_column_should_not_modify_updated_at
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 @@ def test_update_column_with_one_changed_and_one_updated
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

@ -136,4 +136,13 @@ def test_primary_key_returns_nil_if_it_does_not_exist
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

@ -429,9 +429,9 @@ def test_scope_overwrites_default
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

@ -151,6 +151,12 @@ def test_finding_with_order_concatenated
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 @@ def test_size
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")

@ -113,8 +113,7 @@ def test_touching_a_record_with_a_belongs_to_that_uses_a_counter_cache_should_up
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"

@ -17,7 +17,6 @@
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']

@ -16,6 +16,4 @@
s.files = Dir['CHANGELOG', 'README.rdoc', 'lib/**/*']
s.require_path = 'lib'
s.has_rdoc = true
end

@ -9,7 +9,7 @@ module Util
# 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:

@ -5,16 +5,9 @@
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

@ -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

@ -11,32 +11,36 @@ module Pending
@@at_exit = false
def pending(description = "", &block)
if description.is_a?(Symbol)
is_pending = $tags[description]
return block.call unless is_pending
end
if block_given?
failed = false
begin
block.call
rescue Exception
failed = true
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
flunk("<#{description}> did not fail.") unless failed
end
if block_given?
failed = false
caller[0] =~ (/(.*):(.*):in `(.*)'/)
@@pending_cases << "#{$3} at #{$1}, line #{$2}"
print "P"
begin
block.call
rescue Exception
failed = true
end
@@at_exit ||= begin
at_exit do
puts "\nPending Cases:"
@@pending_cases.each do |test_case|
puts test_case
flunk("<#{description}> did not fail.") unless failed
end
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
end
end
end
end

@ -17,7 +17,6 @@
s.bindir = 'bin'
s.executables = ['rails']
s.default_executable = 'rails'
s.add_dependency('activesupport', version)
s.add_dependency('actionpack', version)

@ -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]

@ -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

@ -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.

@ -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".

@ -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.

@ -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+.

@ -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:

@ -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

@ -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.|

@ -34,6 +34,10 @@ def start
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})"

@ -360,11 +360,11 @@ def endpoint(endpoint = nil)
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

@ -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 @@ def set_default_accessors!
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

@ -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 @@ def display(results, options={})
puts
end
end
end
end

@ -13,6 +13,14 @@
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'

@ -17,11 +17,11 @@
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

@ -559,6 +559,45 @@ def new
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