Merge remote branch 'rails/master'

This commit is contained in:
Xavier Noria 2010-07-22 01:29:18 +02:00
commit 919eb200a9
146 changed files with 1281 additions and 1240 deletions

58
README.rdoc Normal file

@ -0,0 +1,58 @@
== Welcome to Rails
Rails is a web-application framework that includes everything needed to create
database-backed web applications according to the Model-View-Control pattern.
This pattern splits the view (also called the presentation) into "dumb"
templates that are primarily responsible for inserting pre-built data in between
HTML tags. The model contains the "smart" domain objects (such as Account,
Product, Person, Post) that holds all the business logic and knows how to
persist themselves to a database. The controller handles the incoming requests
(such as Save New Account, Update Product, Show Post) by manipulating the model
and directing data to the view.
In Rails, the model is handled by what's called an object-relational mapping
layer entitled Active Record. This layer allows you to present the data from
database rows as objects and embellish these data objects with business logic
methods. You can read more about Active Record in
link:files/vendor/rails/activerecord/README.html.
The controller and view are handled by the Action Pack, which handles both
layers by its two parts: Action View and Action Controller. These two layers
are bundled in a single package due to their heavy interdependence. This is
unlike the relationship between the Active Record and Action Pack that is much
more separate. Each of these packages can be used independently outside of
Rails. You can read more about Action Pack in
link:files/vendor/rails/actionpack/README.html.
== Getting Started
1. Install Rails at the command prompt if you haven't yet:
<tt>gem install rails</tt>
2. At the command prompt, create a new Rails application:
<tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
3. Change directory to <tt>myapp</tt> and start the web server:
<tt>cd myapp; rails server</tt> (run with --help for options)
4. Go to http://localhost:3000/ and you'll see:
"Welcome aboard: You're riding Ruby on Rails!"
5. Follow the guidelines to start developing your application. You can find
the following resources handy:
* The README file created within your application
* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
== Contributing
Check out the contributing guide at http://edgeguides.rubyonrails.org/contributing_to_rails.html
== License
Ruby on Rails is released under the MIT license.

@ -69,7 +69,7 @@ Rake::RDocTask.new do |rdoc|
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.options << '-A cattr_accessor=object'
rdoc.options << '--charset' << 'utf-8'
rdoc.options << '--main' << 'railties/README'
rdoc.options << '--main' << 'README.rdoc'
# Workaround: RDoc assumes that rdoc.template can be required, and that
# rdoc.template.upcase is a constant living in RDoc::Generator::HTML
@ -83,38 +83,38 @@ Rake::RDocTask.new do |rdoc|
rdoc.rdoc_files.include('railties/CHANGELOG')
rdoc.rdoc_files.include('railties/MIT-LICENSE')
rdoc.rdoc_files.include('railties/README')
rdoc.rdoc_files.include('railties/README.rdoc')
rdoc.rdoc_files.include('railties/lib/**/*.rb')
rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/*')
rdoc.rdoc_files.include('activerecord/README')
rdoc.rdoc_files.include('activerecord/README.rdoc')
rdoc.rdoc_files.include('activerecord/CHANGELOG')
rdoc.rdoc_files.include('activerecord/lib/active_record/**/*.rb')
rdoc.rdoc_files.exclude('activerecord/lib/active_record/vendor/*')
rdoc.rdoc_files.include('activeresource/README')
rdoc.rdoc_files.include('activeresource/README.rdoc')
rdoc.rdoc_files.include('activeresource/CHANGELOG')
rdoc.rdoc_files.include('activeresource/lib/active_resource.rb')
rdoc.rdoc_files.include('activeresource/lib/active_resource/*')
rdoc.rdoc_files.include('actionpack/README')
rdoc.rdoc_files.include('actionpack/README.rdoc')
rdoc.rdoc_files.include('actionpack/CHANGELOG')
rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb')
rdoc.rdoc_files.include('actionpack/lib/action_view/**/*.rb')
rdoc.rdoc_files.exclude('actionpack/lib/action_controller/vendor/*')
rdoc.rdoc_files.include('actionmailer/README')
rdoc.rdoc_files.include('actionmailer/README.rdoc')
rdoc.rdoc_files.include('actionmailer/CHANGELOG')
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/base.rb')
rdoc.rdoc_files.exclude('actionmailer/lib/action_mailer/vendor/*')
rdoc.rdoc_files.include('activesupport/README')
rdoc.rdoc_files.include('activesupport/README.rdoc')
rdoc.rdoc_files.include('activesupport/CHANGELOG')
rdoc.rdoc_files.include('activesupport/lib/active_support/**/*.rb')
rdoc.rdoc_files.exclude('activesupport/lib/active_support/vendor/*')
rdoc.rdoc_files.include('activemodel/README')
rdoc.rdoc_files.include('activemodel/README.rdoc')
rdoc.rdoc_files.include('activemodel/CHANGELOG')
rdoc.rdoc_files.include('activemodel/lib/active_model/**/*.rb')
end

@ -126,36 +126,20 @@ or is accessible as a GEM.
Additionally, Action Mailer requires the Mail gem, http://github.com/mikel/mail
== Bundled software
* Text::Format 0.63 by Austin Ziegler released under OpenSource
Read more on http://www.halostatue.ca/ruby/Text__Format.html
== Download
The latest version of Action Mailer can be found at
The latest version of Action Mailer can be installed with Rubygems:
* http://rubyforge.org/project/showfiles.php?group_id=361
* gem install actionmailer
Documentation can be found at
* http://actionmailer.rubyonrails.org
== Installation
You can install Action Mailer with the following command.
% [sudo] ruby install.rb
from its distribution directory.
* http://api.rubyonrails.org
== License
Action Mailer is released under the MIT license.
== Support
The Action Mailer homepage is http://www.rubyonrails.org. You can find

