Merge remote branch 'rails/master'
Conflicts: actionpack/lib/abstract_controller/base.rb
This commit is contained in:
commit
207fa59675
5
Gemfile
5
Gemfile
|
@ -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"
|
||||
|
||||
|
|
15
Rakefile
15
Rakefile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'rubygems'
|
||||
gem 'rdoc', '= 2.2'
|
||||
require 'rdoc'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>")
|
||||
# # => "<p>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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -85,6 +85,7 @@ module ActionView
|
|||
|
||||
def setup_with_controller
|
||||
@controller = ActionView::TestCase::TestController.new
|
||||
@request = @controller.request
|
||||
@output_buffer = ActiveSupport::SafeBuffer.new
|
||||
@rendered = ''
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<%= form.label :title %>
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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=\"<Africa>\"><option value=\"<sa>\"><South Africa></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="<Africa>"><option value="<sa>"><South Africa></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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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><b> test with unsafe string </b><script>code!</script></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 <...", 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<script>code!</script>",
|
||||
"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(
|
||||
"<p>This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
|
||||
"<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(
|
||||
"<p>This is a <em><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
|
||||
"<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(
|
||||
"<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>",
|
||||
"<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(
|
||||
"<p class="<strong class=\"highlight\">beautiful</strong>">This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
|
||||
"<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(
|
||||
"<p>This is a <strong class=\"highlight\">beautiful</strong> <a href="http://example.com/<strong class=\"highlight\">beautiful</strong>#top?what=<strong class=\"highlight\">beautiful</strong>%20morning&when=now+then">morning</a>, but also a <strong class=\"highlight\">beautiful</strong> day</p>",
|
||||
"<p>This is a <strong class=\"highlight\">beautiful</strong> <a href=\"http://example.com/beautiful#top?what=beautiful%20morning&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&num=2">http://www.rubyonrails.com?id=1&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)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
dir = File.dirname(__FILE__)
|
||||
|
||||
gem 'rdoc', '= 2.2'
|
||||
require 'rdoc'
|
||||
|
||||
require 'rake/testtask'
|
||||
|
||||
task :default => :test
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
0
activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
Executable file → Normal file
0
activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
Executable file → Normal 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.
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
require 'yajl-ruby' unless defined?(Yajl)
|
||||
require 'yajl' unless defined?(Yajl)
|
||||
|
||||
module ActiveSupport
|
||||
module JSON
|
||||
|
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
(<a href="%cvsurl%">CVS</a>)
|
||||
ENDIF:cvsurl
|
||||
<td><%= values['full_path'] %>
|
||||
<% if values['cvsurl'] %>
|
||||
(<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
|
||||
(<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'] %>
|
||||
(<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> </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
|
||||
[ <a href="%codeurl%" target="SOURCE_CODE" onclick="javascript:openCode('%codeurl%'); return false;">source</a> ]
|
||||
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'] %>
|
||||
[ <a href="<%= method['codeurl'] %>" target="SOURCE_CODE" onclick="javascript:openCode('<%= method['codeurl'] %>'); return false;">source</a> ]
|
||||
<% 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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
gem 'rdoc', '= 2.2'
|
||||
require 'rdoc'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
<%%= render 'form' %>
|
||||
|
||||
<%%= link_to 'Show', @<%= singular_name %> %> |
|
||||
<%%= link_to 'Back', <%= plural_name %>_path %>
|
||||
<%%= link_to 'Back', <%= index_helper %>_path %>
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<%%= render 'form' %>
|
||||
|
||||
<%%= link_to 'Back', <%= plural_name %>_path %>
|
||||
<%%= link_to 'Back', <%= index_helper %>_path %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue