1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge branch 'master' of github.com:lifo/docrails

Conflicts:
	actionpack/lib/action_view/helpers/asset_tag_helper.rb
This commit is contained in:
Vijay Dev 2012-09-28 23:17:07 +05:30
commit 955a72c692
36 changed files with 363 additions and 371 deletions

View file

@ -23,10 +23,10 @@ module ActionController #:nodoc:
# Configuration examples (MemoryStore is the default):
#
# config.action_controller.cache_store = :memory_store
# config.action_controller.cache_store = :file_store, "/path/to/cache/directory"
# config.action_controller.cache_store = :mem_cache_store, "localhost"
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211")
# config.action_controller.cache_store = MyOwnStore.new("parameter")
# config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
# config.action_controller.cache_store = :mem_cache_store, 'localhost'
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
# config.action_controller.cache_store = MyOwnStore.new('parameter')
module Caching
extend ActiveSupport::Concern
extend ActiveSupport::Autoload
@ -73,7 +73,7 @@ module ActionController #:nodoc:
end
protected
# Convenience accessor
# Convenience accessor.
def cache(key, options = {}, &block)
if cache_configured?
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)

View file

@ -1,16 +1,16 @@
require 'set'
module ActionController #:nodoc:
module ActionController
module Caching
# Action caching is similar to page caching by the fact that the entire
# output of the response is cached, but unlike page caching, every
# request still goes through Action Pack. The key benefit of this is
# that filters run before the cache is served, which allows for
# authentication and other restrictions on whether someone is allowed
# to execute such action. Example:
# to execute such action.
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# before_filter :authenticate, except: :public
#
# caches_page :public
# caches_action :index, :show
@ -35,8 +35,8 @@ module ActionController #:nodoc:
# <tt>http://david.example.com/lists.xml</tt>
# are treated like separate requests and so are cached separately.
# Keep in mind when expiring an action cache that
# <tt>:action => 'lists'</tt> is not the same as
# <tt>:action => 'list', :format => :xml</tt>.
# <tt>action: 'lists'</tt> is not the same as
# <tt>action: 'list', format: :xml</tt>.
#
# You can modify the default action cache path by passing a
# <tt>:cache_path</tt> option. This will be passed directly to
@ -53,18 +53,18 @@ module ActionController #:nodoc:
# The following example depicts some of the points made above:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# before_filter :authenticate, except: :public
#
# caches_page :public
#
# caches_action :index, :if => Proc.new do
# caches_action :index, if: Proc.new do
# !request.format.json? # cache if is not a JSON request
# end
#
# caches_action :show, :cache_path => { :project => 1 },
# :expires_in => 1.hour
# caches_action :show, cache_path: { project: 1 },
# expires_in: 1.hour
#
# caches_action :feed, :cache_path => Proc.new do
# caches_action :feed, cache_path: Proc.new do
# if params[:user_id]
# user_list_url(params[:user_id, params[:id])
# else
@ -73,7 +73,7 @@ module ActionController #:nodoc:
# end
# end
#
# If you pass <tt>:layout => false</tt>, it will only cache your action
# If you pass <tt>layout: false</tt>, it will only cache your action
# content. That's useful when your layout has dynamic information.
#
# Warning: If the format of the request is determined by the Accept HTTP
@ -162,9 +162,9 @@ module ActionController #:nodoc:
class ActionCachePath
attr_reader :path, :extension
# If +infer_extension+ is true, the cache path extension is looked up from the request's
# If +infer_extension+ is +true+, the cache path extension is looked up from the request's
# path and format. This is desirable when reading and writing the cache, but not when
# expiring the cache - expire_action should expire the same files regardless of the
# expiring the cache - +expire_action+ should expire the same files regardless of the
# request format.
def initialize(controller, options = {}, infer_extension = true)
if infer_extension

View file

@ -1,29 +1,29 @@
module ActionController #:nodoc:
module ActionController
module Caching
# Fragment caching is used for caching various blocks within
# Fragment caching is used for caching various blocks within
# views without caching the entire action as a whole. This is
# useful when certain elements of an action change frequently or
# depend on complicated state while other parts rarely change or
# useful when certain elements of an action change frequently or
# depend on complicated state while other parts rarely change or
# can be shared amongst multiple parties. The caching is done using
# the <tt>cache</tt> helper available in the Action View. See
# the +cache+ helper available in the Action View. See
# ActionView::Helpers::CacheHelper for more information.
#
# While it's strongly recommended that you use key-based cache
# expiration (see links in CacheHelper for more information),
# it is also possible to manually expire caches. For example:
#
# expire_fragment("name_of_cache")
# expire_fragment('name_of_cache')
module Fragments
# Given a key (as described in <tt>expire_fragment</tt>), returns
# a key suitable for use in reading, writing, or expiring a
# Given a key (as described in +expire_fragment+), returns
# a key suitable for use in reading, writing, or expiring a
# cached fragment. All keys are prefixed with <tt>views/</tt> and uses
# ActiveSupport::Cache.expand_cache_key for the expansion.
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
# Writes <tt>content</tt> to the location signified by
# <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats).
# Writes +content+ to the location signified by
# +key+ (see +expire_fragment+ for acceptable formats).
def write_fragment(key, content, options = nil)
return content unless cache_configured?
@ -35,8 +35,8 @@ module ActionController #:nodoc:
content
end
# Reads a cached fragment from the location signified by <tt>key</tt>
# (see <tt>expire_fragment</tt> for acceptable formats).
# Reads a cached fragment from the location signified by +key+
# (see +expire_fragment+ for acceptable formats).
def read_fragment(key, options = nil)
return unless cache_configured?
@ -47,8 +47,8 @@ module ActionController #:nodoc:
end
end
# Check if a cached fragment from the location signified by
# <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
# Check if a cached fragment from the location signified by
# +key+ exists (see +expire_fragment+ for acceptable formats).
def fragment_exist?(key, options = nil)
return unless cache_configured?
key = fragment_cache_key(key)
@ -65,7 +65,7 @@ module ActionController #:nodoc:
# * String - This would normally take the form of a path, like
# <tt>pages/45/notes</tt>.
# * Hash - Treated as an implicit call to +url_for+, like
# <tt>{:controller => "pages", :action => "notes", :id => 45}</tt>
# <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
# * Regexp - Will remove any fragment that matches, so
# <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
# don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
@ -74,8 +74,8 @@ module ActionController #:nodoc:
# only supported on caches that can iterate over all keys (unlike
# memcached).
#
# +options+ is passed through to the cache store's <tt>delete</tt>
# method (or <tt>delete_matched</tt>, for Regexp keys.)
# +options+ is passed through to the cache store's +delete+
# method (or <tt>delete_matched</tt>, for Regexp keys).
def expire_fragment(key, options = nil)
return unless cache_configured?
key = fragment_cache_key(key) unless key.is_a?(Regexp)
@ -89,7 +89,7 @@ module ActionController #:nodoc:
end
end
def instrument_fragment_cache(name, key)
def instrument_fragment_cache(name, key) # :nodoc:
ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
end
end

View file