@ -32,7 +32,7 @@ Rake::RDocTask.new { |rdoc|
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
rdoc.options << '--charset' << 'utf-8'
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
rdoc.rdoc_files.include('README', 'CHANGELOG')
rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG')
rdoc.rdoc_files.include('lib/action_mailer.rb')
rdoc.rdoc_files.include('lib/action_mailer/*.rb')
rdoc.rdoc_files.include('lib/action_mailer/delivery_method/*.rb')

@ -13,7 +13,7 @@
s.homepage = 'http://www.rubyonrails.org'
s.rubyforge_project = 'actionmailer'
s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*']
s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
s.require_path = 'lib'
s.requirements << 'none'

@ -1,30 +0,0 @@
require 'rbconfig'
require 'find'
require 'ftools'
include Config
# this was adapted from rdoc's install.rb by way of Log4r
$sitedir = CONFIG["sitelibdir"]
unless $sitedir
version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
$libdir = File.join(CONFIG["libdir"], "ruby", version)
$sitedir = $:.find {|x| x =~ /site_ruby/ }
if !$sitedir
$sitedir = File.join($libdir, "site_ruby")
elsif $sitedir !~ Regexp.quote(version)
$sitedir = File.join($sitedir, version)
end
end
# the actual gruntwork
Dir.chdir("lib")
Find.find("action_mailer", "action_mailer.rb") { |f|
if f[-3..-1] == ".rb"
File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
else
File::makedirs(File.join($sitedir, *f.split(/\//)))
end
}

@ -1,5 +1,7 @@
*Rails 3.0.0 [Release Candidate] (unreleased)*
* Allow stylesheet/javascript extensions to be changed through railties. [Josh Kalderimis]
* link_to, button_to, and tag/tag_options now rely on html_escape instead of escape_once. [fxn]
* url_for returns always unescaped strings, and the :escape option is gone. [fxn]

@ -19,15 +19,6 @@ the HTML. To avoid cluttering the templates with code, a bunch of helper
classes provide common behavior for forms, dates, and strings. And it's easy
to add specific helpers to keep the separation as the application evolves.
Note: Some of the features, such as scaffolding and form building, are tied to
ActiveRecord[http://activerecord.rubyonrails.org] (an object-relational
mapping package), but that doesn't mean that Action Pack depends on Active
Record. Action Pack is an independent package that can be used with any sort
of backend (Instiki[http://www.instiki.org], which is based on an older version
of Action Pack, used Madeleine for example). Read more about the role Action
Pack can play when used together with Active Record on
http://www.rubyonrails.org.
A short rundown of the major features:
* Actions grouped in controller as methods instead of separate command objects
@ -366,22 +357,13 @@ an URL such as /weblog/5 (where 5 is the id of the post).
== Download
The latest version of Action Pack can be found at
The latest version of Action Pack can be installed with Rubygems:
* http://rubyforge.org/project/showfiles.php?group_id=249
* gem install actionpack
Documentation can be found at
Documentation can be found at
* http://api.rubyonrails.com
== Installation
You can install Action Pack with the following command.
% [sudo] ruby install.rb
from its distribution directory.
* http://api.rubyonrails.org
== License

@ -47,7 +47,7 @@ Rake::RDocTask.new { |rdoc|
if ENV['DOC_FILES']
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
else
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
rdoc.rdoc_files.include('README.rdoc', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
rdoc.rdoc_files.include(Dir['lib/**/*.rb'] -
Dir['lib/*/vendor/**/*.rb'])
rdoc.rdoc_files.exclude('lib/actionpack.rb')

@ -13,7 +13,7 @@
s.homepage = 'http://www.rubyonrails.org'
s.rubyforge_project = 'actionpack'
s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*']
s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
s.require_path = 'lib'
s.requirements << 'none'

@ -1,30 +0,0 @@
require 'rbconfig'
require 'find'
require 'ftools'
include Config
# this was adapted from rdoc's install.rb by way of Log4r
$sitedir = CONFIG["sitelibdir"]
unless $sitedir
version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
$libdir = File.join(CONFIG["libdir"], "ruby", version)
$sitedir = $:.find {|x| x =~ /site_ruby/ }
if !$sitedir
$sitedir = File.join($libdir, "site_ruby")
elsif $sitedir !~ Regexp.quote(version)
$sitedir = File.join($sitedir, version)
end
end
# the actual gruntwork
Dir.chdir("lib")
Find.find("action_controller", "action_controller.rb", "action_view", "action_view.rb") { |f|
if f[-3..-1] == ".rb"
File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
else
File::makedirs(File.join($sitedir, *f.split(/\//)))
end
}

@ -1,6 +1,7 @@
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require 'action_pack'
require 'active_support/ruby/shim'
require 'active_support/dependencies/autoload'
require 'active_support/core_ext/class/attribute'

@ -6,7 +6,6 @@ module ActionController
autoload :Base
autoload :Caching
autoload :PolymorphicRoutes
autoload :Metal
autoload :Middleware

@ -28,7 +28,6 @@ def self.without_modules(*modules)
SessionManagement,
Caching,
MimeResponds,
PolymorphicRoutes,
ImplicitRender,
Cookies,
@ -69,4 +68,4 @@ def self.inherited(klass)
end
end
require "action_controller/deprecated/base"
require "action_controller/deprecated/base"

@ -1,3 +1,4 @@
require 'benchmark'
require 'abstract_controller/logger'
module ActionController

@ -3,6 +3,15 @@ module Rescue
extend ActiveSupport::Concern
include ActiveSupport::Rescuable
def rescue_with_handler(exception)
if (exception.respond_to?(:original_exception) &&
(orig_exception = exception.original_exception) &&
handler_for_rescue(orig_exception))
exception = orig_exception
end
super(exception)
end
private
def process_action(*args)
super

@ -1,3 +1,5 @@
require 'active_support/core_ext/file/path'
module ActionController #:nodoc:
# Methods for sending arbitrary data and for streaming files to the browser,
# instead of rendering.

@ -1,183 +0,0 @@
module ActionController
# Polymorphic URL helpers are methods for smart resolution to a named route call when
# given an Active Record model instance. They are to be used in combination with
# ActionController::Resources.
#
# These methods are useful when you want to generate correct URL or path to a RESTful
# resource without having to know the exact type of the record in question.
#
# Nested resources and/or namespaces are also supported, as illustrated in the example:
#
# polymorphic_url([:admin, @article, @comment])
#
# results in:
#
# admin_article_comment_url(@article, @comment)
#
# == Usage within the framework
#
# Polymorphic URL helpers are used in a number of places throughout the Rails framework:
#
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
# <tt>url_for(@article)</tt>;
# * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
# <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
# action;
# * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
# <tt>redirect_to(post)</tt> in your controllers;
# * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
# for feed entries.
#
# == Prefixed polymorphic helpers
#
# In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
# number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
# in options. Those are:
#
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
#
# Example usage:
#
# edit_polymorphic_path(@post) # => "/posts/1/edit"
# polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf"
module PolymorphicRoutes
# Constructs a call to a named RESTful route for the given record and returns the
# resulting URL string. For example:
#
# # calls post_url(post)
# polymorphic_url(post) # => "http://example.com/posts/1"
# polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
# polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
# polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
# polymorphic_url(Comment) # => "http://example.com/comments"
#
# ==== Options
#
# * <tt>:action</tt> - Specifies the action prefix for the named route:
# <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
# Default is <tt>:url</tt>.
#
# ==== Examples
#
# # an Article record
# polymorphic_url(record) # same as article_url(record)
#
# # a Comment record
# polymorphic_url(record) # same as comment_url(record)
#
# # it recognizes new records and maps to the collection
# record = Comment.new
# polymorphic_url(record) # same as comments_url()
#
# # the class of a record will also map to the collection
# polymorphic_url(Comment) # same as comments_url()
#
def polymorphic_url(record_or_hash_or_array, options = {})
if record_or_hash_or_array.kind_of?(Array)
record_or_hash_or_array = record_or_hash_or_array.compact
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
end
record = extract_record(record_or_hash_or_array)
record = record.to_model if record.respond_to?(:to_model)
args = case record_or_hash_or_array
when Hash; [ record_or_hash_or_array ]
when Array; record_or_hash_or_array.dup
else [ record_or_hash_or_array ]
end
inflection = if options[:action].to_s == "new"
args.pop
:singular
elsif (record.respond_to?(:persisted?) && !record.persisted?)
args.pop
:plural
elsif record.is_a?(Class)
args.pop
:plural
else
:singular
end
args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
url_options = options.except(:action, :routing_type)
unless url_options.empty?
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
end
__send__(named_route, *args)
end
# Returns the path component of a URL for the given record. It uses
# <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
def polymorphic_path(record_or_hash_or_array, options = {})
polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
end
%w(edit new).each do |action|
module_eval <<-EOT, __FILE__, __LINE__ + 1
def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
polymorphic_url( # polymorphic_url(
record_or_hash, # record_or_hash,
options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
end # end
#
def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
polymorphic_url( # polymorphic_url(
record_or_hash, # record_or_hash,
options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
end # end
EOT
end
private
def action_prefix(options)
options[:action] ? "#{options[:action]}_" : ''
end
def routing_type(options)
options[:routing_type] || :url
end
def build_named_route_call(records, inflection, options = {})
unless records.is_a?(Array)
record = extract_record(records)
route = ''
else
record = records.pop
route = records.inject("") do |string, parent|
if parent.is_a?(Symbol) || parent.is_a?(String)
string << "#{parent}_"
else
string << RecordIdentifier.__send__("plural_class_name", parent).singularize
string << "_"
end
end
end
if record.is_a?(Symbol) || record.is_a?(String)
route << "#{record}_"
else
route << RecordIdentifier.__send__("plural_class_name", record)
route = route.singularize if inflection == :singular
route << "_"
route << "index_" if RecordIdentifier.uncountable?(record) && inflection == :plural
end
action_prefix(options) + route + routing_type(options).to_s
end
def extract_record(record_or_hash_or_array)
case record_or_hash_or_array
when Array; record_or_hash_or_array.last
when Hash; record_or_hash_or_array[:id]
else record_or_hash_or_array
end
end
end
end

@ -46,7 +46,7 @@ module RecordIdentifier
# dom_class(post, :edit) # => "edit_post"
# dom_class(Person, :edit) # => "edit_person"
def dom_class(record_or_class, prefix = nil)
singular = singular_class_name(record_or_class)
singular = ActiveModel::Naming.singular(record_or_class)
prefix ? "#{prefix}#{JOIN}#{singular}" : singular
end
@ -67,6 +67,8 @@ def dom_id(record, prefix = nil)
end
end
protected
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
# This can be overwritten to customize the default generated string representation if desired.
# If you need to read back a key from a dom_id in order to query for the underlying database record,
@ -85,34 +87,5 @@ def record_key_for_dom_id(record)
def sanitize_dom_id(candidate_id)
candidate_id # TODO implement conversion to valid DOM id values
end
# Returns the plural class name of a record or class. Examples:
#
# plural_class_name(post) # => "posts"
# plural_class_name(Highrise::Person) # => "highrise_people"
def plural_class_name(record_or_class)
model_name_from_record_or_class(record_or_class).plural
end
# Returns the singular class name of a record or class. Examples:
#
# singular_class_name(post) # => "post"
# singular_class_name(Highrise::Person) # => "highrise_person"
def singular_class_name(record_or_class)
model_name_from_record_or_class(record_or_class).singular
end
# Identifies whether the class name of a record or class is uncountable. Examples:
#
# uncountable?(Sheep) # => true
# uncountable?(Post) => false
def uncountable?(record_or_class)
plural_class_name(record_or_class) == singular_class_name(record_or_class)
end
private
def model_name_from_record_or_class(record_or_class)
(record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
end
end
end

@ -24,9 +24,14 @@
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
require 'active_support'
require 'active_support/dependencies/autoload'
require 'action_pack'
require 'active_model'
require 'rack'
module Rack
@ -63,6 +68,7 @@ module Http
autoload :Headers
autoload :MimeNegotiation
autoload :Parameters
autoload :ParameterFilter
autoload :FilterParameters
autoload :Upload
autoload :UploadedFile, 'action_dispatch/http/upload'

@ -26,88 +26,32 @@ module Http
module FilterParameters
extend ActiveSupport::Concern
@@compiled_parameter_filter_for = {}
@@parameter_filter_for = {}
# Return a hash of parameters with all sensitive data replaced.
def filtered_parameters
@filtered_parameters ||= if filtering_parameters?
process_parameter_filter(parameters)
else
parameters.dup
end
@filtered_parameters ||= parameter_filter.filter(parameters)
end
alias :fitered_params :filtered_parameters
# Return a hash of request.env with all sensitive data replaced.
def filtered_env
filtered_env = @env.dup
filtered_env.each do |key, value|
if (key =~ /RAW_POST_DATA/i)
filtered_env[key] = '[FILTERED]'
elsif value.is_a?(Hash)
filtered_env[key] = process_parameter_filter(value)
end
end
filtered_env
@filtered_env ||= env_filter.filter(@env)
end
protected
def filtering_parameters? #:nodoc:
@env["action_dispatch.parameter_filter"].present?
def parameter_filter
parameter_filter_for(@env["action_dispatch.parameter_filter"])
end
def process_parameter_filter(params) #:nodoc:
compiled_parameter_filter_for(@env["action_dispatch.parameter_filter"]).call(params)
def env_filter
parameter_filter_for(Array.wrap(@env["action_dispatch.parameter_filter"]) << /RAW_POST_DATA/)
end
def compile_parameter_filter(filters) #:nodoc:
strings, regexps, blocks = [], [], []
filters.each do |item|
case item
when NilClass
when Proc
blocks << item
when Regexp
regexps << item
else
strings << item.to_s
end
end
regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
[regexps, blocks]
end
def compiled_parameter_filter_for(filters) #:nodoc:
@@compiled_parameter_filter_for[filters] ||= begin
regexps, blocks = compile_parameter_filter(filters)
lambda do |original_params|
filtered_params = {}
original_params.each do |key, value|
if regexps.find { |r| key =~ r }
value = '[FILTERED]'
elsif value.is_a?(Hash)
value = process_parameter_filter(value)
elsif value.is_a?(Array)
value = value.map { |v| v.is_a?(Hash) ? process_parameter_filter(v) : v }
elsif blocks.present?
key = key.dup
value = value.dup if value.duplicable?
blocks.each { |b| b.call(key, value) }
end
filtered_params[key] = value
end
filtered_params
end
end
def parameter_filter_for(filters)
@@parameter_filter_for[filters] ||= ParameterFilter.new(filters)
end
end
end
end
end

@ -0,0 +1,72 @@
module ActionDispatch
module Http
class ParameterFilter
def initialize(filters)
@filters = filters
end
def filter(params)
if enabled?
compiled_filter.call(params)
else
params.dup
end
end
private
def enabled?
@filters.present?
end
def compiled_filter
@compiled_filter ||= begin
regexps, blocks = compile_filter
lambda do |original_params|
filtered_params = {}
original_params.each do |key, value|
if regexps.find { |r| key =~ r }
value = '[FILTERED]'
elsif value.is_a?(Hash)
value = filter(value)
elsif value.is_a?(Array)
value = value.map { |v| v.is_a?(Hash) ? filter(v) : v }
elsif blocks.present?
key = key.dup
value = value.dup if value.duplicable?
blocks.each { |b| b.call(key, value) }
end
filtered_params[key] = value
end
filtered_params
end
end
end
def compile_filter
strings, regexps, blocks = [], [], []
@filters.each do |item|
case item
when NilClass
when Proc
blocks << item
when Regexp
regexps << item
else
strings << item.to_s
end
end
regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
[regexps, blocks]
end
end
end
end

@ -24,9 +24,9 @@ def initialize(by, env, default_options)
def [](key)
if key == :id
load_session_id! unless super(:id) || has_session_id?
load_session_id! unless key?(:id) || has_session_id?
end
super(key)
super
end
private
@ -191,8 +191,11 @@ def set_cookie(request, options)
def load_session(env)
stale_session_check! do
sid = current_session_id(env)
sid, session = get_session(env, sid)
if sid = current_session_id(env)
sid, session = get_session(env, sid)
else
sid, session = generate_sid, {}
end
[sid, session]
end
end

@ -25,7 +25,6 @@ def initialize(app, options = {})
private
def get_session(env, sid)
sid ||= generate_sid
begin
session = @pool.get(sid) || {}
rescue MemCache::MemCacheError, Errno::ECONNREFUSED

@ -46,7 +46,7 @@ def initialize(*args, &block)
end
def insert(index, *args, &block)
index = self.index(index) unless index.is_a?(Integer)
index = assert_index(index, :before)
middleware = self.class::Middleware.new(*args, &block)
super(index, middleware)
end
@ -54,9 +54,8 @@ def insert(index, *args, &block)
alias_method :insert_before, :insert
def insert_after(index, *args, &block)
i = index.is_a?(Integer) ? index : self.index(index)
raise "No such middleware to insert after: #{index.inspect}" unless i
insert(i + 1, *args, &block)
index = assert_index(index, :after)
insert(index + 1, *args, &block)
end
def swap(target, *args, &block)
@ -79,5 +78,13 @@ def build(app = nil, &block)
raise "MiddlewareStack#build requires an app" unless app
reverse.inject(app) { |a, e| e.build(a) }
end
protected
def assert_index(index, where)
i = index.is_a?(Integer) ? index : self.index(index)
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
i
end
end
end

@ -1,6 +1,5 @@
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/regexp'
require 'action_controller/polymorphic_routes'
module ActionDispatch
# = Routing
@ -217,13 +216,14 @@ module Routing
autoload :Route, 'action_dispatch/routing/route'
autoload :RouteSet, 'action_dispatch/routing/route_set'
autoload :UrlFor, 'action_dispatch/routing/url_for'
autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes'
SEPARATORS = %w( / . ? ) #:nodoc:
HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] #:nodoc:
# A helper module to hold URL related helpers.
module Helpers #:nodoc:
include ActionController::PolymorphicRoutes
include PolymorphicRoutes
end
end
end

@ -0,0 +1,186 @@
module ActionDispatch
module Routing
# Polymorphic URL helpers are methods for smart resolution to a named route call when
# given an Active Record model instance. They are to be used in combination with
# ActionController::Resources.
#
# These methods are useful when you want to generate correct URL or path to a RESTful
# resource without having to know the exact type of the record in question.
#
# Nested resources and/or namespaces are also supported, as illustrated in the example:
#
# polymorphic_url([:admin, @article, @comment])
#
# results in:
#
# admin_article_comment_url(@article, @comment)
#
# == Usage within the framework
#
# Polymorphic URL helpers are used in a number of places throughout the Rails framework:
#
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
# <tt>url_for(@article)</tt>;
# * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
# <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
# action;
# * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
# <tt>redirect_to(post)</tt> in your controllers;
# * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
# for feed entries.
#
# == Prefixed polymorphic helpers
#
# In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
# number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
# in options. Those are:
#
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
#
# Example usage:
#
# edit_polymorphic_path(@post) # => "/posts/1/edit"
# polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf"
module PolymorphicRoutes
# Constructs a call to a named RESTful route for the given record and returns the
# resulting URL string. For example:
#
# # calls post_url(post)
# polymorphic_url(post) # => "http://example.com/posts/1"
# polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
# polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
# polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
# polymorphic_url(Comment) # => "http://example.com/comments"
#
# ==== Options
#
# * <tt>:action</tt> - Specifies the action prefix for the named route:
# <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
# Default is <tt>:url</tt>.
#
# ==== Examples
#
# # an Article record
# polymorphic_url(record) # same as article_url(record)
#
# # a Comment record
# polymorphic_url(record) # same as comment_url(record)
#
# # it recognizes new records and maps to the collection
# record = Comment.new
# polymorphic_url(record) # same as comments_url()
#
# # the class of a record will also map to the collection
# polymorphic_url(Comment) # same as comments_url()
#
def polymorphic_url(record_or_hash_or_array, options = {})
if record_or_hash_or_array.kind_of?(Array)
record_or_hash_or_array = record_or_hash_or_array.compact
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
end
record = extract_record(record_or_hash_or_array)
record = record.to_model if record.respond_to?(:to_model)
args = case record_or_hash_or_array
when Hash; [ record_or_hash_or_array ]
when Array; record_or_hash_or_array.dup
else [ record_or_hash_or_array ]
end
inflection = if options[:action].to_s == "new"
args.pop
:singular
elsif (record.respond_to?(:persisted?) && !record.persisted?)
args.pop
:plural
elsif record.is_a?(Class)
args.pop
:plural
else
:singular
end
args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
url_options = options.except(:action, :routing_type)
unless url_options.empty?
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
end
send(named_route, *args)
end
# Returns the path component of a URL for the given record. It uses
# <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
def polymorphic_path(record_or_hash_or_array, options = {})
polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
end
%w(edit new).each do |action|
module_eval <<-EOT, __FILE__, __LINE__ + 1
def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
polymorphic_url( # polymorphic_url(
record_or_hash, # record_or_hash,
options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
end # end
#
def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
polymorphic_url( # polymorphic_url(
record_or_hash, # record_or_hash,
options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
end # end
EOT
end
private
def action_prefix(options)
options[:action] ? "#{options[:action]}_" : ''
end
def routing_type(options)
options[:routing_type] || :url
end
def build_named_route_call(records, inflection, options = {})
unless records.is_a?(Array)
record = extract_record(records)
route = ''
else
record = records.pop
route = records.inject("") do |string, parent|
if parent.is_a?(Symbol) || parent.is_a?(String)
string << "#{parent}_"
else
string << ActiveModel::Naming.plural(parent).singularize
string << "_"
end
end
end
if record.is_a?(Symbol) || record.is_a?(String)
route << "#{record}_"
else
route << ActiveModel::Naming.plural(record)
route = route.singularize if inflection == :singular
route << "_"
route << "index_" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
end
action_prefix(options) + route + routing_type(options).to_s
end
def extract_record(record_or_hash_or_array)
case record_or_hash_or_array
when Array; record_or_hash_or_array.last
when Hash; record_or_hash_or_array[:id]
else record_or_hash_or_array
end
end
end
end
end

@ -414,7 +414,8 @@ def opts
elsif value.is_a?(Array)
value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
else
Rack::Mount::Utils.escape_uri(value.to_param)
return nil unless param = value.to_param
param.split('/').map { |v| Rack::Mount::Utils.escape_uri(v) }.join("/")
end
end
{:parameterize => parameterize}

@ -82,6 +82,7 @@ module Routing
#
module UrlFor
extend ActiveSupport::Concern
include PolymorphicRoutes
included do
# TODO: with_routing extends @controller with url_helpers, trickling down to including this module which overrides its default_url_options

@ -1,7 +1,6 @@
module ActionDispatch
module Assertions
autoload :DomAssertions, 'action_dispatch/testing/assertions/dom'
autoload :ModelAssertions, 'action_dispatch/testing/assertions/model'
autoload :ResponseAssertions, 'action_dispatch/testing/assertions/response'
autoload :RoutingAssertions, 'action_dispatch/testing/assertions/routing'
autoload :SelectorAssertions, 'action_dispatch/testing/assertions/selector'
@ -11,7 +10,6 @@ module Assertions
included do
include DomAssertions
include ModelAssertions
include ResponseAssertions
include RoutingAssertions
include SelectorAssertions

@ -1,19 +0,0 @@
module ActionDispatch
module Assertions
module ModelAssertions
# Ensures that the passed record is valid by Active Record standards and
# returns any error messages if it is not.
#
# ==== Examples
#
# # assert that a newly created record is valid
# model = Model.new
# assert_valid(model)
#
def assert_valid(record)
::ActiveSupport::Deprecation.warn("assert_valid is deprecated. Use assert record.valid? instead", caller)
assert record.valid?, record.errors.full_messages.join("\n")
end
end
end
end

@ -23,6 +23,7 @@
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require 'active_support/ruby/shim'
require 'active_support/core_ext/class/attribute_accessors'

@ -20,7 +20,6 @@ module Helpers #:nodoc:
autoload :NumberHelper
autoload :PrototypeHelper
autoload :RawOutputHelper
autoload :RecordIdentificationHelper
autoload :RecordTagHelper
autoload :SanitizeHelper
autoload :ScriptaculousHelper
@ -51,7 +50,6 @@ module Helpers #:nodoc:
include NumberHelper
include PrototypeHelper
include RawOutputHelper
include RecordIdentificationHelper
include RecordTagHelper
include SanitizeHelper
include ScriptaculousHelper

@ -800,7 +800,8 @@ def build_options(selected, options = {})
start = options.delete(:start) || 0
stop = options.delete(:end) || 59
step = options.delete(:step) || 1
leading_zeros = options.delete(:leading_zeros).nil? ? true : false
options.reverse_merge!({:leading_zeros => true})
leading_zeros = options.delete(:leading_zeros)
select_options = []
start.step(stop, step) do |i|

@ -298,12 +298,12 @@ def form_for(record_or_name_or_array, *args, &proc)
object_name = record_or_name_or_array
when Array
object = record_or_name_or_array.last
object_name = options[:as] || ActionController::RecordIdentifier.singular_class_name(object)
object_name = options[:as] || ActiveModel::Naming.singular(object)
apply_form_for_options!(record_or_name_or_array, options)
args.unshift object
else
object = record_or_name_or_array
object_name = options[:as] || ActionController::RecordIdentifier.singular_class_name(object)
object_name = options[:as] || ActiveModel::Naming.singular(object)
apply_form_for_options!([object], options)
args.unshift object
end
@ -529,7 +529,7 @@ def fields_for(record_or_name_or_array, *args, &block)
object = args.first
else
object = record_or_name_or_array
object_name = ActionController::RecordIdentifier.singular_class_name(object)
object_name = ActiveModel::Naming.singular(object)
end
builder = options[:builder] || ActionView::Base.default_form_builder
@ -1152,11 +1152,11 @@ def fields_for(record_or_name_or_array, *args, &block)
end
when Array
object = record_or_name_or_array.last
name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
name = "#{object_name}#{index}[#{ActiveModel::Naming.singular(object)}]"
args.unshift(object)
else
object = record_or_name_or_array
name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
name = "#{object_name}#{index}[#{ActiveModel::Naming.singular(object)}]"
args.unshift(object)
end

@ -447,7 +447,7 @@ def option_groups_from_collection_for_select(collection, group_method, group_lab
# wrap the output in an appropriate <tt><select></tt> tag.
def grouped_options_for_select(grouped_options, selected_key = nil, prompt = nil)
body = ''
body << content_tag(:option, prompt, :value => "") if prompt
body << content_tag(:option, prompt, { :value => "" }, true) if prompt
grouped_options = grouped_options.sort if grouped_options.is_a?(Hash)
@ -593,11 +593,11 @@ def to_time_zone_select_tag(priority_zones, options, html_options)
private
def add_options(option_tags, options, value = nil)
if options[:include_blank]
option_tags = "<option value=\"\">#{options[:include_blank] if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
option_tags = "<option value=\"\">#{html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
end
if value.blank? && options[:prompt]
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
option_tags = "<option value=\"\">#{prompt}</option>\n" + option_tags
option_tags = "<option value=\"\">#{html_escape(prompt)}</option>\n" + option_tags
end
option_tags.html_safe
end

@ -139,7 +139,7 @@ def remote_function(options)
function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
return function
return function.html_safe
end
# All the methods were moved to GeneratorMethods so that

@ -1,23 +0,0 @@
module ActionView
# = Action View Record Identification Helpers
#
# See ActionController::RecordIdentifier for documentation on these methods.
module Helpers
module RecordIdentificationHelper
# See ActionController::RecordIdentifier.partial_path -- this is just a delegate to that for convenient access in the view.
def partial_path(*args, &block)
ActionController::RecordIdentifier.partial_path(*args, &block)
end
# See ActionController::RecordIdentifier.dom_class -- this is just a delegate to that for convenient access in the view.
def dom_class(*args, &block)
ActionController::RecordIdentifier.dom_class(*args, &block)
end
# See ActionController::RecordIdentifier.dom_id -- this is just a delegate to that for convenient access in the view.
def dom_id(*args, &block)
ActionController::RecordIdentifier.dom_id(*args, &block)
end
end
end
end

@ -1,7 +1,11 @@
require 'action_controller/record_identifier'
module ActionView
# = Action View Record Tag Helpers
module Helpers
module RecordTagHelper
include ActionController::RecordIdentifier
# Produces a wrapper DIV element with id and class parameters that
# relate to the specified Active Record object. Usage example:
#

@ -398,7 +398,7 @@ def link_to_unless_current(name, options = {}, html_options = {}, &block)
def link_to_unless(condition, name, options = {}, html_options = {}, &block)
if condition
if block_given?
block.arity <= 1 ? yield(name) : yield(name, options, html_options)
block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
else
name
end

@ -37,7 +37,7 @@ module Behavior
include ActionController::TemplateAssertions
include ActionView::Context
include ActionController::PolymorphicRoutes
include ActionDispatch::Routing::PolymorphicRoutes
include ActionController::RecordIdentifier
include AbstractController::Helpers

@ -482,21 +482,6 @@ def test_redirected_to_with_nested_controller
assert_redirected_to :controller => 'admin/user'
end
def test_assert_valid
get :get_valid_record
assert_deprecated { assert_valid assigns('record') }
end
def test_assert_valid_failing
get :get_invalid_record
begin
assert_deprecated { assert_valid assigns('record') }
assert false
rescue ActiveSupport::TestCase::Assertion => e
end
end
def test_assert_response_uses_exception_message
@controller = AssertResponseWithUnexpectedErrorController.new
get :index

@ -26,20 +26,6 @@ def name
end
end
class Comment::Nested < Comment; end
class Test::Unit::TestCase
protected
def comments_url
'http://www.example.com/comments'
end
def comment_url(comment)
"http://www.example.com/comments/#{comment.id}"
end
end
class RecordIdentifierTest < Test::Unit::TestCase
include ActionController::RecordIdentifier
@ -76,30 +62,4 @@ def test_dom_class
def test_dom_class_with_prefix
assert_equal "custom_prefix_#{@singular}", dom_class(@record, :custom_prefix)
end
def test_singular_class_name
assert_equal @singular, singular_class_name(@record)
end
def test_singular_class_name_for_class
assert_equal @singular, singular_class_name(@klass)
end
def test_plural_class_name
assert_equal @plural, plural_class_name(@record)
end
def test_plural_class_name_for_class
assert_equal @plural, plural_class_name(@klass)
end
def test_uncountable
assert_equal true, uncountable?(@uncountable)
assert_equal false, uncountable?(@klass)
end
private
def method_missing(method, *args)
RecordIdentifier.send(method, *args)
end
end

@ -79,6 +79,14 @@ class ResourceUnavailableToRescueAsString < StandardError
render :text => 'no way'
end
rescue_from ActionView::TemplateError do
render :text => 'action_view templater error'
end
rescue_from IOError do
render :text => 'io error'
end
before_filter(:only => :before_filter_raises) { raise 'umm nice' }
def before_filter_raises
@ -141,6 +149,14 @@ def resource_unavailable_raise_as_string
def missing_template
end
def io_error_in_view
raise ActionView::TemplateError.new(nil, {}, IOError.new('this is io error'))
end
def zero_division_error_in_view
raise ActionView::TemplateError.new(nil, {}, ZeroDivisionError.new('this is zero division error'))
end
protected
def deny_access
@ -228,6 +244,17 @@ def test_exception_in_parent_controller
end
class RescueControllerTest < ActionController::TestCase
def test_io_error_in_view
get :io_error_in_view
assert_equal 'io error', @response.body
end
def test_zero_division_error_in_view
get :zero_division_error_in_view
assert_equal 'action_view templater error', @response.body
end
def test_rescue_handler
get :not_authorized
assert_response :forbidden

@ -461,6 +461,13 @@ def test_assert_routing_with_method
def test_assert_routing_in_module
assert_routing 'admin/user', :controller => 'admin/user', :action => 'index'
end
def test_assert_routing_with_glob
with_routing do |set|
set.draw { |map| match('*path' => "pages#show") }
assert_routing('/company/about', { :controller => 'pages', :action => 'show', :path => 'company/about' })
end
end
def test_params_passing
get :test_params, :page => {:name => "Page name", :month => '4', :year => '2004', :day => '6'}

@ -66,6 +66,16 @@ def setup
assert_equal BazMiddleware, @stack[0].klass
end
test "raise an error on invalid index" do
assert_raise RuntimeError do
@stack.insert("HiyaMiddleware", BazMiddleware)
end
assert_raise RuntimeError do
@stack.insert_after("HiyaMiddleware", BazMiddleware)
end
end
test "lazy evaluates middleware class" do
assert_difference "@stack.size" do
@stack.use "MiddlewareStackTest::BazMiddleware"

@ -392,19 +392,19 @@ class RequestTest < ActiveSupport::TestCase
[{'baz'=>[{'foo'=>'baz'}, "1"]}, {'baz'=>[{'foo'=>'[FILTERED]'}, "1"]}, [/foo/]]]
test_hashes.each do |before_filter, after_filter, filter_words|
request = stub_request('action_dispatch.parameter_filter' => filter_words)
assert_equal after_filter, request.send(:process_parameter_filter, before_filter)
parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
assert_equal after_filter, parameter_filter.filter(before_filter)
filter_words << 'blah'
filter_words << lambda { |key, value|
value.reverse! if key =~ /bargain/
}
request = stub_request('action_dispatch.parameter_filter' => filter_words)
parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}}
after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}}
assert_equal after_filter, request.send(:process_parameter_filter, before_filter)
assert_equal after_filter, parameter_filter.filter(before_filter)
end
end

@ -44,7 +44,12 @@ def raise_data_overflow
session[:foo] = 'bye!' * 1024
head :ok
end
def change_session_id
request.session_options[:id] = nil
get_session_id
end
def rescue_action(e) raise end
end
@ -212,6 +217,19 @@ def test_persistent_session_id
end
end
def test_setting_session_id_to_nil_is_respected
with_test_route_set do
cookies[SessionKey] = SignedBar
get "/get_session_id"
sid = response.body
assert_equal sid.size, 36
get "/change_session_id"
assert_not_equal sid, response.body
end
end
def test_session_store_with_expire_after
with_test_route_set(:expire_after => 5.hours) do
# First request accesses the session

@ -210,6 +210,12 @@ def test_grouped_options_for_select_returns_html_safe_string
assert grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]]).html_safe?
end
def test_grouped_options_for_select_with_prompt_returns_html_escaped_string
assert_dom_equal(
"<option value=\"\">&lt;Choose One&gt;</option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>",
grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]], nil, '<Choose One>'))
end
def test_optgroups_with_with_options_with_hash
assert_dom_equal(
"<optgroup label=\"Europe\"><option value=\"Denmark\">Denmark</option>\n<option value=\"Germany\">Germany</option></optgroup><optgroup label=\"North America\"><option value=\"United States\">United States</option>\n<option value=\"Canada\">Canada</option></optgroup>",
@ -367,6 +373,15 @@ def test_select_with_blank_as_string
)
end
def test_select_with_blank_as_string_escaped
@post = Post.new
@post.category = "<mus>"
assert_dom_equal(
"<select id=\"post_category\" name=\"post[category]\"><option value=\"\">&lt;None&gt;</option>\n<option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\" selected=\"selected\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
select("post", "category", %w( abe <mus> hest), :include_blank => '<None>')
)
end
def test_select_with_default_prompt
@post = Post.new
@post.category = ""
@ -394,6 +409,14 @@ def test_select_with_given_prompt
)
end
def test_select_with_given_prompt_escaped
@post = Post.new
assert_dom_equal(
"<select id=\"post_category\" name=\"post[category]\"><option value=\"\">&lt;The prompt&gt;</option>\n<option value=\"abe\">abe</option>\n<option value=\"&lt;mus&gt;\">&lt;mus&gt;</option>\n<option value=\"hest\">hest</option></select>",
select("post", "category", %w( abe <mus> hest), :prompt => '<The prompt>')
)
end
def test_select_with_prompt_and_blank
@post = Post.new
@post.category = ""

@ -104,6 +104,12 @@ def test_update_page_tag_with_html_options
assert_equal javascript_tag(create_generator(&block).to_s, {:defer => 'true'}), update_page_tag({:defer => 'true'}, &block)
end
def test_remote_function
res = remote_function(:url => authors_path, :with => "'author[name]='+$F('author_name')+'&author[dob]='+$F('author_dob')")
assert_equal "new Ajax.Request('/authors', {asynchronous:true, evalScripts:true, parameters:'author[name]='+$F('author_name')+'&author[dob]='+$F('author_dob')})", res
assert res.html_safe?
end
protected
def author_path(record)
"/authors/#{record.id}"

@ -1,3 +1,8 @@
*Rails 3.0.0 [Release Candidate] (unreleased)*
* Added ActiveModel::MassAssignmentSecurity [Eric Chapweske, Josh Kalderimis]
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
* JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh]

@ -32,7 +32,7 @@ Rake::RDocTask.new do |rdoc|
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
rdoc.options << '--charset' << 'utf-8'
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
rdoc.rdoc_files.include("README", "CHANGELOG")
rdoc.rdoc_files.include("README.rdoc", "CHANGELOG")
rdoc.rdoc_files.include("lib/**/*.rb")
end

@ -14,7 +14,7 @@
s.homepage = 'http://www.rubyonrails.org'
s.rubyforge_project = 'activemodel'
s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README', 'lib/**/*']
s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*']
s.require_path = 'lib'
s.has_rdoc = true

@ -57,6 +57,35 @@ module Naming
def model_name
@_model_name ||= ActiveModel::Name.new(self)
end
# Returns the plural class name of a record or class. Examples:
#
# ActiveModel::Naming.plural(post) # => "posts"
# ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
def self.plural(record_or_class)
model_name_from_record_or_class(record_or_class).plural
end
# Returns the singular class name of a record or class. Examples:
#
# ActiveModel::Naming.singular(post) # => "post"
# ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
def self.singular(record_or_class)
model_name_from_record_or_class(record_or_class).singular
end
# Identifies whether the class name of a record or class is uncountable. Examples:
#
# ActiveModel::Naming.uncountable?(Sheep) # => true
# ActiveModel::Naming.uncountable?(Post) => false
def self.uncountable?(record_or_class)
plural(record_or_class) == singular(record_or_class)
end
private
def self.model_name_from_record_or_class(record_or_class)
(record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
end
end
end

@ -1,4 +1,6 @@
require 'cases/helper'
require 'models/contact'
require 'models/sheep'
require 'models/track_back'
class NamingTest < ActiveModel::TestCase
@ -26,3 +28,40 @@ def test_partial_path
assert_equal 'post/track_backs/track_back', @model_name.partial_path
end
end
class NamingHelpersTest < Test::Unit::TestCase
def setup
@klass = Contact
@record = @klass.new
@singular = 'contact'
@plural = 'contacts'
@uncountable = Sheep
end
def test_singular
assert_equal @singular, singular(@record)
end
def test_singular_for_class
assert_equal @singular, singular(@klass)
end
def test_plural
assert_equal @plural, plural(@record)
end
def test_plural_for_class
assert_equal @plural, plural(@klass)
end
def test_uncountable
assert uncountable?(@uncountable), "Expected 'sheep' to be uncoutable"
assert !uncountable?(@klass), "Expected 'contact' to be countable"
end
private
def method_missing(method, *args)
ActiveModel::Naming.send(method, *args)
end
end

@ -1,4 +1,5 @@
class Contact
extend ActiveModel::Naming
include ActiveModel::Conversion
attr_accessor :id, :name, :age, :created_at, :awesome, :preferences

@ -0,0 +1,4 @@
class Sheep
extend ActiveModel::Naming
end

@ -1,5 +1,7 @@
*Rails 3.0.0 [RC1] (unreleased)*
* Changed update_attribute to not run callbacks and update the record directly in the database [Neeraj Singh]
* Add scoping and unscoped as the syntax to replace the old with_scope and with_exclusive_scope [José Valim]
* New rake task, db:migrate:status, displays status of migrations #4947 [Kevin Skoglund]

@ -309,28 +309,13 @@ Admit the Database:
== Download
The latest version of Active Record can be found at
The latest version of Active Record can be installed with Rubygems:
* http://rubyforge.org/project/showfiles.php?group_id=182
* gem install activerecord
Documentation can be found at
* http://ar.rubyonrails.com
== Installation
The prefered method of installing Active Record is through its GEM file. You'll need to have
RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have,
then use:
% [sudo] gem install activerecord-1.10.0.gem
You can also install Active Record the old-fashioned way with the following command:
% [sudo] ruby install.rb
from its distribution directory.
* http://api.rubyonrails.org
== License

@ -172,7 +172,7 @@ Rake::RDocTask.new { |rdoc|
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
rdoc.options << '--charset' << 'utf-8'
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
rdoc.rdoc_files.include('README.rdoc', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
rdoc.rdoc_files.include('lib/**/*.rb')
rdoc.rdoc_files.exclude('lib/active_record/vendor/*')
rdoc.rdoc_files.include('dev-utils/*.rb')

@ -14,12 +14,12 @@
s.homepage = 'http://www.rubyonrails.org'
s.rubyforge_project = 'activerecord'
s.files = Dir['CHANGELOG', 'README', 'examples/**/*', 'lib/**/*']
s.files = Dir['CHANGELOG', 'README.rdoc', 'examples/**/*', 'lib/**/*']
s.require_path = 'lib'
s.has_rdoc = true
s.extra_rdoc_files = %w( README )
s.rdoc_options.concat ['--main', 'README']
s.extra_rdoc_files = %w( README.rdoc )
s.rdoc_options.concat ['--main', 'README.rdoc']
s.add_dependency('activesupport', version)
s.add_dependency('activemodel', version)

@ -1,30 +0,0 @@
require 'rbconfig'
require 'find'
require 'ftools'
include Config
# this was adapted from rdoc's install.rb by ways of Log4r
$sitedir = CONFIG["sitelibdir"]
unless $sitedir
version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
$libdir = File.join(CONFIG["libdir"], "ruby", version)
$sitedir = $:.find {|x| x =~ /site_ruby/ }
if !$sitedir
$sitedir = File.join($libdir, "site_ruby")
elsif $sitedir !~ Regexp.quote(version)
$sitedir = File.join($sitedir, version)
end
end
# the actual gruntwork
Dir.chdir("lib")
Find.find("active_record", "active_record.rb") { |f|
if f[-3..-1] == ".rb"
File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
else
File::makedirs(File.join($sitedir, *f.split(/\//)))
end
}

@ -112,13 +112,13 @@ def preload_one_association(records, association, preload_options={})
# Not all records have the same class, so group then preload
# group on the reflection itself so that if various subclass share the same association then we do not split them
# unnecessarily
records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records|
records.group_by { |record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, _records|
raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
# 'reflection.macro' can return 'belongs_to', 'has_many', etc. Thus,
# the following could call 'preload_belongs_to_association',
# 'preload_has_many_association', etc.
send("preload_#{reflection.macro}_association", records, reflection, preload_options)
send("preload_#{reflection.macro}_association", _records, reflection, preload_options)
end
end
@ -378,7 +378,7 @@ def find_associated_records(ids, reflection, preload_options)
:order => preload_options[:order] || options[:order]
}
reflection.klass.unscoped.apply_finder_options(find_options).to_a
reflection.klass.scoped.apply_finder_options(find_options).to_a
end

@ -3,6 +3,7 @@
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/conversions'
require 'active_support/core_ext/module/remove_method'
module ActiveRecord
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
@ -1354,7 +1355,7 @@ def join_table_name(first_table_name, second_table_name)
end
def association_accessor_methods(reflection, association_proxy_class)
define_method(reflection.name) do |*params|
redefine_method(reflection.name) do |*params|
force_reload = params.first unless params.empty?
association = association_instance_get(reflection.name)
@ -1371,12 +1372,12 @@ def association_accessor_methods(reflection, association_proxy_class)
association.target.nil? ? nil : association
end
define_method("loaded_#{reflection.name}?") do
redefine_method("loaded_#{reflection.name}?") do
association = association_instance_get(reflection.name)
association && association.loaded?
end
define_method("#{reflection.name}=") do |new_value|
redefine_method("#{reflection.name}=") do |new_value|
association = association_instance_get(reflection.name)
if association.nil? || association.target != new_value
@ -1386,8 +1387,8 @@ def association_accessor_methods(reflection, association_proxy_class)
association.replace(new_value)
association_instance_set(reflection.name, new_value.nil? ? nil : association)
end
define_method("set_#{reflection.name}_target") do |target|
redefine_method("set_#{reflection.name}_target") do |target|
return if target.nil? and association_proxy_class == BelongsToAssociation
association = association_proxy_class.new(self, reflection)
association.target = target
@ -1396,7 +1397,7 @@ def association_accessor_methods(reflection, association_proxy_class)
end
def collection_reader_method(reflection, association_proxy_class)
define_method(reflection.name) do |*params|
redefine_method(reflection.name) do |*params|
force_reload = params.first unless params.empty?
association = association_instance_get(reflection.name)
@ -1409,8 +1410,8 @@ def collection_reader_method(reflection, association_proxy_class)
association
end
define_method("#{reflection.name.to_s.singularize}_ids") do
redefine_method("#{reflection.name.to_s.singularize}_ids") do
if send(reflection.name).loaded? || reflection.options[:finder_sql]
send(reflection.name).map(&:id)
else
@ -1430,14 +1431,14 @@ def collection_accessor_methods(reflection, association_proxy_class, writer = tr
collection_reader_method(reflection, association_proxy_class)
if writer
define_method("#{reflection.name}=") do |new_value|
redefine_method("#{reflection.name}=") do |new_value|
# Loads proxy class instance (defined in collection_reader_method) if not already loaded
association = send(reflection.name)
association.replace(new_value)
association
end
define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i)
send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids))
end
@ -1445,7 +1446,7 @@ def collection_accessor_methods(reflection, association_proxy_class, writer = tr
end
def association_constructor_method(constructor, reflection, association_proxy_class)
define_method("#{constructor}_#{reflection.name}") do |*params|
redefine_method("#{constructor}_#{reflection.name}") do |*params|
attributees = params.first unless params.empty?
replace_existing = params[1].nil? ? true : params[1]
association = association_instance_get(reflection.name)
@ -1486,8 +1487,8 @@ def add_counter_cache_callbacks(reflection)
end
def add_touch_callbacks(reflection, touch_attribute)
method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
method_name = :"belongs_to_touch_after_save_or_destroy_for_#{reflection.name}"
redefine_method(method_name) do
association = send(reflection.name)
if touch_attribute == true

@ -218,9 +218,9 @@ def count(column_name = nil, options = {})
# are actually removed from the database, that depends precisely on
# +delete_records+. They are in any case removed from the collection.
def delete(*records)
remove_records(records) do |records, old_records|
remove_records(records) do |_records, old_records|
delete_records(old_records) if old_records.any?
records.each { |record| @target.delete(record) }
_records.each { |record| @target.delete(record) }
end
end
@ -231,7 +231,7 @@ def delete(*records)
# ignoring the +:dependent+ option.
def destroy(*records)
records = find(records) if records.any? {|record| record.kind_of?(Fixnum) || record.kind_of?(String)}
remove_records(records) do |records, old_records|
remove_records(records) do |_records, old_records|
old_records.each { |record| record.destroy }
end
@ -396,11 +396,12 @@ def load_target
if @target.is_a?(Array) && @target.any?
@target = find_target.map do |f|
i = @target.index(f)
t = @target.delete_at(i) if i
if t && t.changed?
t
if i
@target.delete_at(i).tap do |t|
keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names)
t.attributes = f.attributes.except(*keys)
end
else
f.mark_for_destruction if t && t.marked_for_destruction?
f
end
end + @target
@ -477,7 +478,14 @@ def add_record_to_target_with_callbacks(record)
callback(:before_add, record)
yield(record) if block_given?
@target ||= [] unless loaded?
@target << record unless @reflection.options[:uniq] && @target.include?(record)
index = @target.index(record)
unless @reflection.options[:uniq] && index
if index
@target[index] = record
else
@target << record
end
end
callback(:after_add, record)
set_inverse_instance(record, @owner)
record

@ -45,17 +45,23 @@ def insert_record(record, force = true, validate = true)
if @reflection.options[:insert_sql]
@owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
else
relation = Arel::Table.new(@reflection.options[:join_table])
relation = Arel::Table.new(@reflection.options[:join_table])
timestamps = record_timestamp_columns(record)
timezone = record.send(:current_time_from_proper_timezone) if timestamps.any?
attributes = columns.inject({}) do |attrs, column|
case column.name.to_s
name = column.name
case name.to_s
when @reflection.primary_key_name.to_s
attrs[relation[column.name]] = owner_quoted_id
attrs[relation[name]] = @owner.id
when @reflection.association_foreign_key.to_s
attrs[relation[column.name]] = record.quoted_id
attrs[relation[name]] = record.id
when *timestamps
attrs[relation[name]] = timezone
else
if record.has_attribute?(column.name)
value = @owner.send(:quote_value, record[column.name], column)
attrs[relation[column.name]] = value unless value.nil?
if record.has_attribute?(name)
value = @owner.send(:quote_value, record[name], column)
attrs[relation[name]] = value unless value.nil?
end
end
attrs
@ -117,6 +123,14 @@ def create_record(attributes, &block)
build_record(attributes, &block)
end
end
def record_timestamp_columns(record)
if record.record_timestamps
record.send(:all_timestamp_attributes).map(&:to_s)
else
[]
end
end
end
end
end

@ -199,11 +199,14 @@ def current_savepoint_name
def log(sql, name)
name ||= "SQL"
result = nil
ActiveSupport::Notifications.instrument("sql.active_record",
:sql => sql, :name => name, :connection_id => self.object_id) do
@runtime += Benchmark.ms { result = yield }
instrumenter = ActiveSupport::Notifications.instrumenter
result = instrumenter.instrument("sql.active_record",
:sql => sql, :name => name, :connection_id => object_id) do
yield
end
@runtime += instrumenter.elapsed
result
rescue Exception => e
message = "#{e.class.name}: #{e.message}: #{sql}"

@ -871,7 +871,7 @@ def setup_fixture_accessors(table_names = nil)
table_names.each do |table_name|
table_name = table_name.to_s.tr('./', '_')
define_method(table_name) do |*fixtures|
redefine_method(table_name) do |*fixtures|
force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
@fixture_cache[table_name] ||= {}

@ -6,14 +6,16 @@ def initialize
end
def sql(event)
return unless logger.debug?
name = '%s (%.1fms)' % [event.payload[:name], event.duration]
sql = event.payload[:sql].squeeze(' ')
if odd?
name = color(name, :cyan, true)
name = color(name, CYAN, true)
sql = color(sql, nil, true)
else
name = color(name, :magenta, true)
name = color(name, MAGENTA, true)
end
debug " #{name} #{sql}"
@ -29,4 +31,4 @@ def logger
end
end
ActiveRecord::LogSubscriber.attach_to :active_record
ActiveRecord::LogSubscriber.attach_to :active_record

@ -26,7 +26,7 @@ module ClassMethods
# You can define a \scope that applies to all finders using
# ActiveRecord::Base.default_scope.
def scoped(options = nil)
if options.present?
if options
scoped.apply_finder_options(options)
else
current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone
@ -105,7 +105,7 @@ def scope(name, scope_options = {}, &block)
extension ? relation.extending(extension) : relation
end
singleton_class.send :define_method, name, &scopes[name]
singleton_class.send(:redefine_method, name, &scopes[name])
end
def named_scope(*args, &block)

@ -112,6 +112,8 @@ def becomes(klass)
# * does not work on attr_accessor attributes. The attribute that is being updated must be column name.
#
def update_attribute(name, value)
raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s
changes = record_update_timestamps || {}
if name

@ -22,6 +22,12 @@ class Railtie < Rails::Railtie
load "active_record/railties/databases.rake"
end
# When loading console, force ActiveRecord to be loaded to avoid cross
# references when loading a constant for the first time.
console do
ActiveRecord::Base
end
initializer "active_record.initialize_timezone" do
ActiveSupport.on_load(:active_record) do
self.time_zone_aware_attributes = true

@ -274,7 +274,7 @@ namespace :db do
task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ]
desc 'Load the seed data from db/seeds.rb'
task :seed => :environment do
task :seed => 'db:abort_if_pending_migrations' do
seed_file = File.join(Rails.root, 'db', 'seeds.rb')
load(seed_file) if File.exist?(seed_file)
end

@ -13,14 +13,15 @@ class Relation
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
delegate :insert, :to => :arel
attr_reader :table, :klass
attr_reader :table, :klass, :loaded
attr_accessor :extensions
alias :loaded? :loaded
def initialize(klass, table)
@klass, @table = klass, table
@implicit_readonly = nil
@loaded = nil
@loaded = false
SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
(ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
@ -292,10 +293,6 @@ def delete(id_or_array)
where(@klass.primary_key => id_or_array).delete_all
end
def loaded?
@loaded
end
def reload
reset
to_a # force reload

@ -294,7 +294,6 @@ def destroy
private
def get_session(env, sid)
Base.silence do
sid ||= generate_sid
session = find_session(sid)
env[SESSION_RECORD_KEY] = session
[sid, session.data]

@ -39,8 +39,9 @@ def create #:nodoc:
if record_timestamps
current_time = current_time_from_proper_timezone
write_attribute('created_at', current_time) if respond_to?(:created_at) && created_at.nil?
write_attribute('created_on', current_time) if respond_to?(:created_on) && created_on.nil?
timestamp_attributes_for_create.each do |column|
write_attribute(column.to_s, current_time) if respond_to?(column) && self.send(column).nil?
end
timestamp_attributes_for_update_in_model.each do |column|
write_attribute(column.to_s, current_time) if self.send(column).nil?
@ -65,7 +66,19 @@ def record_update_timestamps #:nodoc:
end
def timestamp_attributes_for_update_in_model #:nodoc:
[:updated_at, :updated_on].select { |elem| respond_to?(elem) }
timestamp_attributes_for_update.select { |elem| respond_to?(elem) }
end
def timestamp_attributes_for_update #:nodoc:
[:updated_at, :updated_on]
end
def timestamp_attributes_for_create #:nodoc:
[:created_at, :created_on]
end
def all_timestamp_attributes #:nodoc:
timestamp_attributes_for_update + timestamp_attributes_for_create
end
def current_time_from_proper_timezone #:nodoc:

@ -101,6 +101,7 @@ def with_real_execute
#we need to actually modify some data, so we make execute point to the original method
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
alias_method :execute_with_stub, :execute
remove_method :execute
alias_method :execute, :execute_without_stub
end
yield

@ -125,7 +125,7 @@ class OverridingAggregationsTest < ActiveRecord::TestCase
class Name; end
class DifferentName; end
class Person < ActiveRecord::Base
class Person < ActiveRecord::Base
composed_of :composed_of, :mapping => %w(person_first_name first_name)
end

@ -28,7 +28,7 @@ def test_schema_define
assert_equal 7, ActiveRecord::Migrator::current_version
end
def test_schema_raises_an_error_for_invalid_column_ntype
def test_schema_raises_an_error_for_invalid_column_type
assert_raise NoMethodError do
ActiveRecord::Schema.define(:version => 8) do
create_table :vegetables do |t|

@ -5,8 +5,6 @@
require 'models/topic'
require 'models/reply'
require 'models/computer'
require 'models/customer'
require 'models/order'
require 'models/post'
require 'models/author'
require 'models/tag'

@ -1,8 +1,6 @@
require "cases/helper"
require 'models/post'
require 'models/comment'
require 'models/author'
require 'models/category'
require 'models/project'
require 'models/developer'

@ -2,7 +2,6 @@
require 'models/post'
require 'models/comment'
require 'models/author'
require 'models/category'
require 'models/categorization'
require 'models/company'
require 'models/topic'

@ -2,20 +2,14 @@
require 'models/developer'
require 'models/project'
require 'models/company'
require 'models/topic'
require 'models/reply'
require 'models/computer'
require 'models/customer'
require 'models/order'
require 'models/categorization'
require 'models/category'
require 'models/post'
require 'models/author'
require 'models/comment'
require 'models/tag'
require 'models/tagging'
require 'models/person'
require 'models/reader'
require 'models/parrot'
require 'models/pirate'
require 'models/treasure'
@ -24,6 +18,8 @@
require 'models/member'
require 'models/membership'
require 'models/sponsor'
require 'models/country'
require 'models/treaty'
require 'active_support/core_ext/string/conversions'
class ProjectWithAfterCreateHook < ActiveRecord::Base
@ -83,6 +79,55 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
:parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
def setup_data_for_habtm_case
ActiveRecord::Base.connection.execute('delete from countries_treaties')
country = Country.new(:name => 'India')
country.country_id = 'c1'
country.save!
treaty = Treaty.new(:name => 'peace')
treaty.treaty_id = 't1'
country.treaties << treaty
end
def test_should_property_quote_string_primary_keys
setup_data_for_habtm_case
con = ActiveRecord::Base.connection
sql = 'select * from countries_treaties'
record = con.select_rows(sql).last
assert_equal 'c1', record[0]
assert_equal 't1', record[1]
end
def test_should_record_timestamp_for_join_table
setup_data_for_habtm_case
con = ActiveRecord::Base.connection
sql = 'select * from countries_treaties'
record = con.select_rows(sql).last
assert_not_nil record[2]
assert_not_nil record[3]
assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2]
assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3]
end
def test_should_record_timestamp_for_join_table_only_if_timestamp_should_be_recorded
begin
Treaty.record_timestamps = false
setup_data_for_habtm_case
con = ActiveRecord::Base.connection
sql = 'select * from countries_treaties'
record = con.select_rows(sql).last
assert_nil record[2]
assert_nil record[3]
ensure
Treaty.record_timestamps = true
end
end
def test_has_and_belongs_to_many
david = Developer.find(1)

@ -412,7 +412,7 @@ def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
i = interests(:trainspotting)
m = i.man
assert_not_nil m.interests
iz = m.interests.detect {|iz| iz.id == i.id}
iz = m.interests.detect { |_iz| _iz.id == i.id}
assert_not_nil iz
assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
i.topic = 'Eating cheese with a spoon'
@ -516,7 +516,7 @@ def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
i = interests(:llama_wrangling)
m = i.polymorphic_man
assert_not_nil m.polymorphic_interests
iz = m.polymorphic_interests.detect {|iz| iz.id == i.id}
iz = m.polymorphic_interests.detect { |_iz| _iz.id == i.id}
assert_not_nil iz
assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
i.topic = 'Eating cheese with a spoon'

@ -2,11 +2,6 @@
require 'models/developer'
require 'models/project'
require 'models/company'
require 'models/topic'
require 'models/reply'
require 'models/computer'
require 'models/customer'
require 'models/order'
require 'models/categorization'
require 'models/category'
require 'models/post'
@ -17,18 +12,31 @@
require 'models/person'
require 'models/reader'
require 'models/parrot'
require 'models/pirate'
require 'models/treasure'
require 'models/price_estimate'
require 'models/club'
require 'models/member'
require 'models/membership'
require 'models/sponsor'
require 'models/ship_part'
require 'models/ship'
class AssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
:computers, :people, :readers
def test_loading_the_association_target_should_keep_child_records_marked_for_destruction
ship = Ship.create!(:name => "The good ship Dollypop")
part = ship.parts.create!(:name => "Mast")
part.mark_for_destruction
ship.parts.send(:load_target)
assert ship.parts[0].marked_for_destruction?
end
def test_loading_the_association_target_should_load_most_recent_attributes_for_child_records_marked_for_destruction
ship = Ship.create!(:name => "The good ship Dollypop")
part = ship.parts.create!(:name => "Mast")
part.mark_for_destruction
ShipPart.find(part.id).update_attribute(:name, 'Deck')
ship.parts.send(:load_target)
assert_equal 'Deck', ship.parts[0].name
end
def test_include_with_order_works
assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)}

@ -123,6 +123,14 @@ def test_read_attributes_before_type_cast_on_datetime
assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
else
assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db)
developer.created_at = "345643456"
assert_equal developer.created_at_before_type_cast, "345643456"
assert_equal developer.created_at, nil
developer.created_at = "2010-03-21T21:23:32+01:00"
assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00"
assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00")
end
end
@ -1816,8 +1824,8 @@ def test_to_xml_including_methods
def test_to_xml_with_block
value = "Rockin' the block"
xml = Company.new.to_xml(:skip_instruct => true) do |xml|
xml.tag! "arbitrary-element", value
xml = Company.new.to_xml(:skip_instruct => true) do |_xml|
_xml.tag! "arbitrary-element", value
end
assert_equal "<company>", xml.first(9)
assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))

