Merge remote branch 'rails/master'

Conflicts:
	actionpack/lib/abstract_controller/base.rb
This commit is contained in:
Xavier Noria 2010-06-20 23:13:19 +02:00
commit 207fa59675
117 changed files with 1912 additions and 981 deletions

View File

@ -5,6 +5,7 @@ gem "rails", :path => File.dirname(__FILE__)
gem "rake", ">= 0.8.7"
gem "mocha", ">= 0.9.8"
gem "rdoc", "2.2"
mri = !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
if mri && RUBY_VERSION < '1.9'
@ -44,10 +45,6 @@ elsif RUBY_ENGINE == "jruby"
end
end
group :documentation do
gem 'rdoc', '2.1'
end
if ENV['CI']
gem "nokogiri", ">= 1.4.0"

View File

@ -1,3 +1,6 @@
gem 'rdoc', '= 2.2'
require 'rdoc'
require 'rake'
require 'rake/rdoctask'
require 'rake/gempackagetask'
@ -68,7 +71,15 @@ Rake::RDocTask.new do |rdoc|
rdoc.options << '--charset' << 'utf-8'
rdoc.options << '--main' << 'railties/README'
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : './doc/template/horo'
# Workaround: RDoc assumes that rdoc.template can be required, and that
# rdoc.template.upcase is a constant living in RDoc::Generator::HTML
# which holds the actual template class.
#
# We put 'doc/template' in the load path to be able to set the template
# to the string 'horo' and thus meet those RDoc's assumptions.
$:.unshift('doc/template')
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : 'horo'
rdoc.rdoc_files.include('railties/CHANGELOG')
rdoc.rdoc_files.include('railties/MIT-LICENSE')
@ -117,7 +128,7 @@ end
desc "Publish API docs for Rails as a whole and for each component"
task :pdoc => :rdoc do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/api", "doc/rdoc").upload
Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/api", "doc/rdoc").upload
PROJECTS.each do |project|
system %(cd #{project} && #{$0} pdoc)
end

View File

@ -1,4 +1,5 @@
require 'rubygems'
gem 'rdoc', '= 2.2'
require 'rdoc'
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
@ -53,5 +54,5 @@ end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/am", "doc").upload
Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/am", "doc").upload
end

View File

@ -1,5 +1,15 @@
*Rails 3.0.0 [Release Candidate] (unreleased)*
* Add shallow routes back to the new router [Diego Carrion, Andrew White]
resources :posts do
shallow do
resources :comments
end
end
You can now use comment_path for /comments/1 instead of post_comment_path for /posts/1/comments/1.
* Add support for multi-subdomain session by setting cookie host in session cookie so you can share session between www.example.com, example.com and user.example.com. #4818 [Guillermo Álvarez]
* Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino]

View File

@ -1,4 +1,5 @@
require 'rubygems'
gem 'rdoc', '= 2.2'
require 'rdoc'
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

View File

@ -1,4 +1,5 @@
require 'active_support/configurable'
require 'active_support/descendants_tracker'
require 'active_support/core_ext/module/anonymous'
module AbstractController
@ -10,6 +11,7 @@ module AbstractController
attr_internal :action_name
include ActiveSupport::Configurable
extend ActiveSupport::DescendantsTracker
class << self
attr_reader :abstract
@ -21,17 +23,6 @@ module AbstractController
@abstract = true
end
def inherited(klass)
::AbstractController::Base.descendants << klass.to_s
super
end
# A list of all descendants of AbstractController::Base. This is
# useful for initializers which need to add behavior to all controllers.
def descendants
@descendants ||= []
end
# A list of all internal methods for a controller. This finds the first
# abstract superclass of a controller, and gets a list of all public
# instance methods on that abstract class. Public instance methods of

View File

@ -52,8 +52,7 @@ module ActionController
class Metal < AbstractController::Base
abstract!
# :api: public
attr_internal :params, :env
attr_internal :env
# Returns the last part of the controller's name, underscored, without the ending
# "Controller". For instance, MyApp::MyPostsController would return "my_posts" for
@ -85,6 +84,14 @@ module ActionController
super
end
def params
@_params ||= request.parameters
end
def params=(val)
@_params = val
end
# Basic implementations for content_type=, location=, and headers are
# provided to reduce the dependency on the RackDelegation module
# in Renderer and Redirector.

View File

@ -14,10 +14,6 @@ module ActionController
super(action, request)
end
def params
@_params ||= @_request.parameters
end
def response_body=(body)
response.body = body if response
super

View File

@ -11,7 +11,7 @@ module ActionController
# polymorphic_url([:admin, @article, @comment])
#
# results in:
#
#
# admin_article_comment_url(@article, @comment)
#
# == Usage within the framework
@ -166,6 +166,7 @@ module ActionController
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

View File

@ -1,7 +1,7 @@
require 'active_support/core_ext/module'
module ActionController
# The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
# The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
# Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate
# the view actions to a higher logical level. Example:
#
@ -28,7 +28,7 @@ module ActionController
# end
#
# As the example above shows, you can stop caring to a large extent what the actual id of the post is. You just know
# that one is being assigned and that the subsequent calls in redirect_to and the RJS expect that same naming
# that one is being assigned and that the subsequent calls in redirect_to and the RJS expect that same naming
# convention and allows you to write less code if you follow it.
module RecordIdentifier
extend self
@ -59,7 +59,7 @@ module ActionController
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
#
# dom_id(Post.find(45), :edit) # => "edit_post_45"
def dom_id(record, prefix = nil)
def dom_id(record, prefix = nil)
if record_id = record_key_for_dom_id(record)
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
else
@ -102,6 +102,14 @@ module ActionController
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

0
actionpack/lib/action_dispatch/http/request.rb Executable file → Normal file
View File

View File

@ -8,7 +8,7 @@ module ActionDispatch
class Callbacks
include ActiveSupport::Callbacks
define_callbacks :call, :terminator => "result == false", :rescuable => true
define_callbacks :call, :rescuable => true
define_callbacks :prepare, :scope => :name
# Add a preparation callback. Preparation callbacks are run before every
@ -37,12 +37,12 @@ module ActionDispatch
def initialize(app, prepare_each_request = false)
@app, @prepare_each_request = app, prepare_each_request
run_callbacks(:prepare)
_run_prepare_callbacks
end
def call(env)
run_callbacks(:call) do
run_callbacks(:prepare) if @prepare_each_request
_run_call_callbacks do
_run_prepare_callbacks if @prepare_each_request
@app.call(env)
end
end

View File

@ -122,7 +122,7 @@ module ActionDispatch
end
def render(status, body)
[status, {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}, [body]]
[status, {'Content-Type' => 'text/html', 'Content-Length' => body.bytesize.to_s}, [body]]
end
def public_path

View File

@ -10,7 +10,7 @@ module ActionDispatch
# Prepare dispatcher callbacks and run 'prepare' callbacks
initializer "action_dispatch.prepare_dispatcher" do |app|
ActionDispatch::Callbacks.to_prepare { app.routes_reloader.reload_if_changed }
ActionDispatch::Callbacks.to_prepare { app.routes_reloader.execute_if_updated }
end
end
end

View File