@ -1,60 +1,72 @@
require 'fileutils'
require 'active_support/core_ext/class/attribute_accessors'
module ActionController #:nodoc:
module ActionController
module Caching
# Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server
# can serve without going through Action Pack. This is the fastest way to cache your content as opposed to going dynamically
# through the process of generating the content. Unfortunately, this incredible speed-up is only available to stateless pages
# where all visitors are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are
# a great fit for this approach, but account-based systems where people log in and manipulate their own data are often less
# likely candidates.
# Page caching is an approach to caching where the entire action output of is
# stored as a HTML file that the web server can serve without going through
# Action Pack. This is the fastest way to cache your content as opposed to going
# dynamically through the process of generating the content. Unfortunately, this
# incredible speed-up is only available to stateless pages where all visitors are
# treated the same. Content management systems -- including weblogs and wikis --
# have many pages that are a great fit for this approach, but account-based systems
# where people log in and manipulate their own data are often less likely candidates.
#
# Specifying which actions to cache is done through the <tt>caches_page</tt> class method:
# Specifying which actions to cache is done through the +caches_page+ class method:
#
# class WeblogController < ActionController::Base
# caches_page :show, :new
# end
#
# This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used
# that would normally trigger dynamic page generation. Page caching works by configuring a web server to first check for the
# existence of files on disk, and to serve them directly when found, without passing the request through to Action Pack.
# This is much faster than handling the full dynamic request in the usual way.
# This will generate cache files such as <tt>weblog/show/5.html</tt> and
# <tt>weblog/new.html</tt>, which match the URLs used that would normally trigger
# dynamic page generation. Page caching works by configuring a web server to first
# check for the existence of files on disk, and to serve them directly when found,
# without passing the request through to Action Pack. This is much faster than
# handling the full dynamic request in the usual way.
#
# Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
# is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
# Expiration of the cache is handled by deleting the cached file, which results
# in a lazy regeneration approach where the cache is not restored before another
# hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
#
# class WeblogController < ActionController::Base
# def update
# List.update(params[:list][:id], params[:list])
# expire_page :action => "show", :id => params[:list][:id]
# redirect_to :action => "show", :id => params[:list][:id]
# expire_page action: 'show', id: params[:list][:id]
# redirect_to action: 'show', id: params[:list][:id]
# end
# end
#
# Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
# expired.
# Additionally, you can expire caches using Sweepers that act on changes in
# the model to determine when a cache is supposed to be expired.
module Pages
extend ActiveSupport::Concern
included do
# The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
# For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>Rails.root + "/public"</tt>). Changing
# this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
# web server to look in the new location for cached files.
# The cache directory should be the document root for the web server and is
# set using <tt>Base.page_cache_directory = "/document/root"</tt>. For Rails,
# this directory has already been set to Rails.public_path (which is usually
# set to <tt>Rails.root + "/public"</tt>). Changing this setting can be useful
# to avoid naming conflicts with files in <tt>public/</tt>, but doing so will
# likely require configuring your web server to look in the new location for
# cached files.
class_attribute :page_cache_directory
self.page_cache_directory ||= ''
# Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
# order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
# If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
# extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
# Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>.
# In these cases, the page caching mechanism will add one in order to make it
# easy for the cached files to be picked up properly by the web server. By
# default, this cache extension is <tt>.html</tt>. If you want something else,
# like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension.
# In cases where a request already has an extension, such as <tt>.xml</tt>
# or <tt>.rss</tt>, page caching will not add an extension. This allows it
# to work well with RESTful apps.
class_attribute :page_cache_extension
self.page_cache_extension ||= '.html'
# The compression used for gzip. If false (default), the page is not compressed.
# If can be a symbol showing the ZLib compression method, for example, :best_compression
# or :best_speed or an integer configuring the compression level.
# The compression used for gzip. If +false+ (default), the page is not compressed.
# If can be a symbol showing the ZLib compression method, for example, <tt>:best_compression</tt>
# or <tt>:best_speed</tt> or an integer configuring the compression level.
class_attribute :page_cache_compression
self.page_cache_compression ||= false
end
@ -62,7 +74,7 @@ module ActionController #:nodoc:
module ClassMethods
# Expires the page that was cached with the +path+ as a key.
#
# expire_page "/lists/show"
# expire_page '/lists/show'
def expire_page(path)
return unless perform_caching
path = page_cache_path(path)
@ -75,7 +87,7 @@ module ActionController #:nodoc:
# Manually cache the +content+ in the key determined by +path+.
#
# cache_page "I'm the cached content", "/lists/show"
# cache_page "I'm the cached content", '/lists/show'
def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
return unless perform_caching
path = page_cache_path(path, extension)
@ -90,19 +102,19 @@ module ActionController #:nodoc:
end
# Caches the +actions+ using the page-caching approach that'll store
# the cache in a path within the page_cache_directory that
# the cache in a path within the +page_cache_directory+ that
# matches the triggering url.
#
# You can also pass a :gzip option to override the class configuration one.
# You can also pass a <tt>:gzip</tt> option to override the class configuration one.
#
# # cache the index action
# caches_page :index
#
# # cache the index action except for JSON requests
# caches_page :index, :if => Proc.new { !request.format.json? }
# caches_page :index, if: Proc.new { !request.format.json? }
#
# # don't gzip images
# caches_page :image, :gzip => false
# caches_page :image, gzip: false
def caches_page(*actions)
return unless perform_caching
options = actions.extract_options!
@ -144,7 +156,7 @@ module ActionController #:nodoc:
# Expires the page that was cached with the +options+ as a key.
#
# expire_page :controller => "lists", :action => "show"
# expire_page controller: 'lists', action: 'show'
def expire_page(options = {})
return unless self.class.perform_caching
@ -161,10 +173,11 @@ module ActionController #:nodoc:
end
end
# Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used.
# If no options are provided, the url of the current request being handled is used.
# Manually cache the +content+ in the key determined by +options+. If no content is provided,
# the contents of response.body is used. If no options are provided, the url of the current
# request being handled is used.
#
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
# cache_page "I'm the cached content", controller: 'lists', action: 'show'
def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
return unless self.class.perform_caching && caching_allowed?

View file

@ -1,38 +1,41 @@
module ActionController #:nodoc:
module ActionController
module Caching
# Sweepers are the terminators of the caching world and responsible for expiring caches when Active Record objects change.
# They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
# Sweepers are the terminators of the caching world and responsible for expiring
# caches when Active Record objects change. They do this by being half-observers,
# half-filters and implementing callbacks for both roles.
#
# class ListSweeper < ActionController::Caching::Sweeper
# observe List, Item
#
# def after_save(record)
# list = record.is_a?(List) ? record : record.list
# expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
# expire_action(:controller => "lists", :action => "all")
# list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
# expire_page(controller: 'lists', action: %w( show public feed ), id: list.id)
# expire_action(controller: 'lists', action: 'all')
# list.shares.each { |share| expire_page(controller: 'lists', action: 'show', id: share.url_key) }
# end
# end
#
# The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
# The sweeper is assigned in the controllers that wish to have its job performed using
# the +cache_sweeper+ class method:
#
# class ListsController < ApplicationController
# caches_action :index, :show, :public, :feed
# cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
# cache_sweeper :list_sweeper, only: [ :edit, :destroy, :share ]
# end
#
# In the example above, four actions are cached and three actions are responsible for expiring those caches.
#
# You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
# You can also name an explicit class in the declaration of a sweeper, which is needed
# if the sweeper is in a module:
#
# class ListsController < ApplicationController
# caches_action :index, :show, :public, :feed
# cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
# cache_sweeper OpenBar::Sweeper, only: [ :edit, :destroy, :share ]
# end
module Sweeping
extend ActiveSupport::Concern
module ClassMethods #:nodoc:
module ClassMethods # :nodoc:
def cache_sweeper(*sweepers)
configuration = sweepers.extract_options!
@ -51,7 +54,7 @@ module ActionController #:nodoc:
end
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
class Sweeper < ActiveRecord::Observer #:nodoc:
class Sweeper < ActiveRecord::Observer # :nodoc:
attr_accessor :controller
def initialize(*args)