@ -10,7 +10,6 @@
require 'models/project'
require 'models/developer'
require 'models/customer'
require 'models/job'
class DynamicFinderMatchTest < ActiveRecord::TestCase
def test_find_no_match

@ -36,7 +36,7 @@ def test_clean_fixtures
fixtures = nil
assert_nothing_raised { fixtures = create_fixtures(name) }
assert_kind_of(Fixtures, fixtures)
fixtures.each { |name, fixture|
fixtures.each { |_name, fixture|
fixture.each { |key, value|
assert_match(MATCH_ATTRIBUTE_NAME, key)
}
@ -229,9 +229,9 @@ def test_resets_to_min_pk_with_default_pk_and_sequence
def test_create_fixtures_resets_sequences_when_not_cached
@instances.each do |instance|
max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)|
max_id = create_fixtures(instance.class.table_name).inject(0) do |_max_id, (name, fixture)|
fixture_id = fixture['id'].to_i
fixture_id > max_id ? fixture_id : max_id
fixture_id > _max_id ? fixture_id : _max_id
end
# Clone the last fixture to check that it gets the next greatest id.

@ -4,6 +4,7 @@
class LogSubscriberTest < ActiveRecord::TestCase
include ActiveSupport::LogSubscriber::TestHelper
include ActiveSupport::BufferedLogger::Severity
def setup
@old_logger = ActiveRecord::Base.logger
@ -39,4 +40,21 @@ def test_cached_queries
assert_match(/CACHE/, @logger.logged(:debug).last)
assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last)
end
def test_basic_query_doesnt_log_when_level_is_not_debug
@logger.level = INFO
Developer.all
wait
assert_equal 0, @logger.logged(:debug).size
end
def test_cached_queries_doesnt_log_when_level_is_not_debug
@logger.level = INFO
ActiveRecord::Base.cache do
Developer.all
Developer.all
end
wait
assert_equal 0, @logger.logged(:debug).size
end
end