@ -33,7 +33,7 @@ module ActionDispatch
end
class Mapping #:nodoc:
IGNORE_OPTIONS = [:to, :as, :controller, :action, :via, :on, :constraints, :defaults, :only, :except, :anchor]
IGNORE_OPTIONS = [:to, :as, :controller, :action, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
def initialize(set, scope, args)
@set, @scope = set, scope
@ -102,7 +102,7 @@ module ActionDispatch
end
def requirements
@requirements ||= (@options[:constraints] || {}).tap do |requirements|
@requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements|
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
@options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
end
@ -343,7 +343,7 @@ module ActionDispatch
def namespace(path)
path = path.to_s
scope(:path => path, :name_prefix => path, :module => path) { yield }
scope(:path => path, :name_prefix => path, :module => path, :shallow_path => path, :shallow_prefix => path) { yield }
end
def constraints(constraints = {})
@ -378,10 +378,18 @@ module ActionDispatch
Mapper.normalize_path("#{parent}/#{child}")
end
def merge_shallow_path_scope(parent, child)
Mapper.normalize_path("#{parent}/#{child}")
end
def merge_name_prefix_scope(parent, child)
parent ? "#{parent}_#{child}" : child
end
def merge_shallow_prefix_scope(parent, child)
parent ? "#{parent}_#{child}" : child
end
def merge_module_scope(parent, child)
parent ? "#{parent}/#{child}" : child
end
@ -409,11 +417,13 @@ module ActionDispatch
def merge_options_scope(parent, child)
(parent || {}).merge(child)
end
def merge_shallow_scope(parent, child)
child ? true : false
end
end
module Resources
CRUD_ACTIONS = [:index, :show, :create, :update, :destroy] #:nodoc:
class Resource #:nodoc:
def self.default_actions
[:index, :create, :new, :show, :update, :destroy, :edit]
@ -442,15 +452,6 @@ module ActionDispatch
end
end
def action_type(action)
case action
when :index, :create
:collection
when :show, :update, :destroy
:member
end
end
def name
options[:as] || @name
end
@ -463,34 +464,19 @@ module ActionDispatch
name.to_s.singularize
end
def member_prefix
':id'
end
def member_name
singular
end
alias_method :nested_name, :member_name
# Checks for uncountable plurals, and appends "_index" if they're.
def collection_name
uncountable? ? "#{plural}_index" : plural
singular == plural ? "#{plural}_index" : plural
end
def uncountable?
singular == plural
end
def name_for_action(action)
case action_type(action)
when :collection
collection_name
when :member
member_name
end
end
def id_segment
":#{singular}_id"
def shallow?
options[:shallow] ? true : false
end
def constraints
@ -506,21 +492,43 @@ module ActionDispatch
end
def collection_options
(options || {}).dup.tap do |options|
options.delete(:id)
options[:constraints] = options[:constraints].dup if options[:constraints]
options[:constraints].delete(:id) if options[:constraints].is_a?(Hash)
(options || {}).dup.tap do |opts|
opts.delete(:id)
opts[:constraints] = options[:constraints].dup if options[:constraints]
opts[:constraints].delete(:id) if options[:constraints].is_a?(Hash)
end
end
def nested_prefix
id_segment
def nested_path
"#{path}/:#{singular}_id"
end
def nested_options
options = { :name_prefix => member_name }
options["#{singular}_id".to_sym] = id_constraint if id_constraint?
options
{}.tap do |opts|
opts[:name_prefix] = member_name
opts["#{singular}_id".to_sym] = id_constraint if id_constraint?
opts[:options] = { :shallow => shallow? } unless options[:shallow].nil?
end
end
def resource_scope
[{ :controller => controller }]
end
def collection_scope
[path, collection_options]
end
def member_scope
["#{path}/:id", options]
end
def new_scope
[path]
end
def nested_scope
[nested_path, nested_options]
end
end
@ -533,27 +541,28 @@ module ActionDispatch
super
end
def action_type(action)
case action
when :show, :create, :update, :destroy
:member
end
end
def member_prefix
''
end
def member_name
name
end
alias_method :collection_name, :member_name
def nested_prefix
''
def nested_path
path
end
def nested_options
{ :name_prefix => member_name }
{}.tap do |opts|
opts[:name_prefix] = member_name
opts[:options] = { :shallow => shallow? } unless @options[:shallow].nil?
end
end
def shallow?
false
end
def member_scope
[path, options]
end
end
@ -565,28 +574,25 @@ module ActionDispatch
def resource(*resources, &block)
options = resources.extract_options!
options = (@scope[:options] || {}).merge(options)
options[:shallow] = true if @scope[:shallow] && !options.has_key?(:shallow)
if apply_common_behavior_for(:resource, resources, options, &block)
return self
end
resource = SingletonResource.new(resources.pop, options)
resource_scope(SingletonResource.new(resources.pop, options)) do
yield if block_given?
scope(:path => resource.path, :controller => resource.controller) do
with_scope_level(:resource, resource) do
collection_scope do
post :create if parent_resource.actions.include?(:create)
get :new if parent_resource.actions.include?(:new)
end
yield if block_given?
with_scope_level(:member) do
scope(resource.options) do
get :show if resource.actions.include?(:show)
post :create if resource.actions.include?(:create)
put :update if resource.actions.include?(:update)
delete :destroy if resource.actions.include?(:destroy)
get :new, :as => resource.name if resource.actions.include?(:new)
get :edit, :as => resource.name if resource.actions.include?(:edit)
end
end
member_scope do
get :show if parent_resource.actions.include?(:show)
put :update if parent_resource.actions.include?(:update)
delete :destroy if parent_resource.actions.include?(:destroy)
get :edit if parent_resource.actions.include?(:edit)
end
end
@ -596,35 +602,26 @@ module ActionDispatch
def resources(*resources, &block)
options = resources.extract_options!
options = (@scope[:options] || {}).merge(options)
options[:shallow] = true if @scope[:shallow] && !options.has_key?(:shallow)
if apply_common_behavior_for(:resources, resources, options, &block)
return self
end
resource = Resource.new(resources.pop, options)
resource_scope(Resource.new(resources.pop, options)) do
yield if block_given?
scope(:path => resource.path, :controller => resource.controller) do
with_scope_level(:resources, resource) do
yield if block_given?
collection_scope do
get :index if parent_resource.actions.include?(:index)
post :create if parent_resource.actions.include?(:create)
get :new if parent_resource.actions.include?(:new)
end
with_scope_level(:collection) do
scope(resource.collection_options) do
get :index if resource.actions.include?(:index)
post :create if resource.actions.include?(:create)
get :new, :as => resource.singular if resource.actions.include?(:new)
end
end
with_scope_level(:member) do
scope(':id') do
scope(resource.options) do
get :show if resource.actions.include?(:show)
put :update if resource.actions.include?(:update)
delete :destroy if resource.actions.include?(:destroy)
get :edit, :as => resource.singular if resource.actions.include?(:edit)
end
end
end
member_scope do
get :show if parent_resource.actions.include?(:show)
put :update if parent_resource.actions.include?(:update)
delete :destroy if parent_resource.actions.include?(:destroy)
get :edit if parent_resource.actions.include?(:edit)
end
end
@ -636,10 +633,8 @@ module ActionDispatch
raise ArgumentError, "can't use collection outside resources scope"
end
with_scope_level(:collection) do
scope(:name_prefix => parent_resource.collection_name, :as => "") do
yield
end
collection_scope do
yield
end
end
@ -648,10 +643,8 @@ module ActionDispatch
raise ArgumentError, "can't use member outside resource(s) scope"
end
with_scope_level(:member) do
scope(parent_resource.member_prefix, :name_prefix => parent_resource.member_name, :as => "") do
yield
end
member_scope do
yield
end
end
@ -659,10 +652,12 @@ module ActionDispatch
unless resource_scope?
raise ArgumentError, "can't use new outside resource(s) scope"
end
with_scope_level(:new) do
scope(new_scope_prefix, :name_prefix => parent_resource.member_name, :as => "") do
yield
scope(*parent_resource.new_scope) do
scope(action_path(:new)) do
yield
end
end
end
end
@ -673,8 +668,18 @@ module ActionDispatch
end
with_scope_level(:nested) do
scope(parent_resource.nested_prefix, parent_resource.nested_options) do
yield
if parent_resource.shallow?
with_exclusive_scope do
if @scope[:shallow_path].blank?
scope(*parent_resource.nested_scope) { yield }
else
scope(@scope[:shallow_path], :name_prefix => @scope[:shallow_prefix]) do
scope(*parent_resource.nested_scope) { yield }
end
end
end
else
scope(*parent_resource.nested_scope) { yield }
end
end
end
@ -687,63 +692,79 @@ module ActionDispatch
end
end
def shallow
scope(:shallow => true) do
yield
end
end
def match(*args)
options = args.extract_options!
options[:anchor] = true unless options.key?(:anchor)
if args.length > 1
args.each { |path| match(path, options) }
args.each { |path| match(path, options.dup) }
return self
end
if [:collection, :member, :new].include?(options[:on])
args.push(options)
case options.delete(:on)
when :collection
return collection { match(*args) }
when :member
return member { match(*args) }
when :new
return new { match(*args) }
end
end
if @scope[:scope_level] == :resource
args.push(options)
return member { match(*args) }
end
path_names = options.delete(:path_names)
if args.first.is_a?(Symbol)
action = args.first
if CRUD_ACTIONS.include?(action)
begin
old_path = @scope[:path]
@scope[:path] = "#{@scope[:path]}(.:format)"
return match(options.reverse_merge(
:to => action,
:as => parent_resource.name_for_action(action)
))
ensure
@scope[:path] = old_path
end
else
with_exclusive_name_prefix(action_name_prefix(action, options)) do
return match("#{action_path(action, path_names)}(.:format)", options.reverse_merge(:to => action))
path = path_for_action(args.first, path_names)
options = options_for_action(args.first, options)
with_exclusive_scope do
return super(path, options)
end
elsif resource_method_scope?
path = path_for_custom_action
options[:as] = name_for_action(options[:as]) if options[:as]
args.push(options)
with_exclusive_scope do
scope(path) do
return super
end
end
end
args.push(options)
case options.delete(:on)
when :collection
return collection { match(*args) }
when :member
return member { match(*args) }
when :new
return new { match(*args) }
end
if @scope[:scope_level] == :resource
return member { match(*args) }
end
if resource_scope?
raise ArgumentError, "can't define route directly in resource(s) scope"
end
args.push(options)
super
end
def root(options={})
options[:on] ||= :collection if @scope[:scope_level] == :resources
super(options)
if @scope[:scope_level] == :resources
with_scope_level(:nested) do
scope(parent_resource.path, :name_prefix => parent_resource.collection_name) do
super(options)
end
end
else
super(options)
end
end
protected
@ -752,15 +773,6 @@ module ActionDispatch
end
private
def action_path(name, path_names = nil)
path_names ||= @scope[:path_names]
path_names[name.to_sym] || name.to_s
end
def action_name_prefix(action, options = {})
(options[:on] == :new || @scope[:scope_level] == :new) ? "#{action}_new" : action
end
def apply_common_behavior_for(method, resources, options, &block)
if resources.length > 1
resources.each { |r| send(method, r, options, &block) }
@ -784,27 +796,24 @@ module ActionDispatch
false
end
def new_scope_prefix
@scope[:path_names][:new] || 'new'
end
def resource_scope?
[:resource, :resources].include?(@scope[:scope_level])
end
def with_exclusive_name_prefix(prefix)
def resource_method_scope?
[:collection, :member, :new].include?(@scope[:scope_level])
end
def with_exclusive_scope
begin
old_name_prefix = @scope[:name_prefix]
old_name_prefix, old_path = @scope[:name_prefix], @scope[:path]
@scope[:name_prefix], @scope[:path] = nil, nil
if !old_name_prefix.blank?
@scope[:name_prefix] = "#{prefix}_#{@scope[:name_prefix]}"
else
@scope[:name_prefix] = prefix.to_s
with_scope_level(:exclusive) do
yield
end
yield
ensure
@scope[:name_prefix] = old_name_prefix
@scope[:name_prefix], @scope[:path] = old_name_prefix, old_path
end
end
@ -816,6 +825,125 @@ module ActionDispatch
@scope[:scope_level] = old
@scope[:scope_level_resource] = old_resource
end
def resource_scope(resource)
with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do
scope(*parent_resource.resource_scope) do
yield
end
end
end
def collection_scope
with_scope_level(:collection) do
scope(*parent_resource.collection_scope) do
yield
end
end
end
def member_scope
with_scope_level(:member) do
scope(*parent_resource.member_scope) do
yield
end
end
end
def path_for_action(action, path_names)
case action
when :index, :create
"#{@scope[:path]}(.:format)"
when :show, :update, :destroy
if parent_resource.shallow?
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id(.:format)"
else
"#{@scope[:path]}(.:format)"
end
when :new
"#{@scope[:path]}/#{action_path(:new)}(.:format)"
when :edit
if parent_resource.shallow?
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(:edit)}(.:format)"
else
"#{@scope[:path]}/#{action_path(:edit)}(.:format)"
end
else
case @scope[:scope_level]
when :collection, :new
"#{@scope[:path]}/#{action_path(action)}(.:format)"
else
if parent_resource.shallow?
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(action)}(.:format)"
else
"#{@scope[:path]}/#{action_path(action)}(.:format)"
end
end
end
end
def path_for_custom_action
case @scope[:scope_level]
when :collection, :new
@scope[:path]
else
if parent_resource.shallow?
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id"
else
@scope[:path]
end
end
end
def action_path(name, path_names = nil)
path_names ||= @scope[:path_names]
path_names[name.to_sym] || name.to_s
end
def options_for_action(action, options)
options.reverse_merge(
:to => action,
:as => name_for_action(action)
)
end
def name_for_action(action)
name_prefix = @scope[:name_prefix].blank? ? "" : "#{@scope[:name_prefix]}_"
shallow_prefix = @scope[:shallow_prefix].blank? ? "" : "#{@scope[:shallow_prefix]}_"
case action
when :index, :create
"#{name_prefix}#{parent_resource.collection_name}"
when :show, :update, :destroy
if parent_resource.shallow?
"#{shallow_prefix}#{parent_resource.member_name}"
else
"#{name_prefix}#{parent_resource.member_name}"
end
when :edit
if parent_resource.shallow?
"edit_#{shallow_prefix}#{parent_resource.member_name}"
else
"edit_#{name_prefix}#{parent_resource.member_name}"
end
when :new
"new_#{name_prefix}#{parent_resource.member_name}"
else
case @scope[:scope_level]
when :collection
"#{action}_#{name_prefix}#{parent_resource.collection_name}"
when :new
"#{action}_new_#{name_prefix}#{parent_resource.member_name}"
else
if parent_resource.shallow?
"#{action}_#{shallow_prefix}#{parent_resource.member_name}"
else
"#{action}_#{name_prefix}#{parent_resource.member_name}"
end
end
end
end
end
include Base

View File

@ -896,8 +896,10 @@ module ActionView
# Returns the separator for a given datetime component
def separator(type)
case type
when :month, :day
@options[:date_separator]
when :month
@options[:discard_month] ? "" : @options[:date_separator]
when :day
@options[:discard_day] ? "" : @options[:date_separator]
when :hour
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
when :minute

View File

@ -303,7 +303,7 @@ module ActionView
args.unshift object
end
options[:html][:remote] = true if options.delete(:remote)
(options[:html] ||= {})[:remote] = true if options.delete(:remote)
output = form_tag(options.delete(:url) || {}, options.delete(:html) || {})
output << fields_for(object_name, *(args << options), &proc)

View File

@ -399,7 +399,7 @@ module ActionView
options_for_select += "<optgroup label=\"#{html_escape(group_label_string)}\">"
options_for_select += options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key)
options_for_select += '</optgroup>'
end
end.html_safe
end
# Returns a string of <tt><option></tt> tags, like <tt>options_for_select</tt>, but

View File

@ -40,7 +40,10 @@ module ActionView
# for a total length not exceeding <tt>:length</tt>.
#
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
# Pass a <tt>:safe</tt> value as "true" to not to escape the content.
#
# The result is not marked as HTML-safe, so will be subject to the default escaping when
# used in views, unless wrapped by <tt>raw()</tt>. Care should be taken if +text+ contains HTML tags
# or entities, because truncation may produce invalid HTML (such as unbalanced or incomplete tags).
#
# ==== Examples
#
@ -57,12 +60,6 @@ module ActionView
# # => "And they f... (continued)"
#
# truncate("<p>Once upon a time in a world far far away</p>")
# # => "&lt;p&gt;Once upon a time i..."
#
# truncate("<p>Once upon a time in a world far far away</p>", :safe => true)
# # => "<p>Once upon a time in a wo..."
#
# truncate("<p>Once upon a time in a world far far away</p>".html_safe)
# # => "<p>Once upon a time in a wo..."
#
# You can still use <tt>truncate</tt> with the old API that accepts the
@ -85,7 +82,6 @@ module ActionView
options.reverse_merge!(:length => 30)
text = h(text) unless text.html_safe? || options[:safe]
text.truncate(options.delete(:length), options) if text
end
@ -117,13 +113,13 @@ module ActionView
end
options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
text = h(text) unless text.html_safe? || options[:safe]
text = sanitize(text) unless options[:sanitize] == false
if text.blank? || phrases.blank?
text
else
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
end
end.html_safe
end
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
@ -253,9 +249,9 @@ module ActionView
# simple_format("Look ma! A class!", :class => 'description')
# # => "<p class='description'>Look ma! A class!</p>"
def simple_format(text, html_options={}, options={})
text = '' if text.nil?
text = ''.html_safe if text.nil?
start_tag = tag('p', html_options, true)
text = h(text) unless text.html_safe? || options[:safe]
text = sanitize(text) unless options[:sanitize] == false
text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
@ -499,7 +495,11 @@ module ActionView
link_text = block_given?? yield(href) : href
href = 'http://' + href unless scheme
content_tag(:a, link_text, link_attributes.merge('href' => href), !(options[:safe] || text.html_safe?)) + punctuation.reverse.join('')
unless options[:sanitize] == false
link_text = sanitize(link_text)
href = sanitize(href)
end
content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('')
end
end.html_safe
end
@ -514,7 +514,11 @@ module ActionView
text.html_safe
else
display_text = (block_given?) ? yield(text) : text
display_text = h(display_text) unless options[:safe]
unless options[:sanitize] == false
text = sanitize(text)
display_text = sanitize(display_text) unless text == display_text
end
mail_to text, display_text, html_options
end
end

View File

@ -318,7 +318,7 @@ module ActionView
object.class.model_name.partial_path.dup.tap do |partial|
path = @view.controller_path
partial.insert(0, "#{File.dirname(path)}/") if path.include?(?/)
partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
end
end
end

View File

@ -85,6 +85,7 @@ module ActionView
def setup_with_controller
@controller = ActionView::TestCase::TestController.new
@request = @controller.request
@output_buffer = ActiveSupport::SafeBuffer.new
@rendered = ''

View File

@ -381,6 +381,7 @@ class PolymorphicRoutesTest < ActionController::TestCase
with_test_routes do
@series.save
assert_equal "http://example.com/series/#{@series.id}", polymorphic_url(@series)
assert_equal "http://example.com/series", polymorphic_url(Series.new)
end
end

View File

@ -13,6 +13,19 @@ class Comment
end
end
class Sheep
extend ActiveModel::Naming
include ActiveModel::Conversion
attr_reader :id
def to_key; id ? [id] : nil end
def save; @id = 1 end
def new_record?; @id.nil? end
def name
@id.nil? ? 'new sheep' : "sheep ##{@id}"
end
end
class Comment::Nested < Comment; end
class Test::Unit::TestCase
@ -20,7 +33,7 @@ class Test::Unit::TestCase
def comments_url
'http://www.example.com/comments'
end
def comment_url(comment)
"http://www.example.com/comments/#{comment.id}"
end
@ -35,6 +48,7 @@ class RecordIdentifierTest < Test::Unit::TestCase
@record = @klass.new
@singular = 'comment'
@plural = 'comments'
@uncountable = Sheep
end
def test_dom_id_with_new_record
@ -58,7 +72,7 @@ class RecordIdentifierTest < Test::Unit::TestCase
def test_dom_class
assert_equal @singular, dom_class(@record)
end
def test_dom_class_with_prefix
assert_equal "custom_prefix_#{@singular}", dom_class(@record, :custom_prefix)
end
@ -79,6 +93,11 @@ class RecordIdentifierTest < Test::Unit::TestCase
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)

View File