View file

@ -18,8 +18,6 @@ module ActionController
# may want to add the current user id to be part of the etag to prevent authorized displaying
# of cached pages.
#
# === Example
#
# class InvoicesController < ApplicationController
# etag { current_user.try :id }
#
@ -34,25 +32,28 @@ module ActionController
end
end
# Sets the etag, last_modified, or both on the response and renders a
# Sets the etag, +last_modified+, or both on the response and renders a
# <tt>304 Not Modified</tt> response if the request is already fresh.
#
# Parameters:
# * <tt>:etag</tt>
# * <tt>:last_modified</tt>
# * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
# === Parameters:
#
# Example:
# * <tt>:etag</tt>.
# * <tt>:last_modified</tt>.
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
# +true+ if you want your application to be cachable by other devices (proxy caches).
#
# === Example:
#
# def show
# @article = Article.find(params[:id])
# fresh_when(:etag => @article, :last_modified => @article.created_at, :public => true)
# fresh_when(etag: @article, last_modified: @article.created_at, public: true)
# end
#
# This will render the show template if the request isn't sending a matching etag or
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
#
# You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
# You can also just pass a record where +last_modified+ will be set by calling
# +updated_at+ and the etag by passing the object itself.
#
# def show
# @article = Article.find(params[:id])
@ -81,22 +82,24 @@ module ActionController
head :not_modified if request.fresh?(response)
end
# Sets the etag and/or last_modified on the response and checks it against
# Sets the +etag+ and/or +last_modified+ on the response and checks it against
# the client request. If the request doesn't match the options provided, the
# request is considered stale and should be generated from scratch. Otherwise,
# it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
#
# Parameters:
# * <tt>:etag</tt>
# * <tt>:last_modified</tt>
# * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
# === Parameters:
#
# Example:
# * <tt>:etag</tt>.
# * <tt>:last_modified</tt>.
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
# +true+ if you want your application to be cachable by other devices (proxy caches).
#
# === Example:
#
# def show
# @article = Article.find(params[:id])
#
# if stale?(:etag => @article, :last_modified => @article.created_at)
# if stale?(etag: @article, last_modified: @article.created_at)
# @statistics = @article.really_expensive_call
# respond_to do |format|
# # all the supported formats
@ -104,7 +107,8 @@ module ActionController
# end
# end
#
# You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
# You can also just pass a record where +last_modified+ will be set by calling
# updated_at and the etag by passing the object itself.
#
# def show
# @article = Article.find(params[:id])
@ -122,7 +126,7 @@ module ActionController
# def show
# @article = Article.find(params[:id])
#
# if stale?(@article, :public => true)
# if stale?(@article, public: true)
# @statistics = @article.really_expensive_call
# respond_to do |format|
# # all the supported formats
@ -134,18 +138,18 @@ module ActionController
!request.fresh?(response)
end
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a <tt>private</tt> instruction, so that
# intermediate caches must not cache the response.
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
# instruction, so that intermediate caches must not cache the response.
#
# expires_in 20.minutes
# expires_in 3.hours, :public => true
# expires_in 3.hours, :public => true, :must_revalidate => true
# expires_in 3.hours, public: true
# expires_in 3.hours, public: true, must_revalidate: true
#
# This method will overwrite an existing Cache-Control header.
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
#
# The method will also ensure a HTTP Date header for client compatibility.
def expires_in(seconds, options = {}) #:doc:
def expires_in(seconds, options = {})
response.cache_control.merge!(
:max_age => seconds,
:public => options.delete(:public),
@ -157,9 +161,9 @@ module ActionController
response.date = Time.now unless response.date?
end
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or
# intermediate caches (like caching proxy servers).
def expires_now #:doc:
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
# occur by the browser or intermediate caches (like caching proxy servers).
def expires_now
response.cache_control.replace(:no_cache => true)
end

View file

@ -269,7 +269,7 @@ module ActionController
end
end
# == Strong Parameters
# == Strong \Parameters
#
# It provides an interface for protecting attributes from end-user
# assignment. This makes Action Controller parameters forbidden
@ -290,9 +290,9 @@ module ActionController
# end
#
# # This will pass with flying colors as long as there's a person key in the
# # parameters, otherwise it'll raise a ActionController::MissingParameter
# # parameters, otherwise it'll raise an ActionController::MissingParameter
# # exception, which will get caught by ActionController::Base and turned
# # into that 400 Bad Request reply.
# # into a 400 Bad Request reply.
# def update
# redirect_to current_account.people.find(params[:id]).tap { |person|
# person.update_attributes!(person_params)

View file

@ -369,9 +369,9 @@ module ActionView
# <tt>:size</tt> will be ignored if the value is not in the correct format.
#
# image_tag("icon")
# # => <img src="/assets/icon" alt="Icon" />
# # => <img alt="Icon" src="/assets/icon" />
# image_tag("icon.png")
# # => <img src="/assets/icon.png" alt="Icon" />
# # => <img alt="Icon" src="/assets/icon.png" />
# image_tag("icon.png", :size => "16x10", :alt => "Edit Entry")
# # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
# image_tag("/icons/icon.gif", :size => "16")

View file

@ -42,11 +42,15 @@ module ActiveRecord
@association.load_target
end
# Returns +true+ if the association has been loaded, otherwise +false+.
#
# person.pets.loaded? # => false
# person.pets
# person.pets.loaded? # => true
def loaded?
@association.loaded?
end
##
# Works in two ways.
#
# *First:* Specify a subset of fields to be selected from the result set.
@ -104,9 +108,8 @@ module ActiveRecord
@association.select(select, &block)
end
##
# Finds an object in the collection responding to the +id+. Uses the same
# rules as +ActiveRecord::Base.find+. Returns +ActiveRecord::RecordNotFound++
# rules as <tt>ActiveRecord::Base.find</tt>. Returns <tt>ActiveRecord::RecordNotFound</tt>
# error if the object can not be found.
#
# class Person < ActiveRecord::Base
@ -135,7 +138,6 @@ module ActiveRecord
@association.find(*args, &block)
end
##
# Returns the first record, or the first +n+ records, from the collection.
# If the collection is empty, the first form returns +nil+, and the second
# form returns an empty array.
@ -166,7 +168,6 @@ module ActiveRecord
@association.first(*args)
end
##
# Returns the last record, or the last +n+ records, from the collection.
# If the collection is empty, the first form returns +nil+, and the second
# form returns an empty array.
@ -197,7 +198,6 @@ module ActiveRecord
@association.last(*args)
end
##
# Returns a new object of the collection type that has been instantiated
# with +attributes+ and linked to this object, but have not yet been saved.
# You can pass an array of attributes hashes, this will return an array
@ -226,7 +226,6 @@ module ActiveRecord
@association.build(attributes, &block)
end
##
# Returns a new object of the collection type that has been instantiated with
# attributes, linked to this object and that has already been saved (if it
# passes the validations).
@ -257,7 +256,6 @@ module ActiveRecord
@association.create(attributes, &block)
end
##
# Like +create+, except that if the record is invalid, raises an exception.
#
# class Person
@ -274,7 +272,6 @@ module ActiveRecord
@association.create!(attributes, &block)
end
##
# Add one or more records to the collection by setting their foreign keys
# to the association's primary key. Since << flattens its argument list and
# inserts each record, +push+ and +concat+ behave identically. Returns +self+
@ -303,7 +300,6 @@ module ActiveRecord
@association.concat(*records)
end
##
# Replace this collection with +other_array+. This will perform a diff
# and delete/add only records that have changed.
#
@ -330,7 +326,6 @@ module ActiveRecord
@association.replace(other_array)
end
##
# Deletes all the records from the collection. For +has_many+ associations,
# the deletion is done according to the strategy specified by the <tt>:dependent</tt>
# option. Returns an array with the deleted records.
@ -423,7 +418,6 @@ module ActiveRecord
@association.delete_all
end
##
# Deletes the records of the collection directly from the database.
# This will _always_ remove the records ignoring the +:dependent+
# option.
@ -450,7 +444,6 @@ module ActiveRecord
@association.destroy_all
end
##
# Deletes the +records+ supplied and removes them from the collection. For
# +has_many+ associations, the deletion is done according to the strategy
# specified by the <tt>:dependent</tt> option. Returns an array with the
@ -514,7 +507,7 @@ module ActiveRecord
# Pet.find(1, 3)
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
#
# If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
# If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
# *without* calling their +destroy+ method.
#
# class Person < ActiveRecord::Base
@ -569,7 +562,6 @@ module ActiveRecord
@association.delete(*records)
end
##
# Destroys the +records+ supplied and removes them from the collection.
# This method will _always_ remove record from the database ignoring
# the +:dependent+ option. Returns an array with the removed records.
@ -642,7 +634,6 @@ module ActiveRecord
@association.destroy(*records)
end
##
# Specifies whether the records should be unique or not.
#
# class Person < ActiveRecord::Base
@ -661,7 +652,6 @@ module ActiveRecord
@association.uniq
end
##
# Count all records using SQL.
#
# class Person < ActiveRecord::Base
@ -679,7 +669,6 @@ module ActiveRecord
@association.count(column_name, options)
end
##
# Returns the size of the collection. If the collection hasn't been loaded,
# it executes a <tt>SELECT COUNT(*)</tt> query.
#
@ -704,7 +693,6 @@ module ActiveRecord
@association.size
end
##
# Returns the size of the collection calling +size+ on the target.
# If the collection has been already loaded, +length+ and +size+ are
# equivalent.
@ -728,7 +716,6 @@ module ActiveRecord
@association.length
end
##
# Returns +true+ if the collection is empty.
#
# class Person < ActiveRecord::Base
@ -746,7 +733,6 @@ module ActiveRecord
@association.empty?
end
##
# Returns +true+ if the collection is not empty.
#
# class Person < ActiveRecord::Base
@ -780,7 +766,6 @@ module ActiveRecord
@association.any?(&block)
end
##
# Returns true if the collection has more than one record.
# Equivalent to <tt>collection.size > 1</tt>.
#
@ -819,7 +804,6 @@ module ActiveRecord
@association.many?(&block)
end
##
# Returns +true+ if the given object is present in the collection.
#
# class Person < ActiveRecord::Base
@ -889,7 +873,7 @@ module ActiveRecord
end
# Returns a new array of objects from the collection. If the collection
# hasn't been loaded, it fetches the records from the database.
# hasn't been loaded, it fetches the records from the database.
#
# class Person < ActiveRecord::Base
# has_many :pets

View file

@ -7,7 +7,7 @@ module ActiveRecord
end
module AttributeMethods
module Dirty
module Dirty # :nodoc:
extend ActiveSupport::Concern
include ActiveModel::Dirty
@ -21,7 +21,7 @@ module ActiveRecord
end
# Attempts to +save+ the record and clears changed attributes if successful.
def save(*) #:nodoc:
def save(*)
if status = super
@previously_changed = changes
@changed_attributes.clear
@ -30,7 +30,7 @@ module ActiveRecord
end
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
def save!(*) #:nodoc:
def save!(*)
super.tap do
@previously_changed = changes
@changed_attributes.clear
@ -38,7 +38,7 @@ module ActiveRecord
end
# <tt>reload</tt> the record and clears changed attributes.
def reload(*) #:nodoc:
def reload(*)
super.tap do
@previously_changed.clear
@changed_attributes.clear

View file

@ -5,28 +5,29 @@ module ActiveRecord
module PrimaryKey
extend ActiveSupport::Concern
# Returns this record's primary key value wrapped in an Array if one is available
# Returns this record's primary key value wrapped in an Array if one is
# available.
def to_key
key = self.id
[key] if key
end
# Returns the primary key value
# Returns the primary key value.
def id
read_attribute(self.class.primary_key)
end
# Sets the primary key value
# Sets the primary key value.
def id=(value)
write_attribute(self.class.primary_key, value) if self.class.primary_key
end
# Queries the primary key value
# Queries the primary key value.
def id?
query_attribute(self.class.primary_key)
end
# Returns the primary key value before type cast
# Returns the primary key value before type cast.
def id_before_type_cast
read_attribute_before_type_cast(self.class.primary_key)
end
@ -52,14 +53,16 @@ module ActiveRecord
super && !ID_ATTRIBUTE_METHODS.include?(method_name)
end
# Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
# primary_key_prefix_type setting, though.
# Defines the primary key field -- can be overridden in subclasses.
# Overwriting will negate any effect of the +primary_key_prefix_type+
# setting, though.
def primary_key
@primary_key = reset_primary_key unless defined? @primary_key
@primary_key
end
# Returns a quoted version of the primary key name, used to construct SQL statements.
# Returns a quoted version of the primary key name, used to construct
# SQL statements.
def quoted_primary_key
@quoted_primary_key ||= connection.quote_column_name(primary_key)
end
@ -92,16 +95,17 @@ module ActiveRecord
# Sets the name of the primary key column.
#
# class Project < ActiveRecord::Base
# self.primary_key = "sysid"
# self.primary_key = 'sysid'
# end
#
# You can also define the primary_key method yourself:
# You can also define the +primary_key+ method yourself:
#
# class Project < ActiveRecord::Base
# def self.primary_key
# "foo_" + super
# 'foo_' + super
# end
# end
#
# Project.primary_key # => "foo_id"
def primary_key=(value)
@primary_key = value && value.to_s

View file

@ -14,9 +14,10 @@ module ActiveRecord
end
module ClassMethods
# +cache_attributes+ allows you to declare which converted attribute values should
# be cached. Usually caching only pays off for attributes with expensive conversion
# methods, like time related columns (e.g. +created_at+, +updated_at+).
# +cache_attributes+ allows you to declare which converted attribute
# values should be cached. Usually caching only pays off for attributes
# with expensive conversion methods, like time related columns (e.g.
# +created_at+, +updated_at+).
def cache_attributes(*attribute_names)
cached_attributes.merge attribute_names.map { |attr| attr.to_s }
end
@ -65,8 +66,9 @@ module ActiveRecord
ActiveRecord::Model.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
# Returns the value of the attribute identified by <tt>attr_name</tt> after
# it has been typecast (for example, "2004-12-12" in a data column is cast
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
return unless attr_name
name_sym = attr_name.to_sym

View file

@ -4,17 +4,19 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
# Returns a hash of all the attributes that have been specified for
# serialization as keys and their class restriction as values.
class_attribute :serialized_attributes, instance_accessor: false
self.serialized_attributes = {}
end
module ClassMethods
# If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
# then specify the name of that attribute using this method and it will be handled automatically.
# The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
# class on retrieval or SerializationTypeMismatch will be raised.
# If you have an attribute that needs to be saved to the database as an
# object, and retrieved as the same object, then specify the name of that
# attribute using this method and it will be handled automatically. The
# serialization is done through YAML. If +class_name+ is specified, the
# serialized object must be of that class on retrieval or
# <tt>SerializationTypeMismatch</tt> will be raised.
#
# ==== Parameters
#
@ -22,7 +24,8 @@ module ActiveRecord
# * +class_name+ - Optional, class name that the object type should be equal to.
#
# ==== Example
# # Serialize a preferences attribute
#
# # Serialize a preferences attribute.
# class User < ActiveRecord::Base
# serialize :preferences
# end
@ -60,7 +63,7 @@ module ActiveRecord
end
end
class Attribute < Struct.new(:coder, :value, :state)
class Attribute < Struct.new(:coder, :value, :state) # :nodoc:
def unserialized_value
state == :serialized ? unserialize : value
end

View file

@ -20,8 +20,9 @@ module ActiveRecord
end
end
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings
# for fixnum and float columns are turned into +nil+.
# Updates the attribute identified by <tt>attr_name</tt> with the
# specified +value+. Empty strings for fixnum and float columns are
# turned into +nil+.
def write_attribute(attr_name, value)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key

View file

@ -1,9 +1,8 @@
require 'yaml'
module ActiveRecord
# :stopdoc:
module Coders
class YAMLColumn
module Coders # :nodoc:
class YAMLColumn # :nodoc:
RESCUE_ERRORS = [ ArgumentError, Psych::SyntaxError ]
attr_accessor :object_class
@ -41,5 +40,4 @@ module ActiveRecord
end
end
end
# :startdoc
end

View file

@ -3,7 +3,7 @@ require 'yaml'
module ActiveRecord
class Fixtures
class File
class File # :nodoc:
include Enumerable
##

View file

@ -108,12 +108,9 @@ module ActiveRecord
# The default inheritance column name is +type+, which means it's a
# reserved word inside Active Record. To be able to use single-table
# inheritance with another column name, or to use the column +type+ in
# your own model for something else, you can override this method to
# return a different name:
# your own model for something else, you can set +inheritance_column+:
#
# def self.inheritance_column
# 'zoink'
# end
# self.inheritance_column = 'zoink'
def inheritance_column
'type'
end

View file

@ -10,7 +10,7 @@ require "action_controller/railtie"
module ActiveRecord
# = Active Record Railtie
class Railtie < Rails::Railtie
class Railtie < Rails::Railtie # :nodoc:
config.active_record = ActiveSupport::OrderedOptions.new
config.app_generators.orm :active_record, :migration => true,
@ -76,7 +76,7 @@ module ActiveRecord
config.after_initialize do |app|
ActiveSupport.on_load(:active_record) do
filename = File.join(app.config.paths["db"].first, "schema_cache.dump")
if File.file?(filename)
cache = Marshal.load File.binread filename
if cache.version == ActiveRecord::Migrator.current_version

View file

@ -2,7 +2,7 @@ require 'active_support/core_ext/module/attr_internal'
require 'active_record/log_subscriber'
module ActiveRecord
module Railties
module Railties # :nodoc:
module ControllerRuntime #:nodoc:
extend ActiveSupport::Concern
@ -37,7 +37,7 @@ module ActiveRecord
end
end
module ClassMethods
module ClassMethods # :nodoc:
def log_process_action(payload)
messages, db_runtime = super, payload[:db_runtime]
messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime

View file

@ -5,17 +5,17 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
# Stores the default scope for the class
# Stores the default scope for the class.
class_attribute :default_scopes, instance_writer: false
self.default_scopes = []
end
module ClassMethods
# Returns a scope for the model without the default_scope.
# Returns a scope for the model without the +default_scope+.
#
# class Post < ActiveRecord::Base
# def self.default_scope
# where :published => true
# where published: true
# end
# end
#
@ -23,16 +23,16 @@ module ActiveRecord
# Post.unscoped.all # Fires "SELECT * FROM posts"
#
# This method also accepts a block. All queries inside the block will
# not use the default_scope:
# not use the +default_scope+:
#
# Post.unscoped {
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
# }
#
# It is recommended that you use the block form of unscoped because
# chaining unscoped with <tt>scope</tt> does not work. Assuming that
# <tt>published</tt> is a <tt>scope</tt>, the following two statements
# are equal: the <tt>default_scope</tt> is applied on both.
# chaining unscoped with +scope+ does not work. Assuming that
# +published+ is a +scope+, the following two statements
# are equal: the +default_scope+ is applied on both.
#
# Post.unscoped.published
# Post.published
@ -50,35 +50,37 @@ module ActiveRecord
# the model.
#
# class Article < ActiveRecord::Base
# default_scope { where(:published => true) }
# default_scope { where(published: true) }
# end
#
# Article.all # => SELECT * FROM articles WHERE published = true
#
# The <tt>default_scope</tt> is also applied while creating/building a record. It is not
# applied while updating a record.
# The +default_scope+ is also applied while creating/building a record.
# It is not applied while updating a record.
#
# Article.new.published # => true
# Article.create.published # => true
#
# (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
# macro, and it will be called when building the default scope.)
# (You can also pass any object which responds to +call+ to the
# +default_scope+ macro, and it will be called when building the
# default scope.)
#
# If you use multiple <tt>default_scope</tt> declarations in your model then they will
# be merged together:
# If you use multiple +default_scope+ declarations in your model then
# they will be merged together:
#
# class Article < ActiveRecord::Base
# default_scope { where(:published => true) }
# default_scope { where(:rating => 'G') }
# default_scope { where(published: true) }
# default_scope { where(rating: 'G') }
# end
#
# Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
#
# This is also the case with inheritance and module includes where the parent or module
# defines a <tt>default_scope</tt> and the child or including class defines a second one.
# This is also the case with inheritance and module includes where the
# parent or module defines a +default_scope+ and the child or including
# class defines a second one.
#
# If you need to do more complex things with a default scope, you can alternatively
# define it as a class method:
# If you need to do more complex things with a default scope, you can
# alternatively define it as a class method:
#
# class Article < ActiveRecord::Base
# def self.default_scope
@ -100,7 +102,7 @@ module ActiveRecord
self.default_scopes = default_scopes + [scope]
end
def build_default_scope #:nodoc:
def build_default_scope # :nodoc:
if !Base.is_a?(method(:default_scope).owner)
# The user has defined their own default scope method, so call that
evaluate_default_scope { default_scope }
@ -117,17 +119,18 @@ module ActiveRecord
end
end
def ignore_default_scope? #:nodoc:
def ignore_default_scope? # :nodoc:
Thread.current["#{self}_ignore_default_scope"]
end
def ignore_default_scope=(ignore) #:nodoc:
def ignore_default_scope=(ignore) # :nodoc:
Thread.current["#{self}_ignore_default_scope"] = ignore
end
# The ignore_default_scope flag is used to prevent an infinite recursion situation where
# a default scope references a scope which has a default scope which references a scope...
def evaluate_default_scope
# The ignore_default_scope flag is used to prevent an infinite recursion
# situation where a default scope references a scope which has a default
# scope which references a scope...
def evaluate_default_scope # :nodoc:
return if ignore_default_scope?
begin

View file

@ -3,7 +3,7 @@ require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/kernel/singleton_class'
module ActiveRecord
# = Active Record Named \Scopes
# = Active Record \Named \Scopes
module Scoping
module Named
extend ActiveSupport::Concern
@ -16,11 +16,11 @@ module ActiveRecord
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
#
# fruits = Fruit.all
# fruits = fruits.where(:color => 'red') if options[:red_only]
# fruits = fruits.where(color: 'red') if options[:red_only]
# fruits = fruits.limit(10) if limited?
#
# You can define a \scope that applies to all finders using
# ActiveRecord::Base.default_scope.
# You can define a scope that applies to all finders using
# <tt>ActiveRecord::Base.default_scope</tt>.
def all
if current_scope
current_scope.clone
@ -31,7 +31,6 @@ module ActiveRecord
end
end
##
# Collects attributes from scopes that should be applied when creating
# an AR instance for the particular class this is called on.
def scope_attributes # :nodoc:
@ -44,86 +43,70 @@ module ActiveRecord
end
end
##
# Are there default attributes associated with this scope?
def scope_attributes? # :nodoc:
current_scope || default_scopes.any?
end
# Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
# such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
# Adds a class method for retrieving and querying objects. A \scope
# represents a narrowing of a database query, such as
# <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
#
# class Shirt < ActiveRecord::Base
# scope :red, where(:color => 'red')
# scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true)
# scope :red, -> { where(color: 'red') }
# scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
# end
#
# The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
# in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>.
# The above calls to +scope+ define class methods <tt>Shirt.red</tt> and
# <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
# represents the query <tt>Shirt.where(color: 'red')</tt>.
#
# Note that this is simply 'syntactic sugar' for defining an actual class method:
# You should always pass a callable object to the scopes defined
# with +scope+. This ensures that the scope is re-evaluated each
# time it is called.
#
# Note that this is simply 'syntactic sugar' for defining an actual
# class method:
#
# class Shirt < ActiveRecord::Base
# def self.red
# where(:color => 'red')
# where(color: 'red')
# end
# end
#
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it
# resembles the association object constructed by a <tt>has_many</tt> declaration. For instance,
# you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>.
# Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable;
# <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt>
# all behave as if Shirt.red really was an Array.
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
# <tt>Shirt.red</tt> is not an Array; it resembles the association object
# constructed by a +has_many+ declaration. For instance, you can invoke
# <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
# association objects, named \scopes act like an Array, implementing
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
# <tt>Shirt.red</tt> really was an Array.
#
# These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce
# all shirts that are both red and dry clean only.
# Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
# returns the number of garments for which these criteria obtain. Similarly with
# <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
# These named \scopes are composable. For instance,
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
# both red and dry clean only. Nested finds and calculations also work
# with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
# returns the number of garments for which these criteria obtain.
# Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
#
# All \scopes are available as class methods on the ActiveRecord::Base descendant upon which
# the \scopes were defined. But they are also available to <tt>has_many</tt> associations. If,
# All scopes are available as class methods on the ActiveRecord::Base
# descendant upon which the \scopes were defined. But they are also
# available to +has_many+ associations. If,
#
# class Person < ActiveRecord::Base
# has_many :shirts
# end
#
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
# only shirts.
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
# Elton's red, dry clean only shirts.
#
# Named \scopes can also be procedural:
# \Named scopes can also have extensions, just as with +has_many+
# declarations:
#
# class Shirt < ActiveRecord::Base
# scope :colored, lambda { |color| where(:color => color) }
# end
#
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
#
# On Ruby 1.9 you can use the 'stabby lambda' syntax:
#
# scope :colored, ->(color) { where(:color => color) }
#
# Note that scopes defined with \scope will be evaluated when they are defined, rather than
# when they are used. For example, the following would be incorrect:
#
# class Post < ActiveRecord::Base
# scope :recent, where('published_at >= ?', Time.current - 1.week)
# end
#
# The example above would be 'frozen' to the <tt>Time.current</tt> value when the <tt>Post</tt>
# class was defined, and so the resultant SQL query would always be the same. The correct
# way to do this would be via a lambda, which will re-evaluate the scope each time
# it is called:
#
# class Post < ActiveRecord::Base
# scope :recent, lambda { where('published_at >= ?', Time.current - 1.week) }
# end
#
# Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
#
# class Shirt < ActiveRecord::Base
# scope :red, where(:color => 'red') do
# scope :red, -> { where(color: 'red') } do
# def dom_id
# 'red_shirts'
# end
@ -133,18 +116,18 @@ module ActiveRecord
# Scopes can also be used while creating/building a record.
#
# class Article < ActiveRecord::Base
# scope :published, where(:published => true)
# scope :published, -> { where(published: true) }
# end
#
# Article.published.new.published # => true
# Article.published.create.published # => true
#
# Class methods on your model are automatically available
# \Class methods on your model are automatically available
# on scopes. Assuming the following setup:
#
# class Article < ActiveRecord::Base
# scope :published, where(:published => true)
# scope :featured, where(:featured => true)
# scope :published, -> { where(published: true) }
# scope :featured, -> { where(featured: true) }
#
# def self.latest_article
# order('published_at desc').first

View file

@ -10,8 +10,8 @@ module ActiveRecord
# puts invalid.record.errors
# end
class RecordInvalid < ActiveRecordError
attr_reader :record
def initialize(record)
attr_reader :record # :nodoc:
def initialize(record) # :nodoc:
@record = record
errors = @record.errors.full_messages.join(", ")
super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
@ -44,23 +44,24 @@ module ActiveRecord
end
end
# The validation process on save can be skipped by passing <tt>:validate => false</tt>. The regular Base#save method is
# replaced with this when the validations module is mixed in, which it is by default.
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
# The regular Base#save method is replaced with this when the validations
# module is mixed in, which it is by default.
def save(options={})
perform_validations(options) ? super : false
end
# Attempts to save the record just like Base#save but will raise a +RecordInvalid+ exception instead of returning false
# if the record is not valid.
# Attempts to save the record just like Base#save but will raise a +RecordInvalid+
# exception instead of returning +false+ if the record is not valid.
def save!(options={})
perform_validations(options) ? super : raise(RecordInvalid.new(self))
end
# Runs all the validations within the specified context. Returns true if no errors are found,
# false otherwise.
# Runs all the validations within the specified context. Returns +true+ if
# no errors are found, +false+ otherwise.
#
# If the argument is false (default is +nil+), the context is set to <tt>:create</tt> if
# <tt>new_record?</tt> is true, and to <tt>:update</tt> if it is not.
# If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
# <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
#
# Validations with no <tt>:on</tt> option will run no matter the context. Validations with
# some <tt>:on</tt> option will only run in the specified context.
@ -72,7 +73,7 @@ module ActiveRecord
protected
def perform_validations(options={})
def perform_validations(options={}) # :nodoc:
perform_validation = options[:validate] != false
perform_validation ? valid?(options[:context]) : true
end

View file

@ -38,7 +38,7 @@ module ActiveRecord
# proc or string should return or evaluate to a +true+ or +false+ value.
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
# determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
# or <tt>unless: => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a +true+ or +false+
# value.
def validates_associated(*attr_names)

View file

@ -1,6 +1,6 @@
module ActiveRecord
module Validations
class PresenceValidator < ActiveModel::Validations::PresenceValidator
class PresenceValidator < ActiveModel::Validations::PresenceValidator # :nodoc:
def validate(record)
super
attributes.each do |attribute|
@ -29,7 +29,7 @@ module ActiveRecord
#
# If you want to validate the presence of a boolean field (where the real values
# are true and false), you will want to use
# <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>.
# <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
#
# This is due to the way Object#blank? handles boolean values:
# <tt>false.blank? # => true</tt>.
@ -46,16 +46,15 @@ module ActiveRecord
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
# the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
# <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
# or string should return or evaluate to a true or false value.
# the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
# <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
# or string should return or evaluate to a +true+ or +false+ value.
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
# if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
# or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
# proc or string should return or evaluate to a true or false value.
# if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
# proc or string should return or evaluate to a +true+ or +false+ value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
#
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
end

View file

@ -2,7 +2,7 @@ require 'active_support/core_ext/array/prepend_and_append'
module ActiveRecord
module Validations
class UniquenessValidator < ActiveModel::EachValidator #:nodoc:
class UniquenessValidator < ActiveModel::EachValidator # :nodoc:
def initialize(options)
super(options.reverse_merge(:case_sensitive => true))
end
@ -199,7 +199,7 @@ module ActiveRecord
# can catch it and restart the transaction (e.g. by telling the user
# that the title already exists, and asking him to re-enter the title).
# This technique is also known as optimistic concurrency control:
# http://en.wikipedia.org/wiki/Optimistic_concurrency_control
# http://en.wikipedia.org/wiki/Optimistic_concurrency_control.
#
# The bundled ActiveRecord::ConnectionAdapters distinguish unique index
# constraint errors from other types of database errors by throwing an
@ -209,10 +209,10 @@ module ActiveRecord
#
# The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
#
# * ActiveRecord::ConnectionAdapters::MysqlAdapter
# * ActiveRecord::ConnectionAdapters::Mysql2Adapter
# * ActiveRecord::ConnectionAdapters::SQLite3Adapter
# * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
# * ActiveRecord::ConnectionAdapters::MysqlAdapter.
# * ActiveRecord::ConnectionAdapters::Mysql2Adapter.
# * ActiveRecord::ConnectionAdapters::SQLite3Adapter.
# * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
def validates_uniqueness_of(*attr_names)
validates_with UniquenessValidator, _merge_attributes(attr_names)
end

View file

@ -2,7 +2,17 @@ require 'date'
require 'active_support/core_ext/time/calculations'
class String
# Form can be either :utc (default) or :local.
# Converts a string to a Time value.
# The +form+ can be either :utc or :local (default :utc).
#
# The time is parsed using Date._parse method.
# If +form+ is :local, then time is formatted using Time.zone
#
# "3-2-2012".to_time # => 2012-02-03 00:00:00 UTC
# "12:20".to_time # => ArgumentError: invalid date
# "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 UTC
# "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 UTC
# "2012-12-13T06:12".to_time(:local) # => 2012-12-13 06:12:00 +0100
def to_time(form = :utc)
unless blank?
date_values = ::Date._parse(self, false).

View file

@ -25,7 +25,17 @@ module ActiveSupport
# == Subscribers
#
# You can consume those events and the information they provide by registering
# a subscriber. For instance, let's store all "render" events in an array:
# a subscriber.
#
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
# name # => String, name of the event (such as 'render' from above)
# start # => Time, when the instrumented block started execution
# finish # => Time, when the instrumented block ended execution
# id # => String, unique ID for this notification
# payload # => Hash, the payload
# end
#
# For instance, let's store all "render" events in an array:
#
# events = []
#

View file

@ -538,7 +538,7 @@ Active Record
* `mysql` and `mysql2` connections will set `SQL_MODE=STRICT_ALL_TABLES` by default to avoid silent data loss. This can be disabled by specifying `strict: false` in `config/database.yml`.
* Added default order to `ActiveRecord::Base#first` to assure consistent results among diferent database engines. Introduced `ActiveRecord::Base#take` as a replacement to the old behavior.
* Added default order to `ActiveRecord::Base#first` to assure consistent results among different database engines. Introduced `ActiveRecord::Base#take` as a replacement to the old behavior.
* Added an `:index` option to automatically create indexes for `references` and `belongs_to` statements in migrations. This can be either a boolean or a hash that is identical to options available to the `add_index` method:
@ -652,6 +652,14 @@ Active Record
* PostgreSQL hstore types are automatically deserialized from the database.
* Added `#update_columns` method which updates the attributes from the passed-in hash without calling save, hence skipping validations and callbacks. `ActiveRecordError` will be raised when called on new objects or when at least one of the attributes is marked as read only.
```ruby
post.attributes # => {"id"=>2, "title"=>"My title", "body"=>"My content", "author"=>"Peter"}
post.update_columns({title: 'New title', author: 'Sebastian'}) # => true
post.attributes # => {"id"=>2, "title"=>"New title", "body"=>"My content", "author"=>"Sebastian"}
```
### Deprecations
* Deprecated most of the 'dynamic finder' methods. All dynamic methods except for `find_by_...` and `find_by_...!` are deprecated. Here's how you can rewrite the code:

View file

@ -482,7 +482,6 @@ The following configuration options are best made in one of the environment file
|`deliveries`|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
|`default_options`|Allows you to set default values for the `mail` method options (`:from`, `:reply_to`, etc.).|
|`async`|Setting this flag will turn on asynchronous message sending, message rendering and delivery will be pushed to `Rails.queue` for processing.|
|`default_options`|Allows you to set default values for the `mail` method options (`:from`, `:reply_to`, etc.).|
### Example Action Mailer Configuration

View file

@ -68,7 +68,6 @@ The methods are:
* `none`
* `offset`
* `order`
* `none`
* `preload`
* `readonly`
* `references`
@ -547,8 +546,6 @@ By default, `Model.find` selects all the fields from the result set using `selec
To select only a subset of fields from the result set, you can specify the subset via the `select` method.
NOTE: If the `select` method is used, all the returning objects will be [read only](#readonly-objects).
For example, to select only `viewable_by` and `locked` columns:
```ruby

View file

@ -42,7 +42,7 @@ The first feature of the pipeline is to concatenate assets. This is important in
Rails 2.x introduced the ability to concatenate JavaScript and CSS assets by placing `:cache => true` at the end of the `javascript_include_tag` and `stylesheet_link_tag` methods. But this technique has some limitations. For example, it cannot generate the caches in advance, and it is not able to transparently include assets provided by third-party libraries.
Starting with version 3.1, Rails defaults to concatenating all JavaScript files into one master `.js` file and all CSS files into one master `.css` file. As you'll learn later in this guide, you can customize this strategy to group files any way you like. In production, Rails inserts an MD5 fingerprint into each filename so that the file is cached by the web browser. You can invalidate the cache by altering this fingerprint, which happens automatically whenever you change the file contents..
Starting with version 3.1, Rails defaults to concatenating all JavaScript files into one master `.js` file and all CSS files into one master `.css` file. As you'll learn later in this guide, you can customize this strategy to group files any way you like. In production, Rails inserts an MD5 fingerprint into each filename so that the file is cached by the web browser. You can invalidate the cache by altering this fingerprint, which happens automatically whenever you change the file contents.
The second feature of the asset pipeline is asset minification or compression. For CSS files, this is done by removing whitespace and comments. For JavaScript, more complex processes can be applied. You can choose from a set of built in options or specify your own.

View file

@ -638,60 +638,7 @@ Debugging Memory Leaks
A Ruby application (on Rails or not), can leak memory - either in the Ruby code or at the C code level.
In this section, you will learn how to find and fix such leaks by using tools such as BleakHouse and Valgrind.
### BleakHouse
[BleakHouse](https://github.com/evan/bleak_house/) is a library for finding memory leaks.
If a Ruby object does not go out of scope, the Ruby Garbage Collector won't sweep it since it is referenced somewhere. Leaks like this can grow slowly and your application will consume more and more memory, gradually affecting the overall system performance. This tool will help you find leaks on the Ruby heap.
To install it run:
```bash
$ gem install bleak_house
```
Then setup your application for profiling. Then add the following at the bottom of config/environment.rb:
```ruby
require 'bleak_house' if ENV['BLEAK_HOUSE']
```
Start a server instance with BleakHouse integration:
```bash
$ RAILS_ENV=production BLEAK_HOUSE=1 ruby-bleak-house rails server
```
Make sure to run a couple hundred requests to get better data samples, then press `CTRL-C`. The server will stop and Bleak House will produce a dumpfile in `/tmp`:
```
** BleakHouse: working...
** BleakHouse: complete
** Bleakhouse: run 'bleak /tmp/bleak.5979.0.dump' to analyze.
```
To analyze it, just run the listed command. The top 20 leakiest lines will be listed:
```
191691 total objects
Final heap size 191691 filled, 220961 free
Displaying top 20 most common line/class pairs
89513 __null__:__null__:__node__
41438 __null__:__null__:String
2348 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:Array
1508 /opt/local//lib/ruby/gems/1.8/specifications/gettext-1.90.0.gemspec:14:String
1021 /opt/local//lib/ruby/gems/1.8/specifications/heel-0.2.0.gemspec:14:String
951 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:111:String
935 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:String
834 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:146:Array
...
```
This way you can find where your application is leaking memory and fix it.
If [BleakHouse](https://github.com/evan/bleak_house/) doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or real leak in the interpreter. In that case, try using Valgrind to investigate further.
In this section, you will learn how to find and fix such leaks by using tool such as Valgrind.
### Valgrind
@ -724,4 +671,3 @@ References
* [Debugging with ruby-debug](http://bashdb.sourceforge.net/ruby-debug.html)
* [ruby-debug cheat sheet](http://cheat.errtheblog.com/s/rdebug/)
* [Ruby on Rails Wiki: How to Configure Logging](http://wiki.rubyonrails.org/rails/pages/HowtoConfigureLogging)
* [Bleak House Documentation](http://blog.evanweaver.com/files/doc/fauna/bleak_house/)

View file

@ -816,7 +816,8 @@ So, for example, instead of the default error message `"can not be blank"` you c
#### Translations for the Active Record `error_messages_for` Helper
If you are using the Active Record `error_messages_for` helper, you will want to add translations for it.
If you are using the Active Record `error_messages_for` helper, you will want to add
translations for it.
Rails ships with the following translations:
@ -831,6 +832,9 @@ en:
body: "There were problems with the following fields:"
```
NOTE: In order to use this helper, you need to install [DynamicForm](https://github.com/joelmoss/dynamic_form)
gem by adding this line to your Gemfile: `gem 'dynamic_form'`.
### Overview of Other Built-In Methods that Provide I18n Support
Rails uses fixed strings and other localizations, such as format strings and other format information in a couple of helpers. Here's a brief overview.

View file

@ -401,6 +401,18 @@ resources :photos do
end
```
#### Adding Routes for Additional New Actions
To add an alternate new action using the `:on` shortcut:
```ruby
resources :comments do
get 'preview', :on => :new
end
```
This will enable Rails to recognize paths such as `/comments/new/preview` with GET, and route to the `preview` action of `CommentsController`. It will also create the `preview_new_comment_url` and `preview_new_comment_path` route helpers.
#### A Note of Caution
If you find yourself adding many extra actions to a resourceful route, it's time to stop and ask yourself whether you're disguising the presence of another resource.

View file

@ -51,7 +51,21 @@ Rails 4.0 has changed `serialized_attributes` and `attr_readonly` to class metho
### Active Model
Rails 4.0 has changed how errors attach with the `ActiveModel::Validations::ConfirmationValidator`. Now when confirmation validations fail the error will be attached to `:#{attribute}_confirmation` instead of `attribute`.
Rails 4.0 has changed how errors attach with the `ActiveModel::Validations::ConfirmationValidator`.
Now when confirmation validations fail the error will be attached to
`:#{attribute}_confirmation` instead of `attribute`.
Rails 4.0 has changed `ActiveModel::Serializers::JSON.include_root_in_json` default
value to `false`. Now, Active Model Serializers and Active Record objects have the
same default behaviour. This means that you can comment or remove the following option
in the `config/initializers/wrap_parameters.rb` file:
```ruby
# Disable root element in JSON by default.
# ActiveSupport.on_load(:active_record) do
# self.include_root_in_json = false
# end
```
### Action Pack

View file

@ -8,9 +8,6 @@ require 'rbconfig'
task :default => :test
task :test => 'test:isolated'
## This is required until the regular test task
## below passes. It's not ideal, but at least
## we can see the failures
namespace :test do
task :isolated do
dir = ENV["TEST_DIR"] || "**"