@ -8,7 +8,6 @@
require 'models/developer'
require 'models/project'
require 'models/comment'
require 'models/category'
class MethodScopingTest < ActiveRecord::TestCase
fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects
@ -543,4 +542,4 @@ def test_nested_scoped_find_merges_new_and_old_style_joins
assert_equal 1, scoped_authors.size
assert_equal authors(:david).attributes, scoped_authors.first.attributes
end
end
end

@ -270,27 +270,27 @@ def test_many_should_return_true_if_more_than_one
assert Topic.base.many?
end
def test_should_build_with_proxy_options
def test_should_build_on_top_of_named_scope
topic = Topic.approved.build({})
assert topic.approved
end
def test_should_build_new_with_proxy_options
def test_should_build_new_on_top_of_named_scope
topic = Topic.approved.new
assert topic.approved
end
def test_should_create_with_proxy_options
def test_should_create_on_top_of_named_scope
topic = Topic.approved.create({})
assert topic.approved
end
def test_should_create_with_bang_with_proxy_options
def test_should_create_with_bang_on_top_of_named_scope
topic = Topic.approved.create!({})
assert topic.approved
end
def test_should_build_with_proxy_options_chained
def test_should_build_on_top_of_chained_named_scopes
topic = Topic.approved.by_lifo.build({})
assert topic.approved
assert_equal 'lifo', topic.author_name