@ -7,6 +7,10 @@ module Fun
# :ported:
def hello_world
end
def nested_partial_with_form_builder
render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {}, Proc.new {})
end
end
end
@ -1230,6 +1234,13 @@ class RenderTest < ActionController::TestCase
assert_match(/<label/, @response.body)
assert_template('test/_labelling_form')
end
def test_nested_partial_with_form_builder
@controller = Fun::GamesController.new
get :nested_partial_with_form_builder
assert_match(/<label/, @response.body)
assert_template('fun/games/_form')
end
def test_partial_collection
get :partial_collection

View File

@ -68,6 +68,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
get 'admin/accounts' => "queenbee#accounts"
end
get 'admin/passwords' => "queenbee#passwords", :constraints => ::TestRoutingMapper::IpRestrictor
scope 'pt', :name_prefix => 'pt' do
resources :projects, :path_names => { :edit => 'editar', :new => 'novo' }, :path => 'projetos' do
post :preview, :on => :new
@ -142,6 +144,32 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :comments, :except => :destroy
end
resource :past, :only => :destroy
resource :present, :only => :update
resource :future, :only => :create
resources :relationships, :only => [:create, :destroy]
resources :friendships, :only => [:update]
shallow do
namespace :api do
resources :teams do
resources :players
resource :captain
end
end
end
resources :threads, :shallow => true do
resource :owner
resources :messages do
resources :comments do
member do
post :preview
end
end
end
end
resources :sheep
resources :clients do
@ -154,6 +182,33 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
resources :customers do
get "recent" => "customers#recent", :as => :recent, :on => :collection
get "profile" => "customers#profile", :as => :profile, :on => :member
post "preview" => "customers#preview", :as => :preview, :on => :new
resource :avatar do
get "thumbnail(.:format)" => "avatars#thumbnail", :as => :thumbnail, :on => :member
end
resources :invoices do
get "outstanding" => "invoices#outstanding", :as => :outstanding, :on => :collection
get "overdue", :to => :overdue, :on => :collection
get "print" => "invoices#print", :as => :print, :on => :member
post "preview" => "invoices#preview", :as => :preview, :on => :new
end
resources :notes, :shallow => true do
get "preview" => "notes#preview", :as => :preview, :on => :new
get "print" => "notes#print", :as => :print, :on => :member
end
end
namespace :api do
resources :customers do
get "recent" => "customers#recent", :as => :recent, :on => :collection
get "profile" => "customers#profile", :as => :profile, :on => :member
post "preview" => "customers#preview", :as => :preview, :on => :new
end
end
match 'sprockets.js' => ::TestRoutingMapper::SprocketsApp
match 'people/:id/update', :to => 'people#update', :as => :update_person
@ -223,8 +278,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ }
scope :module => 'api' do
scope :module => :api do
resource :token
resources :errors, :shallow => true do
resources :notices
end
end
scope :path => 'api' do
@ -448,6 +506,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
get '/admin/accounts', {}, {'REMOTE_ADDR' => '10.0.0.100'}
assert_equal 'pass', @response.headers['X-Cascade']
get '/admin/passwords', {}, {'REMOTE_ADDR' => '192.168.1.100'}
assert_equal 'queenbee#passwords', @response.body
get '/admin/passwords', {}, {'REMOTE_ADDR' => '10.0.0.100'}
assert_equal 'pass', @response.headers['X-Cascade']
end
end
@ -709,6 +773,38 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
def test_resource_routes_only_create_update_destroy
with_test_routes do
delete '/past'
assert_equal 'pasts#destroy', @response.body
assert_equal '/past', past_path
put '/present'
assert_equal 'presents#update', @response.body
assert_equal '/present', present_path
post '/future'
assert_equal 'futures#create', @response.body
assert_equal '/future', future_path
end
end
def test_resources_routes_only_create_update_destroy
with_test_routes do
post '/relationships'
assert_equal 'relationships#create', @response.body
assert_equal '/relationships', relationships_path
delete '/relationships/1'
assert_equal 'relationships#destroy', @response.body
assert_equal '/relationships/1', relationship_path(1)
put '/friendships/1'
assert_equal 'friendships#update', @response.body
assert_equal '/friendships/1', friendship_path(1)
end
end
def test_resource_with_slugs_in_ids
with_test_routes do
get '/posts/rails-rocks'
@ -823,7 +919,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/account/admin/subscription', account_admin_subscription_path
end
end
def test_namespace_nested_in_resources
with_test_routes do
get '/clients/1/google/account'
@ -1132,6 +1228,143 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
def test_shallow_nested_resources
with_test_routes do
get '/api/teams'
assert_equal 'api/teams#index', @response.body
assert_equal '/api/teams', api_teams_path
get '/api/teams/new'
assert_equal 'api/teams#new', @response.body
assert_equal '/api/teams/new', new_api_team_path
get '/api/teams/1'
assert_equal 'api/teams#show', @response.body
assert_equal '/api/teams/1', api_team_path(:id => '1')
get '/api/teams/1/edit'
assert_equal 'api/teams#edit', @response.body
assert_equal '/api/teams/1/edit', edit_api_team_path(:id => '1')
get '/api/teams/1/players'
assert_equal 'api/players#index', @response.body
assert_equal '/api/teams/1/players', api_team_players_path(:team_id => '1')
get '/api/teams/1/players/new'
assert_equal 'api/players#new', @response.body
assert_equal '/api/teams/1/players/new', new_api_team_player_path(:team_id => '1')
get '/api/players/2'
assert_equal 'api/players#show', @response.body
assert_equal '/api/players/2', api_player_path(:id => '2')
get '/api/players/2/edit'
assert_equal 'api/players#edit', @response.body
assert_equal '/api/players/2/edit', edit_api_player_path(:id => '2')
get '/api/teams/1/captain'
assert_equal 'api/captains#show', @response.body
assert_equal '/api/teams/1/captain', api_team_captain_path(:team_id => '1')
get '/api/teams/1/captain/new'
assert_equal 'api/captains#new', @response.body
assert_equal '/api/teams/1/captain/new', new_api_team_captain_path(:team_id => '1')
get '/api/teams/1/captain/edit'
assert_equal 'api/captains#edit', @response.body
assert_equal '/api/teams/1/captain/edit', edit_api_team_captain_path(:team_id => '1')
get '/threads'
assert_equal 'threads#index', @response.body
assert_equal '/threads', threads_path
get '/threads/new'
assert_equal 'threads#new', @response.body
assert_equal '/threads/new', new_thread_path
get '/threads/1'
assert_equal 'threads#show', @response.body
assert_equal '/threads/1', thread_path(:id => '1')
get '/threads/1/edit'
assert_equal 'threads#edit', @response.body
assert_equal '/threads/1/edit', edit_thread_path(:id => '1')
get '/threads/1/owner'
assert_equal 'owners#show', @response.body
assert_equal '/threads/1/owner', thread_owner_path(:thread_id => '1')
get '/threads/1/messages'
assert_equal 'messages#index', @response.body
assert_equal '/threads/1/messages', thread_messages_path(:thread_id => '1')
get '/threads/1/messages/new'
assert_equal 'messages#new', @response.body
assert_equal '/threads/1/messages/new', new_thread_message_path(:thread_id => '1')
get '/messages/2'
assert_equal 'messages#show', @response.body
assert_equal '/messages/2', message_path(:id => '2')
get '/messages/2/edit'
assert_equal 'messages#edit', @response.body
assert_equal '/messages/2/edit', edit_message_path(:id => '2')
get '/messages/2/comments'
assert_equal 'comments#index', @response.body
assert_equal '/messages/2/comments', message_comments_path(:message_id => '2')
get '/messages/2/comments/new'
assert_equal 'comments#new', @response.body
assert_equal '/messages/2/comments/new', new_message_comment_path(:message_id => '2')
get '/comments/3'
assert_equal 'comments#show', @response.body
assert_equal '/comments/3', comment_path(:id => '3')
get '/comments/3/edit'
assert_equal 'comments#edit', @response.body
assert_equal '/comments/3/edit', edit_comment_path(:id => '3')
post '/comments/3/preview'
assert_equal 'comments#preview', @response.body
assert_equal '/comments/3/preview', preview_comment_path(:id => '3')
end
end
def test_custom_resource_routes_are_scoped
with_test_routes do
assert_equal '/customers/recent', recent_customers_path
assert_equal '/customers/1/profile', profile_customer_path(:id => '1')
assert_equal '/customers/new/preview', preview_new_customer_path
assert_equal '/customers/1/avatar/thumbnail.jpg', thumbnail_customer_avatar_path(:customer_id => '1', :format => :jpg)
assert_equal '/customers/1/invoices/outstanding', outstanding_customer_invoices_path(:customer_id => '1')
assert_equal '/customers/1/invoices/2/print', print_customer_invoice_path(:customer_id => '1', :id => '2')
assert_equal '/customers/1/invoices/new/preview', preview_new_customer_invoice_path(:customer_id => '1')
assert_equal '/customers/1/notes/new/preview', preview_new_customer_note_path(:customer_id => '1')
assert_equal '/notes/1/print', print_note_path(:id => '1')
assert_equal '/api/customers/recent', recent_api_customers_path
assert_equal '/api/customers/1/profile', profile_api_customer_path(:id => '1')
assert_equal '/api/customers/new/preview', preview_new_api_customer_path
get '/customers/1/invoices/overdue'
assert_equal 'invoices#overdue', @response.body
end
end
def test_shallow_nested_routes_ignore_module
with_test_routes do
get '/errors/1/notices'
assert_equal 'api/notices#index', @response.body
assert_equal '/errors/1/notices', error_notices_path(:error_id => '1')
get '/notices/1'
assert_equal 'api/notices#show', @response.body
assert_equal '/notices/1', notice_path(:id => '1')
end
end
private
def with_test_routes
yield

View File

@ -0,0 +1 @@
<%= form.label :title %>

View File

@ -882,6 +882,33 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :start_year => 2003, :end_year => 2005, :prefix => "date[first]"})
end
def test_select_date_with_separator_and_discard_day
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
expected << " / "
expected << %(<select id="date_first_month" name="date[first][month]">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
expected << %(<input type="hidden" id="date_first_day" name="date[first][day]" value="1" />\n)
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :discard_day => true, :start_year => 2003, :end_year => 2005, :prefix => "date[first]"})
end
def test_select_date_with_separator_discard_month_and_day
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
expected << %(<input type="hidden" id="date_first_month" name="date[first][month]" value="8" />\n)
expected << %(<input type="hidden" id="date_first_day" name="date[first][day]" value="16" />\n)
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :discard_month => true, :discard_day => true, :start_year => 2003, :end_year => 2005, :prefix => "date[first]"})
end
def test_select_datetime
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)

View File

@ -644,6 +644,26 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
def test_form_for_with_remote_without_html
assert_deprecated do
form_for(:post, @post, :remote => true) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
end
expected =
"<form action='http://www.example.com' method='post' data-remote='true'>" +
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
"</form>"
assert_dom_equal expected, output_buffer
end
def test_form_for_without_object
form_for(:post, :html => { :id => 'create-post' }) do |f|
concat f.text_field(:title)

View File

@ -177,17 +177,16 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_option_groups_from_collection_for_select
@continents = [
Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] )
]
assert_dom_equal(
"<optgroup label=\"&lt;Africa&gt;\"><option value=\"&lt;sa&gt;\">&lt;South Africa&gt;</option>\n<option value=\"so\">Somalia</option></optgroup><optgroup label=\"Europe\"><option value=\"dk\" selected=\"selected\">Denmark</option>\n<option value=\"ie\">Ireland</option></optgroup>",
option_groups_from_collection_for_select(@continents, "countries", "continent_name", "country_id", "country_name", "dk")
option_groups_from_collection_for_select(dummy_continents, "countries", "continent_name", "country_id", "country_name", "dk")
)
end
def test_option_groups_from_collection_for_select_returns_html_safe_string
assert option_groups_from_collection_for_select(dummy_continents, "countries", "continent_name", "country_id", "country_name", "dk").html_safe?
end
def test_grouped_options_for_select_with_array
assert_dom_equal(
"<optgroup label=\"North America\"><option value=\"US\">United States</option>\n<option value=\"Canada\">Canada</option></optgroup><optgroup label=\"Europe\"><option value=\"GB\">Great Britain</option>\n<option value=\"Germany\">Germany</option></optgroup>",
@ -824,31 +823,21 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_grouped_collection_select
@continents = [
Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] )
]
@post = Post.new
@post.origin = 'dk'
assert_dom_equal(
%Q{<select id="post_origin" name="post[origin]"><optgroup label="&lt;Africa&gt;"><option value="&lt;sa&gt;">&lt;South Africa&gt;</option>\n<option value="so">Somalia</option></optgroup><optgroup label="Europe"><option value="dk" selected="selected">Denmark</option>\n<option value="ie">Ireland</option></optgroup></select>},
grouped_collection_select("post", "origin", @continents, :countries, :continent_name, :country_id, :country_name)
grouped_collection_select("post", "origin", dummy_continents, :countries, :continent_name, :country_id, :country_name)
)
end
def test_grouped_collection_select_under_fields_for
@continents = [
Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] )
]
@post = Post.new
@post.origin = 'dk'
output_buffer = fields_for :post, @post do |f|
concat f.grouped_collection_select("origin", @continents, :countries, :continent_name, :country_id, :country_name)
concat f.grouped_collection_select("origin", dummy_continents, :countries, :continent_name, :country_id, :country_name)
end
assert_dom_equal(
@ -864,4 +853,9 @@ class FormOptionsHelperTest < ActionView::TestCase
Post.new("Babe went home", "Babe", "To a little house", "shh!"),
Post.new("Cabe went home", "Cabe", "To a little house", "shh!") ]
end
def dummy_continents
[ Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) ]
end
end

View File

@ -218,4 +218,12 @@ module ActionView
end
end
end
class RenderTemplateTest < ActionView::TestCase
test "render template" do
controller.controller_path = "test"
render(:template => "test/calling_partial_with_layout")
assert_template "partial_for_use_in_layout"
end
end
end