@ -59,6 +59,7 @@ def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
pirate.save!
assert_equal 1, pirate.birds_with_reject_all_blank.count
assert_equal 'Tweetie', pirate.birds_with_reject_all_blank.first.name
end
def test_should_raise_an_ArgumentError_for_non_existing_associations
@ -74,7 +75,7 @@ def test_should_disable_allow_destroy_by_default
ship = pirate.create_ship(:name => 'Nights Dirty Lightning')
assert_no_difference('Ship.count') do
pirate.update_attributes(:ship_attributes => { '_destroy' => true })
pirate.update_attributes(:ship_attributes => { '_destroy' => true, :id => ship.id })
end
end
@ -100,7 +101,8 @@ def test_reject_if_method_with_arguments
pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
assert_no_difference('Ship.count') { pirate.save! }
# pirate.reject_empty_ships_on_create returns false for saved records
# pirate.reject_empty_ships_on_create returns false for saved pirate records
# in the previous step note that pirate gets saved but ship fails
pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
assert_difference('Ship.count') { pirate.save! }
end
@ -266,6 +268,28 @@ def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
end
assert_equal 'Mayflower', @ship.reload.name
end
def test_should_update_existing_when_update_only_is_true_and_id_is_given
@ship.delete
@ship = @pirate.create_update_only_ship(:name => 'Nights Dirty Lightning')
assert_no_difference('Ship.count') do
@pirate.update_attributes(:update_only_ship_attributes => { :name => 'Mayflower', :id => @ship.id })
end
assert_equal 'Mayflower', @ship.reload.name
end
def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction
Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => true
@ship.delete
@ship = @pirate.create_update_only_ship(:name => 'Nights Dirty Lightning')
assert_difference('Ship.count', -1) do
@pirate.update_attributes(:update_only_ship_attributes => { :name => 'Mayflower', :id => @ship.id, :_destroy => true })
end
Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => false
end
end
class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
@ -411,6 +435,27 @@ def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
end
assert_equal 'Arr', @pirate.reload.catchphrase
end
def test_should_update_existing_when_update_only_is_true_and_id_is_given
@pirate.delete
@pirate = @ship.create_update_only_pirate(:catchphrase => 'Aye')
assert_no_difference('Pirate.count') do
@ship.update_attributes(:update_only_pirate_attributes => { :catchphrase => 'Arr', :id => @pirate.id })
end
assert_equal 'Arr', @pirate.reload.catchphrase
end
def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction
Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => true
@pirate.delete
@pirate = @ship.create_update_only_pirate(:catchphrase => 'Aye')
assert_difference('Pirate.count', -1) do
@ship.update_attributes(:update_only_pirate_attributes => { :catchphrase => 'Arr', :id => @pirate.id, :_destroy => true })
end
Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => false
end
end
module NestedAttributesOnACollectionAssociationTests
@ -811,7 +856,25 @@ def setup
@part = @ship.parts.create!(:name => "Mast")
@trinket = @part.trinkets.create!(:name => "Necklace")
end
test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do
@ship.parts_attributes=[{:id => @part.id,:name =>'Deck'}]
assert_equal 1, @ship.parts.proxy_target.size
assert_equal 'Deck', @ship.parts[0].name
end
test "if association is not loaded and child doesn't change and I am saving a grandchild then in memory record should be used" do
@ship.parts_attributes=[{:id => @part.id,:trinkets_attributes =>[{:id => @trinket.id, :name => 'Ruby'}]}]
assert_equal 1, @ship.parts.proxy_target.size
assert_equal 'Mast', @ship.parts[0].name
assert_no_difference("@ship.parts[0].trinkets.proxy_target.size") do
@ship.parts[0].trinkets.proxy_target.size
end
assert_equal 'Ruby', @ship.parts[0].trinkets[0].name
@ship.save
assert_equal 'Ruby', @ship.parts[0].trinkets[0].name
end
test "when grandchild changed in memory, saving parent should save grandchild" do
@trinket.name = "changed"
@ship.save

@ -5,25 +5,19 @@
require 'models/reply'
require 'models/category'
require 'models/company'
require 'models/customer'
require 'models/developer'
require 'models/project'
require 'models/default'
require 'models/auto_id'
require 'models/column_name'
require 'models/subscriber'
require 'models/keyboard'
require 'models/comment'
require 'models/minimalistic'
require 'models/warehouse_thing'
require 'models/parrot'
require 'models/minivan'
require 'models/loose_person'
require 'rexml/document'
require 'active_support/core_ext/exception'
class PersistencesTest < ActiveRecord::TestCase
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans
def test_create
topic = Topic.new
@ -220,6 +214,11 @@ def test_update_attribute
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
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

@ -13,7 +13,7 @@ def test_to_key_with_default_primary_key
topic = Topic.new
assert topic.to_key.nil?
topic = Topic.find(1)
assert_equal topic.to_key, [1]
assert_equal [1], topic.to_key
end
def test_to_key_with_customized_primary_key
@ -26,7 +26,7 @@ def test_to_key_with_customized_primary_key
def test_to_key_with_primary_key_after_destroy
topic = Topic.find(1)
topic.destroy
assert_equal topic.to_key, [1]
assert_equal [1], topic.to_key
end
def test_integer_key

@ -1,8 +1,6 @@
require "cases/helper"
require 'models/topic'
require 'models/reply'
require 'models/task'
require 'models/course'
require 'models/category'
require 'models/post'