View File

@ -19,6 +19,10 @@ class TextHelperTest < ActionView::TestCase
assert_equal 'foobar', output_buffer
end
def test_simple_format_should_be_html_safe
assert simple_format("<b> test with html tags </b>").html_safe?
end
def test_simple_format
assert_equal "<p></p>", simple_format(nil)
@ -36,43 +40,25 @@ class TextHelperTest < ActionView::TestCase
assert_equal %Q(<p class="test">para 1</p>\n\n<p class="test">para 2</p>), simple_format("para 1\n\npara 2", :class => 'test')
end
def test_simple_format_should_be_html_safe
assert simple_format("<b> test with html tags </b>").html_safe?
def test_simple_format_should_sanitize_input_when_sanitize_option_is_not_false
assert_equal "<p><b> test with unsafe string </b></p>", simple_format("<b> test with unsafe string </b><script>code!</script>")
end
def test_simple_format_should_escape_unsafe_input
assert_equal "<p>&lt;b&gt; test with unsafe string &lt;/b&gt;&lt;script&gt;code!&lt;/script&gt;</p>", simple_format("<b> test with unsafe string </b><script>code!</script>")
def test_simple_format_should_not_sanitize_input_when_sanitize_option_is_false
assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>", {}, :sanitize => false)
end
def test_simple_format_should_not_escape_input_if_safe_option
assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>", {}, :safe => true)
def test_truncate_should_not_be_html_safe
assert !truncate("Hello World!", :length => 12).html_safe?
end
def test_simple_format_should_not_escape_safe_input
assert_equal "<p><b> test with safe string </b></p>", simple_format("<b> test with safe string </b>".html_safe)
end
def test_truncate_should_be_html_safe
assert truncate("Hello World!", :length => 12).html_safe?
end
def test_truncate
assert_equal "Hello World!", truncate("Hello World!", :length => 12)
assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12)
end
def test_truncate_should_escape_unsafe_input
assert_equal "Hello &lt...", truncate("Hello <script>code!</script>World!!", :length => 12)
end
def test_truncate_should_not_escape_input_if_safe_option
assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!", :length => 12, :safe => true)
assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!", :length => 12, :safe => true)
end
def test_truncate_should_not_escape_safe_input
assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!".html_safe, :length => 12)
assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!".html_safe, :length => 12)
def test_truncate_should_not_escape_input
assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!", :length => 12)
end
def test_truncate_should_use_default_length_of_30
@ -138,24 +124,17 @@ class TextHelperTest < ActionView::TestCase
assert_equal ' ', highlight(' ', 'blank text is returned verbatim')
end
def test_highlight_should_escape_unsafe_input
def test_highlight_should_sanitize_input
assert_equal(
"This is a <strong class=\"highlight\">beautiful</strong> morning&lt;script&gt;code!&lt;/script&gt;",
"This is a <strong class=\"highlight\">beautiful</strong> morning",
highlight("This is a beautiful morning<script>code!</script>", "beautiful")
)
end
def test_highlight_should_not_escape_input_if_safe_option
def test_highlight_should_not_sanitize_if_sanitize_option_if_false
assert_equal(
"This is a <strong class=\"highlight\">beautiful</strong> morning<script>code!</script>",
highlight("This is a beautiful morning<script>code!</script>", "beautiful", :safe => true)
)
end
def test_highlight_should_not_escape_safe_input
assert_equal(
"This is a <strong class=\"highlight\">beautiful</strong> morning<script>code!</script>",
highlight("This is a beautiful morning<script>code!</script>".html_safe, "beautiful")
highlight("This is a beautiful morning<script>code!</script>", "beautiful", :sanitize => false)
)
end
@ -189,23 +168,23 @@ class TextHelperTest < ActionView::TestCase
def test_highlight_with_html
assert_equal(
"&lt;p&gt;This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day&lt;/p&gt;",
"<p>This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
highlight("<p>This is a beautiful morning, but also a beautiful day</p>", "beautiful")
)
assert_equal(
"&lt;p&gt;This is a &lt;em&gt;<strong class=\"highlight\">beautiful</strong>&lt;/em&gt; morning, but also a <strong class=\"highlight\">beautiful</strong> day&lt;/p&gt;",
"<p>This is a <em><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
highlight("<p>This is a <em>beautiful</em> morning, but also a beautiful day</p>", "beautiful")
)
assert_equal(
"&lt;p&gt;This is a &lt;em class=&quot;error&quot;&gt;<strong class=\"highlight\">beautiful</strong>&lt;/em&gt; morning, but also a <strong class=\"highlight\">beautiful</strong> &lt;span class=&quot;last&quot;&gt;day&lt;/span&gt;&lt;/p&gt;",
"<p>This is a <em class=\"error\"><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> <span class=\"last\">day</span></p>",
highlight("<p>This is a <em class=\"error\">beautiful</em> morning, but also a beautiful <span class=\"last\">day</span></p>", "beautiful")
)
assert_equal(
"&lt;p class=&quot;<strong class=\"highlight\">beautiful</strong>&quot;&gt;This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day&lt;/p&gt;",
"<p class=\"beautiful\">This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
highlight("<p class=\"beautiful\">This is a beautiful morning, but also a beautiful day</p>", "beautiful")
)
assert_equal(
"&lt;p&gt;This is a <strong class=\"highlight\">beautiful</strong> &lt;a href=&quot;http://example.com/<strong class=\"highlight\">beautiful</strong>#top?what=<strong class=\"highlight\">beautiful</strong>%20morning&amp;when=now+then&quot;&gt;morning&lt;/a&gt;, but also a <strong class=\"highlight\">beautiful</strong> day&lt;/p&gt;",
"<p>This is a <strong class=\"highlight\">beautiful</strong> <a href=\"http://example.com/beautiful#top?what=beautiful%20morning&amp;when=now+then\">morning</a>, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
highlight("<p>This is a beautiful <a href=\"http://example.com/beautiful\#top?what=beautiful%20morning&when=now+then\">morning</a>, but also a beautiful day</p>", "beautiful")
)
end
@ -217,6 +196,10 @@ class TextHelperTest < ActionView::TestCase
assert_nil excerpt("This is a beautiful morning", "day")
end
def test_excerpt_should_not_be_html_safe
assert !excerpt('This is a beautiful! morning', 'beautiful', 5).html_safe?
end
def test_excerpt_in_borderline_cases
assert_equal("", excerpt("", "", 0))
assert_equal("a", excerpt("a", "a", 0))
@ -323,9 +306,13 @@ class TextHelperTest < ActionView::TestCase
end
end
def generate_result(link_text, href = nil)
def generate_result(link_text, href = nil, escape = false)
href ||= link_text
%{<a href="#{CGI::escapeHTML href}">#{CGI::escapeHTML link_text}</a>}
if escape
%{<a href="#{CGI::escapeHTML href}">#{CGI::escapeHTML link_text}</a>}
else
%{<a href="#{href}">#{link_text}</a>}
end
end
def test_auto_link_should_be_html_safe
@ -430,19 +417,14 @@ class TextHelperTest < ActionView::TestCase
assert_equal %(<p>#{link10_result} Link</p>), auto_link("<p>#{link10_raw} Link</p>")
end
def test_auto_link_should_sanitize_unsafe_input
def test_auto_link_should_sanitize_input_when_sanitize_option_is_not_false
link_raw = %{http://www.rubyonrails.com?id=1&num=2}
assert_equal %{<a href="http://www.rubyonrails.com?id=1&amp;num=2">http://www.rubyonrails.com?id=1&amp;num=2</a>}, auto_link(link_raw)
assert_equal %{<a href="http://www.rubyonrails.com?id=1&num=2">http://www.rubyonrails.com?id=1&num=2</a>}, auto_link(link_raw)
end
def test_auto_link_should_sanitize_unsafe_input
def test_auto_link_should_not_sanitize_input_when_sanitize_option_is_false
link_raw = %{http://www.rubyonrails.com?id=1&num=2}
assert_equal %{<a href="http://www.rubyonrails.com?id=1&num=2">http://www.rubyonrails.com?id=1&num=2</a>}, auto_link(link_raw, :safe => true)
end
def test_auto_link_should_not_sanitize_safe_input
link_raw = %{http://www.rubyonrails.com?id=1&num=2}
assert_equal %{<a href="http://www.rubyonrails.com?id=1&num=2">http://www.rubyonrails.com?id=1&num=2</a>}, auto_link(link_raw.html_safe)
assert_equal %{<a href="http://www.rubyonrails.com?id=1&num=2">http://www.rubyonrails.com?id=1&num=2</a>}, auto_link(link_raw, :sanitize => false)
end
def test_auto_link_other_protocols
@ -453,6 +435,7 @@ class TextHelperTest < ActionView::TestCase
z39_scheme = 'z39.50r://host:696/db'
chrome_scheme = 'chrome://package/section/path'
view_source = 'view-source:http://en.wikipedia.org/wiki/URI_scheme'
assert_equal generate_result(file_scheme), auto_link(file_scheme)
assert_equal generate_result(z39_scheme), auto_link(z39_scheme)
assert_equal generate_result(chrome_scheme), auto_link(chrome_scheme)
assert_equal generate_result(view_source), auto_link(view_source)

3
activemodel/Rakefile Executable file → Normal file
View File

@ -1,5 +1,8 @@
dir = File.dirname(__FILE__)
gem 'rdoc', '= 2.2'
require 'rdoc'
require 'rake/testtask'
task :default => :test

View File

@ -3,6 +3,7 @@ require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/keys'
require 'active_model/errors'
require 'active_model/validations/callbacks'
module ActiveModel
@ -164,8 +165,7 @@ module ActiveModel
def valid?(context = nil)
current_context, self.validation_context = validation_context, context
errors.clear
_run_validate_callbacks
errors.empty?
run_validations!
ensure
self.validation_context = current_context
end
@ -194,6 +194,13 @@ module ActiveModel
# end
#
alias :read_attribute_for_validation :send
protected
def run_validations!
_run_validate_callbacks
errors.empty?
end
end
end

View File

@ -0,0 +1,57 @@
require 'active_support/callbacks'
module ActiveModel
module Validations
module Callbacks
# == Active Model Validation callbacks
#
# Provides an interface for any class to have <tt>before_validation</tt> and
# <tt>after_validation</tt> callbacks.
#
# First, extend ActiveModel::Callbacks from the class you are creating:
#
# class MyModel
# include ActiveModel::Validations::Callbacks
#
# before_validation :do_stuff_before_validation
# after_validation :do_tuff_after_validation
# end
#
# Like other before_* callbacks if <tt>before_validation</tt> returns false
# then <tt>valid?</tt> will not be called.
extend ActiveSupport::Concern
included do
include ActiveSupport::Callbacks
define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
end
module ClassMethods
def before_validation(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
options[:if] = Array.wrap(options[:if])
options[:if] << "self.validation_context == :#{options[:on]}"
end
set_callback(:validation, :before, *args, &block)
end
def after_validation(*args, &block)
options = args.extract_options!
options[:prepend] = true
options[:if] = Array.wrap(options[:if])
options[:if] << "!halted && value != false"
options[:if] << "self.validation_context == :#{options[:on]}" if options[:on]
set_callback(:validation, :after, *(args << options), &block)
end
end
protected
# Overwrite run validations to include callbacks.
def run_validations!
_run_validation_callbacks { super }
end
end
end
end

View File

@ -0,0 +1,77 @@
# encoding: utf-8
require 'cases/helper'
class Dog
include ActiveModel::Validations
include ActiveModel::Validations::Callbacks
attr_accessor :name, :history
def history
@history ||= []
end
end
class DogWithMethodCallbacks < Dog
before_validation :set_before_validation_marker
after_validation :set_after_validation_marker
def set_before_validation_marker; self.history << 'before_validation_marker'; end
def set_after_validation_marker; self.history << 'after_validation_marker' ; end
end
class DogValidtorsAreProc < Dog
before_validation { self.history << 'before_validation_marker' }
after_validation { self.history << 'after_validation_marker' }
end
class DogWithTwoValidators < Dog
before_validation { self.history << 'before_validation_marker1' }
before_validation { self.history << 'before_validation_marker2' }
end
class DogValidatorReturningFalse < Dog
before_validation { false }
before_validation { self.history << 'before_validation_marker2' }
end
class DogWithMissingName < Dog
before_validation { self.history << 'before_validation_marker' }
validates_presence_of :name
end
class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase
def test_before_validation_and_after_validation_callbacks_should_be_called
d = DogWithMethodCallbacks.new
d.valid?
assert_equal ['before_validation_marker', 'after_validation_marker'], d.history
end
def test_before_validation_and_after_validation_callbacks_should_be_called_with_proc
d = DogValidtorsAreProc.new
d.valid?
assert_equal ['before_validation_marker', 'after_validation_marker'], d.history
end
def test_before_validation_and_after_validation_callbacks_should_be_called_in_declared_order
d = DogWithTwoValidators.new
d.valid?
assert_equal ['before_validation_marker1', 'before_validation_marker2'], d.history
end
def test_further_callbacks_should_not_be_called_if_before_validation_returns_false
d = DogValidatorReturningFalse.new
output = d.valid?
assert_equal [], d.history
assert_equal false, output
end
def test_validation_test_should_be_done
d = DogWithMissingName.new
output = d.valid?
assert_equal ['before_validation_marker'], d.history
assert_equal false, output
end
end

View File

@ -1,3 +1,8 @@
*Rails 3.0.0 [RC1] (unreleased)*
* PostgreSQL: ensure the database time zone matches Ruby's time zone. #4895 [Aaron Patterson]
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
* Fixed that ActiveRecord::Base.compute_type would swallow NoMethodError #4751 [Andrew Bloomgarden, Andrew White]

View File

@ -32,5 +32,7 @@ being initialized - you can initialize the schema with:
rake test_mysql TEST=test/cases/aaa_create_tables_test.rb
The incantation for running a particular test looks like this
ruby -w -I"lib:test:test/connections/native_postgresql" test/cases/datatype_test_postgresql.rb -n test_timestamp_with_zone_values_without_rails_time_zone_support

View File

@ -1,4 +1,5 @@
require 'rubygems'
gem 'rdoc', '= 2.2'
require 'rdoc'
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
@ -222,5 +223,5 @@ end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/ar", "doc").upload
end

0
activerecord/examples/performance.rb Executable file → Normal file
View File

2
activerecord/lib/active_record/associations.rb Executable file → Normal file
View File

@ -2066,7 +2066,7 @@ module ActiveRecord
unless klass.descends_from_active_record?
sti_column = aliased_table[klass.inheritance_column]
sti_condition = sti_column.eq(klass.sti_name)
klass.send(:subclasses).each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
klass.descendants.each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
@join << sti_condition
end

View File

@ -390,7 +390,11 @@ module ActiveRecord
begin
if !loaded?
if @target.is_a?(Array) && @target.any?
@target = find_target + @target.find_all {|t| t.new_record? }
@target = find_target.map do |f|
i = @target.index(f)
t = @target.delete_at(i) if i
(t && t.changed?) ? t : f
end + @target
else
@target = find_target
end

36
activerecord/lib/active_record/base.rb Executable file → Normal file
View File

@ -2,6 +2,7 @@ require 'yaml'
require 'set'
require 'active_support/benchmarkable'
require 'active_support/dependencies'
require 'active_support/descendants_tracker'
require 'active_support/time'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/class/attribute_accessors'
@ -276,28 +277,6 @@ module ActiveRecord #:nodoc:
# on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
cattr_accessor :logger, :instance_writer => false
def self.inherited(child) #:nodoc:
@@subclasses[self] ||= []
@@subclasses[self] << child
super
end
def self.reset_subclasses #:nodoc:
nonreloadables = []
subclasses.each do |klass|
unless ActiveSupport::Dependencies.autoloaded? klass
nonreloadables << klass
next
end
klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) }
klass.instance_methods(false).each { |m| klass.send :undef_method, m }
end
@@subclasses = {}
nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass }
end
@@subclasses = {}
##
# :singleton-method:
# Contains the database configuration - as is typically stored in config/database.yml -
@ -812,7 +791,7 @@ module ActiveRecord #:nodoc:
end
def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
descendants.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
end
def attribute_method?(attribute)
@ -977,7 +956,7 @@ module ActiveRecord #:nodoc:
def type_condition
sti_column = arel_table[inheritance_column]
condition = sti_column.eq(sti_name)
subclasses.each{|subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) }
descendants.each { |subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) }
condition
end
@ -1167,14 +1146,6 @@ module ActiveRecord #:nodoc:
with_scope(method_scoping, :overwrite, &block)
end
# Returns a list of all subclasses of this class, meaning all descendants.
def subclasses
@@subclasses[self] ||= []
@@subclasses[self] + @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
end
public :subclasses
# Sets the default options for the model. The format of the
# <tt>options</tt> argument is the same as in find.
#
@ -1902,6 +1873,7 @@ module ActiveRecord #:nodoc:
extend ActiveModel::Naming
extend QueryCache::ClassMethods
extend ActiveSupport::Benchmarkable
extend ActiveSupport::DescendantsTracker
include ActiveModel::Conversion
include Validations

View File

@ -236,8 +236,7 @@ module ActiveRecord
included do
extend ActiveModel::Callbacks
define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
include ActiveModel::Validations::Callbacks
define_model_callbacks :initialize, :find, :only => :after
define_model_callbacks :save, :create, :update, :destroy
@ -251,29 +250,6 @@ module ActiveRecord
send(meth.to_sym, meth.to_sym)
end
end
def before_validation(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
options[:if] = Array.wrap(options[:if])
options[:if] << "@_on_validate == :#{options[:on]}"
end
set_callback(:validation, :before, *args, &block)
end
def after_validation(*args, &block)
options = args.extract_options!
options[:prepend] = true
options[:if] = Array.wrap(options[:if])
options[:if] << "!halted && value != false"
options[:if] << "@_on_validate == :#{options[:on]}" if options[:on]
set_callback(:validation, :after, *(args << options), &block)
end
end
def valid?(*) #:nodoc:
@_on_validate = new_record? ? :create : :update
_run_validation_callbacks { super }
end
def destroy #:nodoc:
@ -288,6 +264,7 @@ module ActiveRecord
end
private
def create_or_update #:nodoc:
_run_save_callbacks { super }
end

View File

@ -304,7 +304,7 @@ module ActiveRecord
begin
record.rolledback!(rollback)
rescue Exception => e
record.logger.error(e) if record.respond_to?(:logger)
record.logger.error(e) if record.respond_to?(:logger) && record.logger
end
end
end
@ -319,7 +319,7 @@ module ActiveRecord
begin
record.committed!
rescue Exception => e
record.logger.error(e) if record.respond_to?(:logger)
record.logger.error(e) if record.respond_to?(:logger) && record.logger
end
end
end

View File

View File

@ -927,7 +927,12 @@ module ActiveRecord
# If using Active Record's time zone support configure the connection to return
# TIMESTAMP WITH ZONE types in UTC.
execute("SET time zone 'UTC'") if ActiveRecord::Base.default_timezone == :utc
if ActiveRecord::Base.default_timezone == :utc
execute("SET time zone 'UTC'")
else
offset = Time.local(2000).utc_offset / 3600
execute("SET time zone '#{offset}'")
end
end
# Returns the current ID of a table's sequence.

View File

@ -107,8 +107,9 @@ module ActiveRecord
end
protected
def observed_subclasses
observed_classes.sum([]) { |klass| klass.send(:subclasses) }
observed_classes.sum([]) { |klass| klass.send(:descendants) }
end
def observe_callbacks?

View File

@ -69,7 +69,6 @@ module ActiveRecord
unless app.config.cache_classes
ActiveSupport.on_load(:active_record) do
ActionDispatch::Callbacks.after do
ActiveRecord::Base.reset_subclasses
ActiveRecord::Base.clear_reloadable_connections!
end
end

View File

@ -116,6 +116,70 @@ module ActiveRecord
def build_arel
arel = table
arel = build_joins(arel, @joins_values) if @joins_values.present?
@where_values.uniq.each do |where|
next if where.blank?
case where
when Arel::SqlLiteral
arel = arel.where(where)
else
sql = where.is_a?(String) ? where : where.to_sql
arel = arel.where(Arel::SqlLiteral.new("(#{sql})"))
end
end
arel = arel.having(*@having_values.uniq.select{|h| h.present?})
arel = arel.take(@limit_value) if @limit_value.present?
arel = arel.skip(@offset_value) if @offset_value.present?
arel = arel.group(*@group_values.uniq.select{|g| g.present?})
arel = arel.order(*@order_values.uniq.select{|o| o.present?}.map(&:to_s))
selects = @select_values.uniq
if selects.present?
selects.each do |s|
@implicit_readonly = false
arel = arel.project(s) if s.present?
end
else
arel = arel.project(@klass.quoted_table_name + '.*')
end
arel = @from_value.present? ? arel.from(@from_value) : arel.from(@klass.quoted_table_name)
case @lock_value
when TrueClass
arel = arel.lock
when String
arel = arel.lock(@lock_value)
end if @lock_value.present?
arel
end
def build_where(*args)
return if args.blank?
opts = args.first
case opts
when String, Array
@klass.send(:sanitize_sql, args.size > 1 ? args : opts)
when Hash
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
PredicateBuilder.new(table.engine).build_from_hash(attributes, table)
else
opts
end
end
private
def build_joins(relation, joins)
joined_associations = []
association_joins = []
@ -150,77 +214,13 @@ module ActiveRecord
to_join.each do |tj|
unless joined_associations.detect {|ja| ja[0] == tj[0] && ja[1] == tj[1] && ja[2] == tj[2] }
joined_associations << tj
arel = arel.join(tj[0], tj[1]).on(*tj[2])
relation = relation.join(tj[0], tj[1]).on(*tj[2])
end
end
arel = arel.join(custom_joins)
@where_values.uniq.each do |where|
next if where.blank?
case where
when Arel::SqlLiteral
arel = arel.where(where)
else
sql = where.is_a?(String) ? where : where.to_sql
arel = arel.where(Arel::SqlLiteral.new("(#{sql})"))
end
end
@having_values.uniq.each do |h|
arel = h.is_a?(String) ? arel.having(h) : arel.having(*h)
end
arel = arel.take(@limit_value) if @limit_value.present?
arel = arel.skip(@offset_value) if @offset_value.present?
arel = arel.group(*@group_values.uniq.select{|g| g.present?})
arel = arel.order(*@order_values.uniq.select{|o| o.present?}.map(&:to_s))
selects = @select_values.uniq
quoted_table_name = @klass.quoted_table_name
if selects.present?
selects.each do |s|
@implicit_readonly = false
arel = arel.project(s) if s.present?
end
else
arel = arel.project(quoted_table_name + '.*')
end
arel = @from_value.present? ? arel.from(@from_value) : arel.from(quoted_table_name)
case @lock_value
when TrueClass
arel = arel.lock
when String
arel = arel.lock(@lock_value)
end if @lock_value.present?
arel
relation.join(custom_joins)
end
def build_where(*args)
return if args.blank?
opts = args.first
case opts
when String, Array
@klass.send(:sanitize_sql, args.size > 1 ? args : opts)
when Hash
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
PredicateBuilder.new(table.engine).build_from_hash(attributes, table)
else
opts
end
end
private
def apply_modules(modules)
values = Array.wrap(modules)
@extensions += values if values.present?

View File

@ -322,6 +322,7 @@ module ActiveRecord
if @_start_transaction_state[:level] < 1
restore_state = remove_instance_variable(:@_start_transaction_state)
if restore_state
@attributes = @attributes.dup if @attributes.frozen?
@new_record = restore_state[:new_record]
@destroyed = restore_state[:destroyed]
if restore_state[:id]

View File

@ -52,12 +52,12 @@ module ActiveRecord
# Runs all the specified validations and returns true if no errors were added otherwise false.
def valid?(context = nil)
context ||= (new_record? ? :create : :update)
super(context)
output = super(context)
deprecated_callback_method(:validate)
deprecated_callback_method(:"validate_on_#{context}")
errors.empty?
errors.empty? && output
end
protected

View File

@ -1,11 +1,15 @@
class <%= migration_class_name %> < ActiveRecord::Migration
def self.up<% attributes.each do |attribute| %>
<%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%>
<%- end %>
<%- if migration_action -%>
<%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %>
<%- end -%>
<%- end -%>
end
def self.down<% attributes.reverse.each do |attribute| %>
<%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end -%>
<%- end %>
<%- if migration_action -%>
<%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %>
<%- end -%>
<%- end -%>
end
end

View File

@ -65,15 +65,14 @@ class AdapterTest < ActiveRecord::TestCase
end
def test_not_specifying_database_name_for_cross_database_selects
assert_nothing_raised do
ActiveRecord::Base.establish_connection({
:adapter => 'mysql',
:username => 'rails'
})
ActiveRecord::Base.connection.execute "SELECT activerecord_unittest.pirates.*, activerecord_unittest2.courses.* FROM activerecord_unittest.pirates, activerecord_unittest2.courses"
begin
assert_nothing_raised do
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
ActiveRecord::Base.connection.execute "SELECT activerecord_unittest.pirates.*, activerecord_unittest2.courses.* FROM activerecord_unittest.pirates, activerecord_unittest2.courses"
end
ensure
ActiveRecord::Base.establish_connection 'arunit'
end
ActiveRecord::Base.establish_connection 'arunit'
end
end

View File

@ -712,7 +712,6 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
assert_raise(RuntimeError) { assert !@pirate.save }
assert before.first.frozen? # the first child was indeed destroyed
assert_equal before, @pirate.reload.send(association_name)
end

4
activerecord/test/cases/base_test.rb Executable file → Normal file
View File

@ -2076,10 +2076,6 @@ class BasicsTest < ActiveRecord::TestCase
assert !SubStiPost.descends_from_active_record?
end
def test_base_subclasses_is_public_method
assert ActiveRecord::Base.public_methods.map(&:to_sym).include?(:subclasses)
end
def test_find_on_abstract_base_class_doesnt_use_type_condition
old_class = LooseDescendant
Object.send :remove_const, :LooseDescendant

0
activerecord/test/cases/counter_cache_test.rb Executable file → Normal file
View File

View File

@ -1032,7 +1032,7 @@ if ActiveRecord::Base.connection.supports_migrations?
elsif current_adapter?(:SQLiteAdapter)
# - SQLite3 stores a float, in violation of SQL
assert_kind_of BigDecimal, b.value_of_e
assert_equal BigDecimal("2.71828182845905"), b.value_of_e
assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
else
# - SQL standard is an integer
assert_kind_of Fixnum, b.value_of_e

View File

@ -466,6 +466,27 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal 'Grace OMalley', @child_1.reload.name
end
def test_should_not_overwrite_unsaved_updates_when_loading_association
@pirate.reload
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
assert_equal 'Grace OMalley', @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.name
end
def test_should_preserve_order_when_not_overwriting_unsaved_updates
@pirate.reload
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
assert_equal @child_1.id, @pirate.send(@association_name).send(:load_target).first.id
end
def test_should_refresh_saved_records_when_not_overwriting_unsaved_updates
@pirate.reload
record = @pirate.class.reflect_on_association(@association_name).klass.new(:name => 'Grace OMalley')
@pirate.send(@association_name) << record
record.save!
@pirate.send(@association_name).last.update_attributes!(:name => 'Polly')
assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name
end
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
@child_1.stubs(:id).returns('ABC1X')
@child_2.stubs(:id).returns('ABC2X')

View File

@ -221,20 +221,28 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
assert_equal 2, @first.rollbacks
end
def test_after_transaction_callbacks_should_not_raise_errors
def test_after_transaction_callbacks_should_prevent_callbacks_from_being_called
def @first.last_after_transaction_error=(e); @last_transaction_error = e; end
def @first.last_after_transaction_error; @last_transaction_error; end
@first.after_commit_block{|r| r.last_after_transaction_error = :commit; raise "fail!";}
@first.after_rollback_block{|r| r.last_after_transaction_error = :rollback; raise "fail!";}
@first.save!
assert_equal :commit, @first.last_after_transaction_error
@second.after_commit_block{|r| r.history << :after_commit}
@second.after_rollback_block{|r| r.history << :after_rollback}
Topic.transaction do
@first.save!
@second.save!
end
assert_equal :commit, @first.last_after_transaction_error
assert_equal [:after_commit], @second.history
@second.history.clear
Topic.transaction do
@first.save!
@second.save!
raise ActiveRecord::Rollback
end
assert_equal :rollback, @first.last_after_transaction_error
assert_equal [:after_rollback], @second.history
end
end

View File

@ -1,7 +1,7 @@
class Pirate < ActiveRecord::Base
belongs_to :parrot, :validate => true
belongs_to :non_validated_parrot, :class_name => 'Parrot'
has_and_belongs_to_many :parrots, :validate => true
has_and_belongs_to_many :parrots, :validate => true, :order => 'parrots.id ASC'
has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot'
has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot",
:before_add => :log_before_add,
@ -21,7 +21,7 @@ class Pirate < ActiveRecord::Base
has_one :ship
has_one :update_only_ship, :class_name => 'Ship'
has_one :non_validated_ship, :class_name => 'Ship'
has_many :birds
has_many :birds, :order => 'birds.id ASC'
has_many :birds_with_method_callbacks, :class_name => "Bird",
:before_add => :log_before_add,
:after_add => :log_after_add,

View File

@ -1,4 +1,5 @@
require 'rubygems'
gem 'rdoc', '= 2.2'
require 'rdoc'
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
@ -83,5 +84,5 @@ end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/ar", "doc").upload
end

View File

@ -1,5 +1,9 @@
*Rails 3.0.0 [Release Candidate] (unreleased)*
* Added ActiveSupport::FileUpdateChecker to execute a block only if a set of files changed, used by Router and I18n locale files. [José Valim]
* Added ActiveSupport::DescendantsTracker to track descendants with support to constants reloading. [José Valim]
* ActiveSupport::OrderedHash#merge and #merge! accept a block. #4838 [Paul Mucur, fxn]
* Date#since, #ago, #beginning_of_day, #end_of_day, and #xmlschema honor now the user time zone if set. [Geoff Buesing]

View File

@ -1,3 +1,5 @@
gem 'rdoc', '= 2.2'
require 'rdoc'
require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/gempackagetask'
@ -47,5 +49,5 @@ end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/as", "doc").upload
Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/as", "doc").upload
end

0
activesupport/bin/generate_tables Executable file → Normal file
View File

View File

@ -39,6 +39,9 @@ require "active_support/dependencies/autoload"
module ActiveSupport
extend ActiveSupport::Autoload
autoload :DescendantsTracker
autoload :FileUpdateChecker
# TODO: Narrow this list down
eager_autoload do
autoload :BacktraceCleaner

View File

@ -1,6 +1,6 @@
require 'active_support/descendants_tracker'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/inheritable_attributes'
require 'active_support/core_ext/class/subclasses'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
@ -85,6 +85,10 @@ module ActiveSupport
module Callbacks
extend Concern
included do
extend ActiveSupport::DescendantsTracker
end
def run_callbacks(kind, *args, &block)
send("_run_#{kind}_callbacks", *args, &block)
end
@ -428,7 +432,7 @@ module ActiveSupport
options = filters.last.is_a?(Hash) ? filters.pop : {}
filters.unshift(block) if block
([self] + self.descendents).each do |target|
([self] + self.descendants).each do |target|
chain = target.send("_#{name}_callbacks")
yield chain, type, filters, options
target.__define_runner(name)
@ -502,7 +506,7 @@ module ActiveSupport
def reset_callbacks(symbol)
callbacks = send("_#{symbol}_callbacks")
self.descendents.each do |target|
self.descendants.each do |target|
chain = target.send("_#{symbol}_callbacks")
callbacks.each { |c| chain.delete(c) }
target.__define_runner(symbol)

View File

@ -11,9 +11,9 @@ class Class #:nodoc:
# Rubinius
if defined?(Class.__subclasses__)
def descendents
def descendants
subclasses = []
__subclasses__.each {|k| subclasses << k; subclasses.concat k.descendents }
__subclasses__.each {|k| subclasses << k; subclasses.concat k.descendants }
subclasses
end
else
@ -21,7 +21,7 @@ class Class #:nodoc:
begin
ObjectSpace.each_object(Class.new) {}
def descendents
def descendants
subclasses = []
ObjectSpace.each_object(class << self; self; end) do |k|
subclasses << k unless k == self
@ -30,7 +30,7 @@ class Class #:nodoc:
end
# JRuby
rescue StandardError
def descendents
def descendants
subclasses = []
ObjectSpace.each_object(Class) do |k|
subclasses << k if k < self
@ -48,7 +48,7 @@ class Class #:nodoc:
def self.subclasses_of(*superclasses) #:nodoc:
subclasses = []
superclasses.each do |klass|
subclasses.concat klass.descendents.select {|k| k.anonymous? || k.reachable?}
subclasses.concat klass.descendants.select {|k| k.anonymous? || k.reachable?}
end
subclasses
end

View File

@ -12,6 +12,8 @@ class DateTime
#
# DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
def in_time_zone(zone = ::Time.zone)
return self unless zone
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone))
end
end

View File

View File

@ -73,6 +73,8 @@ class Time
#
# Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
def in_time_zone(zone = ::Time.zone)
return self unless zone
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone))
end
end

View File

@ -0,0 +1,39 @@
require 'active_support/dependencies'
module ActiveSupport
# This module provides an internal implementation to track descendants
# which is faster than iterating through ObjectSpace.
module DescendantsTracker
@@descendants = Hash.new { |h, k| h[k] = [] }
def self.descendants
@@descendants
end
def self.clear
@@descendants.each do |klass, descendants|
if ActiveSupport::Dependencies.autoloaded?(klass)
@@descendants.delete(klass)
else
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
end
end
end
def inherited(base)
self.direct_descendants << base
super
end
def direct_descendants
@@descendants[self]
end
def descendants
@@descendants[self].inject([]) do |descendants, klass|
descendants << klass
descendants.concat klass.descendants
end
end
end
end

View File

@ -0,0 +1,37 @@
module ActiveSupport
# This class is responsible to track files and invoke the given block
# whenever one of these files are changed. For example, this class
# is used by Rails to reload routes whenever they are changed upon
# a new request.
#
# routes_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
# paths.each { |p| load(p) }
# Rails::Application.routes.reload!
# end
#
# ActionDispatch::Callbacks.to_prepare do
# routes_reloader.execute_if_updated
# end
#
class FileUpdateChecker
attr_reader :paths, :last_update_at
def initialize(paths, calculate=false, &block)
@paths = paths
@block = block
@last_update_at = updated_at if calculate
end
def updated_at
paths.map { |path| File.stat(path).mtime }.max
end
def execute_if_updated
current_update_at = self.updated_at
if @last_update_at != current_update_at
@last_update_at = current_update_at
@block.call
end
end
end
end

View File

@ -4,5 +4,6 @@ rescue LoadError => e
$stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
ActiveSupport.run_load_hooks(:i18n)

View File

@ -0,0 +1,80 @@
require "active_support"
require "rails"
require "active_support/file_update_checker"
module I18n
class Railtie < Rails::Railtie
config.i18n = ActiveSupport::OrderedOptions.new
config.i18n.railties_load_path = []
config.i18n.load_path = []
config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
def self.reloader
@reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! }
end
# Add I18n::Railtie.reloader to ActionDispatch callbacks. Since, at this
# point, no path was added to the reloader, I18n.reload! is not triggered
# on to_prepare callbacks. This will only happen on the config.after_initialize
# callback below.
initializer "i18n.callbacks" do
ActionDispatch::Callbacks.to_prepare do
I18n::Railtie.reloader.execute_if_updated
end
end
# Set the i18n configuration only after initialization since a lot of
# configuration is still usually done in application initializers.
config.after_initialize do |app|
fallbacks = app.config.i18n.delete(:fallbacks)
app.config.i18n.each do |setting, value|
case setting
when :railties_load_path
app.config.i18n.load_path.unshift(*value)
when :load_path
I18n.load_path += value
else
I18n.send("#{setting}=", value)
end
end
init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
reloader.paths.concat I18n.load_path
reloader.execute_if_updated
end
protected
def self.include_fallbacks_module
I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
end
def self.init_fallbacks(fallbacks)
include_fallbacks_module
args = case fallbacks
when ActiveSupport::OrderedOptions
[*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
when Hash, Array
Array.wrap(fallbacks)
else # TrueClass
[]
end
I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
end
def self.validate_fallbacks(fallbacks)
case fallbacks
when ActiveSupport::OrderedOptions
!fallbacks.empty?
when TrueClass, Array, Hash
true
else
raise "Unexpected fallback type #{fallbacks.inspect}"
end
end
end
end

View File

@ -1,4 +1,4 @@
require 'yajl-ruby' unless defined?(Yajl)
require 'yajl' unless defined?(Yajl)
module ActiveSupport
module JSON

View File

@ -50,10 +50,6 @@ module ActiveSupport #:nodoc:
end
end
def <=>(other)
@wrapped_string <=> other
end
# Forward all undefined methods to the wrapped string.
def method_missing(method, *args, &block)
if method.to_s =~ /!$/
@ -87,6 +83,16 @@ module ActiveSupport #:nodoc:
include Comparable
# Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> depending on whether the Chars object is to be sorted before,
# equal or after the object on the right side of the operation. It accepts any object that implements +to_s+.
# See <tt>String#<=></tt> for more details.
#
# Example:
# 'é'.mb_chars <=> 'ü'.mb_chars #=> -1
def <=>(other)
@wrapped_string <=> other.to_s
end
if RUBY_VERSION < "1.9"
# Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
# +false+ otherwise.
@ -94,16 +100,6 @@ module ActiveSupport #:nodoc:
$KCODE == 'UTF8' && consumes?(string)
end
# Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> depending on whether the Chars object is to be sorted before,
# equal or after the object on the right side of the operation. It accepts any object that implements +to_s+.
# See <tt>String#<=></tt> for more details.
#
# Example:
# 'é'.mb_chars <=> 'ü'.mb_chars #=> -1
def <=>(other)
@wrapped_string <=> other.to_s
end
# Returns a new Chars object containing the _other_ object concatenated to the string.
#
# Example:
@ -375,6 +371,16 @@ module ActiveSupport #:nodoc:
(slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
end
# Capitalizes the first letter of every word, when possible.
#
# Example:
# "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
# "日本語".mb_chars.titleize # => "日本語"
def titleize
chars(downcase.to_s.gsub(/\b('?[\S])/u) { Unicode.apply_mapping $1, :uppercase_mapping })
end
alias_method :titlecase, :titleize
# Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
# passing strings to databases and validations.
#

View File

@ -1,5 +1,6 @@
require "active_support"
require "rails"
require "active_support/i18n_railtie"
module ActiveSupport
class Railtie < Rails::Railtie
@ -26,75 +27,4 @@ module ActiveSupport
Time.zone_default = zone_default
end
end
end
module I18n
class Railtie < Rails::Railtie
config.i18n = ActiveSupport::OrderedOptions.new
config.i18n.railties_load_path = []
config.i18n.load_path = []
config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
initializer "i18n.initialize" do
ActiveSupport.on_load(:i18n) do
I18n.reload!
ActionDispatch::Callbacks.to_prepare do
I18n.reload!
end
end
end
# Set the i18n configuration from config.i18n but special-case for
# the load_path which should be appended to what's already set instead of overwritten.
config.after_initialize do |app|
fallbacks = app.config.i18n.delete(:fallbacks)
app.config.i18n.each do |setting, value|
case setting
when :railties_load_path
app.config.i18n.load_path.unshift(*value)
when :load_path
I18n.load_path += value
else
I18n.send("#{setting}=", value)
end
end
init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
I18n.reload!
end
class << self
protected
def init_fallbacks(fallbacks)
include_fallbacks_module
args = case fallbacks
when ActiveSupport::OrderedOptions
[*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
when Hash, Array
Array.wrap(fallbacks)
else # TrueClass
[]
end
I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
end
def include_fallbacks_module
I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
end
def validate_fallbacks(fallbacks)
case fallbacks
when ActiveSupport::OrderedOptions
!fallbacks.empty?
when TrueClass, Array, Hash
true
else
raise "Unexpected fallback type #{fallbacks.inspect}"
end
end
end
end
end

View File

@ -260,14 +260,8 @@ begin
end
protected
if GC.respond_to?(:enable_stats)
def with_gc_stats
GC.enable_stats
yield
ensure
GC.disable_stats
end
elsif defined?(GC::Profiler)
# Ruby 1.9 + extented GC profiler patch
if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
def with_gc_stats
GC.start
GC.disable
@ -277,6 +271,16 @@ begin
GC::Profiler.disable
GC.enable
end
# Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker)
elsif GC.respond_to?(:enable_stats)
def with_gc_stats
GC.enable_stats
yield
ensure
GC.disable_stats
end
else
def with_gc_stats
yield
@ -319,7 +323,7 @@ begin
def initialize(*args)
# FIXME: yeah my CPU is 2.33 GHz
RubyProf.cpu_frequency = 2.33e9
RubyProf.cpu_frequency = 2.33e9 unless RubyProf.cpu_frequency > 0
super
end
@ -331,38 +335,8 @@ begin
class Memory < Base
Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
# ruby-prof wrapper
if RubyProf.respond_to?(:measure_memory)
def measure
RubyProf.measure_memory / 1024.0
end
# Ruby 1.8 + railsbench patch
elsif GC.respond_to?(:allocated_size)
def measure
GC.allocated_size / 1024.0
end
# Ruby 1.8 + lloyd patch
elsif GC.respond_to?(:heap_info)
def measure
GC.heap_info['heap_current_memory'] / 1024.0
end
# Ruby 1.9 with total_malloc_allocated_size patch
elsif GC.respond_to?(:malloc_total_allocated_size)
def measure
GC.total_malloc_allocated_size / 1024.0
end
# Ruby 1.9 unpatched
elsif GC.respond_to?(:malloc_allocated_size)
def measure
GC.malloc_allocated_size / 1024.0
end
# Ruby 1.9 + GC profiler patch
elsif defined?(GC::Profiler)
# Ruby 1.9 + extended GC profiler patch
if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
def measure
GC.enable
GC.start
@ -370,6 +344,12 @@ begin
GC.disable
kb
end
# Ruby 1.8 + ruby-prof wrapper
elsif RubyProf.respond_to?(:measure_memory)
def measure
RubyProf.measure_memory / 1024.0
end
end
def format(measurement)
@ -380,27 +360,21 @@ begin
class Objects < Base
Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
if RubyProf.respond_to?(:measure_allocations)
def measure
RubyProf.measure_allocations
end
# Ruby 1.8 + railsbench patch
elsif ObjectSpace.respond_to?(:allocated_objects)
def measure
ObjectSpace.allocated_objects
end
# Ruby 1.9 + GC profiler patch
elsif defined?(GC::Profiler)
# Ruby 1.9 + extented GC profiler patch
if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
def measure
GC.enable
GC.start
last = GC::Profiler.data.last
count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS]
count = GC::Profiler.data.last[:HEAP_TOTAL_OBJECTS]
GC.disable
count
end
# Ruby 1.8 + ruby-prof wrapper
elsif RubyProf.respond_to?(:measure_allocations)
def measure
RubyProf.measure_allocations
end
end
def format(measurement)
@ -411,18 +385,21 @@ begin
class GcRuns < Base
Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
if RubyProf.respond_to?(:measure_gc_runs)
# Ruby 1.9 + extented GC profiler patch
if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
def measure
GC.enable
GC.start
count = GC::Profiler.data.last[:GC_RUNS]
GC.disable
count
end
# Ruby 1.8 + ruby-prof wrapper
elsif RubyProf.respond_to?(:measure_gc_runs)
def measure
RubyProf.measure_gc_runs
end
elsif GC.respond_to?(:collections)
def measure
GC.collections
end
elsif GC.respond_to?(:heap_info)
def measure
GC.heap_info['num_gc_passes']
end
end
def format(measurement)
@ -433,14 +410,21 @@ begin
class GcTime < Base
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
if RubyProf.respond_to?(:measure_gc_time)
# Ruby 1.9 + extented GC profiler patch
if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
def measure
GC.enable
GC.start
sec = GC::Profiler.data.inject(0) { |total, run| total += run[:GC_TIME] }
GC.disable
sec
end
# Ruby 1.8 + ruby-prof wrapper
elsif RubyProf.respond_to?(:measure_gc_time)
def measure
RubyProf.measure_gc_time
end
elsif GC.respond_to?(:time)
def measure
GC.time
end
end
def format(measurement)
@ -452,4 +436,4 @@ begin
end
end
rescue LoadError
end
end

View File

@ -737,6 +737,13 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase
end
end
def test_nil_time_zone
Time.use_zone nil do
assert !@t.in_time_zone.respond_to?(:period), 'no period method'
assert !@dt.in_time_zone.respond_to?(:period), 'no period method'
end
end
def test_in_time_zone_with_argument
Time.use_zone 'Eastern Time (US & Canada)' do # Time.zone will not affect #in_time_zone(zone)
assert_equal 'Fri, 31 Dec 1999 15:00:00 AKST -09:00', @t.in_time_zone('Alaska').inspect

View File

@ -0,0 +1,75 @@
require 'abstract_unit'
require 'test/unit'
require 'active_support'
require 'active_support/core_ext/hash/slice'
class DescendantsTrackerTest < Test::Unit::TestCase
class Parent
extend ActiveSupport::DescendantsTracker
end
class Child1 < Parent
end
class Child2 < Parent
end
class Grandchild1 < Child1
end
class Grandchild2 < Child1
end
ALL = [Parent, Child1, Child2, Grandchild1, Grandchild2]
def test_descendants
assert_equal [Child1, Grandchild1, Grandchild2, Child2], Parent.descendants
assert_equal [Grandchild1, Grandchild2], Child1.descendants
assert_equal [], Child2.descendants
end
def test_direct_descendants
assert_equal [Child1, Child2], Parent.direct_descendants
assert_equal [Grandchild1, Grandchild2], Child1.direct_descendants
assert_equal [], Child2.direct_descendants
end
def test_clear_with_autoloaded_parent_children_and_granchildren
mark_as_autoloaded *ALL do
ActiveSupport::DescendantsTracker.clear
assert ActiveSupport::DescendantsTracker.descendants.slice(*ALL).empty?
end
end
def test_clear_with_autoloaded_children_and_granchildren
mark_as_autoloaded Child1, Grandchild1, Grandchild2 do
ActiveSupport::DescendantsTracker.clear
assert_equal [Child2], Parent.descendants
assert_equal [], Child2.descendants
end
end
def test_clear_with_autoloaded_granchildren
mark_as_autoloaded Grandchild1, Grandchild2 do
ActiveSupport::DescendantsTracker.clear
assert_equal [Child1, Child2], Parent.descendants
assert_equal [], Child1.descendants
assert_equal [], Child2.descendants
end
end
protected
def mark_as_autoloaded(*klasses)
old_autoloaded = ActiveSupport::Dependencies.autoloaded_constants.dup
ActiveSupport::Dependencies.autoloaded_constants = klasses.map(&:name)
old_descendants = ActiveSupport::DescendantsTracker.descendants.dup
old_descendants.each { |k, v| old_descendants[k] = v.dup }
yield
ensure
ActiveSupport::Dependencies.autoloaded_constants = old_autoloaded
ActiveSupport::DescendantsTracker.descendants.replace(old_descendants)
end
end

View File

@ -0,0 +1,56 @@
require 'abstract_unit'
require 'test/unit'
require 'active_support'
require 'fileutils'
MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__)
class FileUpdateCheckerTest < Test::Unit::TestCase
FILES = %w(1.txt 2.txt 3.txt)
def setup
FileUtils.touch(FILES)
end
def teardown
FileUtils.rm(FILES)
end
def test_should_not_execute_the_block_if_no_paths_are_given
i = 0
checker = ActiveSupport::FileUpdateChecker.new([]){ i += 1 }
checker.execute_if_updated
assert_equal 0, i
end
def test_should_invoke_the_block_on_first_call_if_it_does_not_calculate_last_updated_at_on_load
i = 0
checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
checker.execute_if_updated
assert_equal 1, i
end
def test_should_not_invoke_the_block_on_first_call_if_it_calculates_last_updated_at_on_load
i = 0
checker = ActiveSupport::FileUpdateChecker.new(FILES, true){ i += 1 }
checker.execute_if_updated
assert_equal 0, i
end
def test_should_not_invoke_the_block_if_no_file_has_changed
i = 0
checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
5.times { checker.execute_if_updated }
assert_equal 1, i
end
def test_should_invoke_the_block_if_a_file_has_changed
i = 0
checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
checker.execute_if_updated
sleep(1)
FileUtils.touch(FILES)
checker.execute_if_updated
assert_equal 2, i
end
end

View File

@ -443,6 +443,11 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
assert_equal 'Abc', 'abc'.mb_chars.capitalize
end
def test_titleize_should_work_on_ascii_characters
assert_equal '', ''.mb_chars.titleize
assert_equal 'Abc Abc', 'abc abc'.mb_chars.titleize
end
def test_respond_to_knows_which_methods_the_proxy_responds_to
assert ''.mb_chars.respond_to?(:slice) # Defined on Chars
assert ''.mb_chars.respond_to?(:capitalize!) # Defined on Chars
@ -480,6 +485,15 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
end
end
def test_titleize_should_be_unicode_aware
assert_equal "Él Que Se Enteró", chars("ÉL QUE SE ENTERÓ").titleize
assert_equal "Абвг Абвг", chars("аБвг аБвг").titleize
end
def test_titleize_should_not_affect_characters_that_do_not_case_fold
assert_equal "日本語", chars("日本語").titleize
end
def test_limit_should_not_break_on_blank_strings
example = chars('')
assert_equal example, example.limit(0)

View File

@ -1,3 +1,5 @@
#!/usr/bin/env ruby
begin
require "rails/cli"
rescue LoadError

259
doc/template/horo.rb vendored
View File

@ -11,8 +11,12 @@ if defined?(RDoc::Diagram)
end
end
require 'rdoc/generator/html'
module RDoc
module Page
module Generator
class HTML
class HORO
FONTS = "\"Bitstream Vera Sans\", Verdana, Arial, Helvetica, sans-serif"
@ -28,7 +32,7 @@ a:hover {
}
body, td, p {
font-family: %fonts%;
font-family: <%= values['fonts'] %>;
background: #FFF;
color: #000;
margin: 0px;
@ -206,7 +210,7 @@ dd {
}
CSS
XHTML_PREAMBLE = %{<?xml version="1.0" encoding="%charset%"?>
XHTML_PREAMBLE = %{<?xml version="1.0" encoding="<%= values['charset'] %>"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@ -221,9 +225,9 @@ XHTML_FRAMESET_PREAMBLE = %{
HEADER = XHTML_PREAMBLE + <<ENDHEADER
<html>
<head>
<title>%title%</title>
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
<link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
<title><%= values['title'] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values['charset'] %>" />
<link rel="stylesheet" href="<%= values['style_url'] %>" type="text/css" media="screen" />
<script language="JavaScript" type="text/javascript">
// <![CDATA[
@ -273,20 +277,20 @@ FILE_PAGE = <<HTML
<table border='0' cellpadding='0' cellspacing='0' width="100%" class='banner'>
<tr><td>
<table width="100%" border='0' cellpadding='0' cellspacing='0'><tr>
<td class="file-title" colspan="2"><span class="file-title-prefix">File</span><br />%short_name%</td>
<td class="file-title" colspan="2"><span class="file-title-prefix">File</span><br /><%= values['short_name'] %></td>
<td align="right">
<table border='0' cellspacing="0" cellpadding="2">
<tr>
<td>Path:</td>
<td>%full_path%
IF:cvsurl
&nbsp;(<a href="%cvsurl%">CVS</a>)
ENDIF:cvsurl
<td><%= values['full_path'] %>
<% if values['cvsurl'] %>
&nbsp;(<a href="<%= values['cvsurl'] %>">CVS</a>)
<% end %>
</td>
</tr>
<tr>
<td>Modified:</td>
<td>%dtm_modified%</td>
<td><%= values['dtm_modified'] %></td>
</tr>
</table>
</td></tr>
@ -299,34 +303,34 @@ HTML
CLASS_PAGE = <<HTML
<table width="100%" border='0' cellpadding='0' cellspacing='0' class='banner'><tr>
<td class="file-title"><span class="file-title-prefix">%classmod%</span><br />%full_name%</td>
<td class="file-title"><span class="file-title-prefix"><%= values['classmod'] %></span><br /><%= values['full_name'] %></td>
<td align="right">
<table cellspacing="0" cellpadding="2">
<tr valign="top">
<td>In:</td>
<td>
START:infiles
HREF:full_path_url:full_path:
IF:cvsurl
&nbsp;(<a href="%cvsurl%">CVS</a>)
ENDIF:cvsurl
END:infiles
<% values['infiles'].each do |infile| %>
<%= href infile['full_path_url'], infile['full_path'] %>:
<% if infile['cvsurl'] %>
&nbsp;(<a href="<%= infile['cvsurl'] %>">CVS</a>)
<% end %>
<% end %>
</td>
</tr>
IF:parent
<% if values['parent'] %>
<tr>
<td>Parent:</td>
<td>
IF:par_url
<a href="%par_url%">
ENDIF:par_url
%parent%
IF:par_url
<% if values['par_url'] %>
<a href="<%= values['par_url'] %>">
<% end %>
<%= values['parent'] %>
<% if values['par_url'] %>
</a>
ENDIF:par_url
<% end %>
</td>
</tr>
ENDIF:parent
<% end %>
</table>
</td>
</tr>
@ -337,149 +341,149 @@ HTML
METHOD_LIST = <<HTML
<div id="content">
IF:diagram
<% if values['diagram'] %>
<table cellpadding='0' cellspacing='0' border='0' width="100%"><tr><td align="center">
%diagram%
<%= values['diagram'] %>
</td></tr></table>
ENDIF:diagram
<% end %>
IF:description
<div class="description">%description%</div>
ENDIF:description
<% if values['description'] %>
<div class="description"><%= values['description'] %></div>
<% end %>
IF:requires
<% if values['requires'] %>
<div class="sectiontitle">Required Files</div>
<ul>
START:requires
<li>HREF:aref:name:</li>
END:requires
<% values['requires'].each do |require| %>
<li><%= href require['aref'], require['name'] %>:</li>
<% end %>
</ul>
ENDIF:requires
<% end %>
IF:toc
<% if values['toc'] %>
<div class="sectiontitle">Contents</div>
<ul>
START:toc
<li><a href="#%href%">%secname%</a></li>
END:toc
<% values['toc'].each do |toc| %>
<li><a href="#<%= toc['href'] %>"><%= toc['secname'] %></a></li>
<% end %>
</ul>
ENDIF:toc
<% end %>
IF:methods
<% if values['methods'] %>
<div class="sectiontitle">Methods</div>
<ul>
START:methods
<li>HREF:aref:name:</li>
END:methods
<% values['methods'].each do |method| %>
<li><%= href method['aref'], method['name'] %></li>
<% end %>
</ul>
ENDIF:methods
<% end %>
IF:includes
<% if values['includes'] %>
<div class="sectiontitle">Included Modules</div>
<ul>
START:includes
<li>HREF:aref:name:</li>
END:includes
<% values['includes'].each do |include| %>
<li><%= href include['aref'], include['name'] %>:</li>
<% end %>
</ul>
ENDIF:includes
<% end %>
START:sections
IF:sectitle
<div class="sectiontitle"><a name="%secsequence%">%sectitle%</a></div>
IF:seccomment
<% values['sections'].each do |section| %>
<% if section['sectitle'] %>
<div class="sectiontitle"><a name="<%= section['secsequence'] %>"><%= section['sectitle'] %></a></div>
<% if section['seccomment'] %>
<div class="description">
%seccomment%
<%= section['seccomment'] %>
</div>
ENDIF:seccomment
ENDIF:sectitle
<% end %>
<% end %>
IF:classlist
<% if section['classlist'] %>
<div class="sectiontitle">Classes and Modules</div>
%classlist%
ENDIF:classlist
<%= section['classlist'] %>
<% end %>
IF:constants
<% if section['constants'] %>
<div class="sectiontitle">Constants</div>
<table border='0' cellpadding='5'>
START:constants
<% section['constants'].each do |constant| %>
<tr valign='top'>
<td class="attr-name">%name%</td>
<td class="attr-name"><%= constant['name'] %></td>
<td>=</td>
<td class="attr-value">%value%</td>
<td class="attr-value"><%= constant['value'] %></td>
</tr>
IF:desc
<% if constant['desc'] %>
<tr valign='top'>
<td>&nbsp;</td>
<td colspan="2" class="attr-desc">%desc%</td>
<td colspan="2" class="attr-desc"><%= constant['desc'] %></td>
</tr>
ENDIF:desc
END:constants
<% end %>
<% end %>
</table>
ENDIF:constants
<% end %>
IF:attributes
<% if section['attributes'] %>
<div class="sectiontitle">Attributes</div>
<table border='0' cellpadding='5'>
START:attributes
<% section['attributes'].each do |attribute| %>
<tr valign='top'>
<td class='attr-rw'>
IF:rw
[%rw%]
ENDIF:rw
<% if attribute['rw'] %>
[<%= attribute['rw'] %>]
<% end %>
</td>
<td class='attr-name'>%name%</td>
<td class='attr-desc'>%a_desc%</td>
<td class='attr-name'><%= attribute['name'] %></td>
<td class='attr-desc'><%= attribute['a_desc'] %></td>
</tr>
END:attributes
<% end %>
</table>
ENDIF:attributes
<% end %>
IF:method_list
START:method_list
IF:methods
<div class="sectiontitle">%type% %category% methods</div>
START:methods
<% if section['method_list'] %>
<% section['method_list'].each do |method_list| %>
<% if method_list['methods'] %>
<div class="sectiontitle"><%= method_list['type'] %> <%= method_list['category'] %> methods</div>
<% method_list['methods'].each do |method| %>
<div class="method">
<div class="title">
IF:callseq
<a name="%aref%"></a><b>%callseq%</b>
ENDIF:callseq
IFNOT:callseq
<a name="%aref%"></a><b>%name%</b>%params%
ENDIF:callseq
IF:codeurl
[&nbsp;<a href="%codeurl%" target="SOURCE_CODE" onclick="javascript:openCode('%codeurl%'); return false;">source</a>&nbsp;]
ENDIF:codeurl
<% if method['callseq'] %>
<a name="<%= method['aref'] %>"></a><b><%= method['callseq'] %></b>
<% end %>
<% unless method['callseq'] %>
<a name="<%= method['aref'] %>"></a><b><%= method['name'] %></b><%= method['params'] %>
<% end %>
<% if method['codeurl'] %>
[&nbsp;<a href="<%= method['codeurl'] %>" target="SOURCE_CODE" onclick="javascript:openCode('<%= method['codeurl'] %>'); return false;">source</a>&nbsp;]
<% end %>
</div>
IF:m_desc
<% if method['m_desc'] %>
<div class="description">
%m_desc%
<%= method['m_desc'] %>
</div>
ENDIF:m_desc
IF:aka
<% end %>
<% if method['aka'] %>
<div class="aka">
This method is also aliased as
START:aka
<a href="%aref%">%name%</a>
END:aka
<% method['aka'].each do |aka| %>
<a href="<%= aka['aref'] %>"><%= aka['name'] %></a>
<% end %>
</div>
ENDIF:aka
IF:sourcecode
<% end %>
<% if method['sourcecode'] %>
<div class="sourcecode">
<p class="source-link">[ <a href="javascript:toggleSource('%aref%_source')" id="l_%aref%_source">show source</a> ]</p>
<div id="%aref%_source" class="dyn-source">
<p class="source-link">[ <a href="javascript:toggleSource('<%= method['aref'] %>_source')" id="l_<%= method['aref'] %>_source">show source</a> ]</p>
<div id="<%= method['aref'] %>_source" class="dyn-source">
<pre>
%sourcecode%
<%= method['sourcecode'] %>
</pre>
</div>
</div>
ENDIF:sourcecode
<% end %>
</div>
END:methods
ENDIF:methods
END:method_list
ENDIF:method_list
END:sections
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
</div>
HTML
@ -489,7 +493,7 @@ FOOTER = <<ENDFOOTER
ENDFOOTER
BODY = HEADER + <<ENDBODY
!INCLUDE! <!-- banner header -->
<%= template_include %> <!-- banner header -->
<div id="bodyContent">
#{METHOD_LIST}
@ -502,8 +506,8 @@ ENDBODY
SRC_PAGE = XHTML_PREAMBLE + <<HTML
<html>
<head><title>%title%</title>
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
<head><title><%= values['title'] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values['charset'] %>" />
<style type="text/css">
.ruby-comment { color: green; font-style: italic }
.ruby-constant { color: #4433aa; font-weight: bold; }
@ -521,7 +525,7 @@ SRC_PAGE = XHTML_PREAMBLE + <<HTML
</style>
</head>
<body bgcolor="white">
<pre>%code%</pre>
<pre><%= values['code'] %></pre>
</body>
</html>
HTML
@ -529,13 +533,13 @@ HTML
########################## Index ################################
FR_INDEX_BODY = <<HTML
!INCLUDE!
<%= template_include %>
HTML
FILE_INDEX = XHTML_PREAMBLE + <<HTML
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
<meta http-equiv="Content-Type" content="text/html; charset=<%= values['charset'] %>" />
<title>Index</title>
<style type="text/css">
<!--
@ -571,11 +575,11 @@ FILE_INDEX = XHTML_PREAMBLE + <<HTML
<base target="docwin" />
</head>
<body>
<div class="banner">%list_title%</div>
<div class="banner"><%= values['list_title'] %></div>
<div class="entries">
START:entries
<a href="%href%">%name%</a><br />
END:entries
<% values['entries'].each do |entrie| %>
<a href="<%= entrie['href'] %>"><%= entrie['name'] %></a><br />
<% end %>
</div>
</body></html>
HTML
@ -586,8 +590,8 @@ METHOD_INDEX = FILE_INDEX
INDEX = XHTML_FRAMESET_PREAMBLE + <<HTML
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>%title%</title>
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
<title><%= values['title'] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values['charset'] %>" />
</head>
<frameset cols="20%,*">
@ -596,7 +600,7 @@ INDEX = XHTML_FRAMESET_PREAMBLE + <<HTML
<frame src="fr_class_index.html" name="Classes" />
<frame src="fr_method_index.html" name="Methods" />
</frameset>
<frame src="%initial_page%" name="docwin" />
<frame src="<%= values['initial_page'] %>" name="docwin" />
<noframes>
<body bgcolor="white">
Click <a href="html/index.html">here</a> for a non-frames
@ -610,4 +614,5 @@ HTML
end
end
end
end

View File

@ -1,3 +1,5 @@
gem 'rdoc', '= 2.2'
require 'rdoc'
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

0
railties/guides/assets/javascripts/code_highlighter.js Executable file → Normal file
View File

0
railties/guides/assets/javascripts/guides.js Executable file → Normal file
View File

0
railties/guides/assets/stylesheets/print.css Executable file → Normal file
View File

0
railties/guides/assets/stylesheets/reset.css Executable file → Normal file
View File

0
railties/guides/assets/stylesheets/style.css Executable file → Normal file
View File

View File

@ -1,4 +1,5 @@
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/file_update_checker'
require 'fileutils'
require 'rails/plugin'
require 'rails/engine'
@ -46,7 +47,6 @@ module Rails
autoload :Configuration, 'rails/application/configuration'
autoload :Finisher, 'rails/application/finisher'
autoload :Railties, 'rails/application/railties'
autoload :RoutesReloader, 'rails/application/routes_reloader'
class << self
private :new
@ -84,17 +84,30 @@ module Rails
delegate :middleware, :to => :config
def add_lib_to_load_paths!
# This method is called just after an application inherits from Rails::Application,
# allowing the developer to load classes in lib and use them during application
# configuration.
#
# class MyApplication < Rails::Application
# require "my_backend" # in lib/my_backend
# config.i18n.backend = MyBackend
# end
#
# Notice this method takes into consideration the default root path. So if you
# are changing config.root inside your application definition or having a custom
# Rails application, you will need to add lib to $LOAD_PATH on your own in case
# you need to load files in lib/ during the application configuration as well.
def add_lib_to_load_paths! #:nodoc:
path = config.root.join('lib').to_s
$LOAD_PATH.unshift(path) if File.exists?(path)
end
def require_environment!
def require_environment! #:nodoc:
environment = paths.config.environment.to_a.first
require environment if environment
end
def eager_load!
def eager_load! #:nodoc:
railties.all(&:eager_load!)
super
end
@ -108,11 +121,18 @@ module Rails
end
def routes_reloader
@routes_reloader ||= RoutesReloader.new
@routes_reloader ||= ActiveSupport::FileUpdateChecker.new([]){ reload_routes! }
end
def reload_routes!
routes_reloader.reload!
routes = Rails::Application.routes
routes.disable_clear_and_finalize = true
routes.clear!
routes_reloader.paths.each { |path| load(path) }
ActiveSupport.on_load(:action_controller) { routes.finalize! }
ensure
routes.disable_clear_and_finalize = false
end
def initialize!

View File

@ -1,4 +1,5 @@
require "active_support/notifications"
require "active_support/descendants_tracker"
module Rails
class Application
@ -55,6 +56,7 @@ module Rails
initializer :set_clear_dependencies_hook do
unless config.cache_classes
ActionDispatch::Callbacks.after do
ActiveSupport::DescendantsTracker.clear
ActiveSupport::Dependencies.clear
end
end

View File

@ -1,46 +0,0 @@
module Rails
class Application
class RoutesReloader
attr_reader :paths
def initialize
@paths, @last_change_at = [], nil
end
def changed_at
routes_changed_at = nil
paths.each do |path|
config_changed_at = File.stat(path).mtime
if routes_changed_at.nil? || config_changed_at > routes_changed_at
routes_changed_at = config_changed_at
end
end
routes_changed_at
end
def reload!
routes = Rails::Application.routes
routes.disable_clear_and_finalize = true
routes.clear!
paths.each { |path| load(path) }
ActiveSupport.on_load(:action_controller) { routes.finalize! }
nil
ensure
routes.disable_clear_and_finalize = false
end
def reload_if_changed
current_change_at = changed_at
if @last_change_at != current_change_at
@last_change_at = current_change_at
reload!
end
end
end
end
end

0
railties/lib/rails/commands/generate.rb Executable file → Normal file
View File

View File

@ -83,7 +83,8 @@ module Rails
:environment => (ENV['RAILS_ENV'] || "development").dup,
:daemonize => false,
:debugger => false,
:pid => "tmp/pids/server.pid"
:pid => File.expand_path("tmp/pids/server.pid"),
:config => File.expand_path("config.ru")
})
end
end

View File

@ -3,4 +3,4 @@
<%%= render 'form' %>
<%%= link_to 'Show', @<%= singular_name %> %> |
<%%= link_to 'Back', <%= plural_name %>_path %>
<%%= link_to 'Back', <%= index_helper %>_path %>

View File

@ -2,4 +2,4 @@
<%%= render 'form' %>
<%%= link_to 'Back', <%= plural_name %>_path %>
<%%= link_to 'Back', <%= index_helper %>_path %>

View File

@ -9,4 +9,4 @@
<% end -%>
<%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> |
<%%= link_to 'Back', <%= plural_name %>_path %>
<%%= link_to 'Back', <%= index_helper %>_path %>

View File

@ -46,6 +46,14 @@ module Rails
end
end
def uncountable?
singular_name == plural_name
end
def index_helper
uncountable? ? "#{plural_name}_index" : plural_name
end
# Tries to retrieve the application name or simple return application.
def application_name
if defined?(Rails) && Rails.application

View File

@ -37,7 +37,7 @@ link:files/vendor/rails/actionpack/README.html.
3. Go to http://localhost:3000/ and you'll see:
"Welcome aboard: You're riding the Rails!"
4. Follow the guidelines to start developing your application. You can find
4. Follow the guidelines to start developing your application. You can find
the following resources handy:
* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
@ -71,13 +71,13 @@ The result will be a message in your log file along the lines of:
More information on how to use the logger is at http://www.ruby-doc.org/core/
Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
several books available online as well:
* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
These two books will bring you up to speed on the Ruby language and also on
These two books will bring you up to speed on the Ruby language and also on
programming in general.
@ -199,7 +199,7 @@ app/controllers
ApplicationController which itself descends from ActionController::Base.
app/models
Holds models that should be named like post.rb. Models descend from
Holds models that should be named like post.rb. Models descend from
ActiveRecord::Base by default.
app/views

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