@ -5,6 +5,8 @@
require 'models/project'
require 'models/comment'
require 'models/category'
require 'models/person'
require 'models/reference'
class RelationScopingTest < ActiveRecord::TestCase
fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects
@ -218,7 +220,7 @@ def test_nested_exclusive_scope_for_create
end
class HasManyScopingTest< ActiveRecord::TestCase
fixtures :comments, :posts
fixtures :comments, :posts, :people, :references
def setup
@welcome = Post.find(1)
@ -250,6 +252,23 @@ def test_nested_scope_finder
assert_equal 'a comment...', @welcome.comments.what_are_you
end
end
def test_should_maintain_default_scope_on_associations
person = people(:michael)
magician = BadReference.find(1)
assert_equal [magician], people(:michael).bad_references
end
def test_should_default_scope_on_associations_is_overriden_by_association_conditions
person = people(:michael)
assert_equal [], people(:michael).fixed_bad_references
end
def test_should_maintain_default_scope_on_eager_loaded_associations
michael = Person.where(:id => people(:michael).id).includes(:bad_references).first
magician = BadReference.find(1)
assert_equal [magician], michael.bad_references
end
end
class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
@ -399,4 +418,4 @@ def test_create_attribute_overwrites_default_values
assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary
assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary
end
end
end

@ -1,5 +1,4 @@
require "cases/helper"
require 'models/tag'
require 'models/tagging'
require 'models/post'
require 'models/topic'

@ -1,6 +1,5 @@
require "cases/helper"
require 'models/topic'
require 'models/reply'
class TransactionCallbacksTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false

@ -1,6 +1,5 @@
require "cases/helper"
require 'models/topic'
require 'models/reply'
class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
def setup

Some files were not shown because too many files have changed in this diff Show More