aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb4
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb2
-rw-r--r--actionpack/lib/action_view/base.rb2
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb2
-rw-r--r--actionpack/lib/sprockets/railtie.rb9
-rw-r--r--actionpack/test/template/test_case_test.rb4
-rw-r--r--activerecord/lib/active_record/associations.rb50
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb22
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb2
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb34
-rw-r--r--activerecord/lib/active_record/errors.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb6
-rw-r--r--activerecord/lib/active_record/persistence.rb7
-rw-r--r--activerecord/lib/active_record/relation.rb15
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb6
-rw-r--r--activerecord/test/cases/adapter_test.rb14
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb4
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb14
-rw-r--r--activerecord/test/cases/associations/eager_test.rb8
-rw-r--r--activerecord/test/cases/base_test.rb10
-rw-r--r--activerecord/test/cases/finder_test.rb17
-rw-r--r--activerecord/test/cases/fixtures_test.rb4
-rw-r--r--activerecord/test/cases/habtm_destroy_order_test.rb10
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb8
-rw-r--r--activerecord/test/cases/primary_keys_test.rb2
-rw-r--r--activerecord/test/schema/schema.rb4
-rw-r--r--activesupport/lib/active_support/cache.rb4
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/test/caching_test.rb23
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb49
-rw-r--r--railties/guides/source/active_record_querying.textile2
-rw-r--r--railties/guides/source/asset_pipeline.textile37
-rw-r--r--railties/guides/source/debugging_rails_applications.textile26
-rw-r--r--railties/guides/source/form_helpers.textile2
-rw-r--r--railties/guides/source/security.textile10
-rw-r--r--railties/lib/rails.rb9
-rw-r--r--railties/lib/rails/application.rb9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb5
-rw-r--r--railties/test/application/configuration_test.rb11
47 files changed, 361 insertions, 125 deletions
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 2bdb23c4d7..abeb49d16f 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -12,13 +12,13 @@ module ActionController #:nodoc:
#
# <% cache do %>
# All the topics in the system:
- # <%= render :partial => "topic", :collection => Topic.find(:all) %>
+ # <%= render :partial => "topic", :collection => Topic.all %>
# <% end %>
#
# This cache will bind the name of the action that called it, so if
# this code was part of the view for the topics/list action, you
# would be able to invalidate it using:
- #
+ #
# expire_fragment(:controller => "topics", :action => "list")
#
# This default behavior is limited if you need to cache multiple
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 75757db564..2df0e9422c 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -29,7 +29,7 @@ module ActionController
# class EventsController < ActionController::Base
# helper FormattedTimeHelper
# def index
- # @events = Event.find(:all)
+ # @events = Event.all
# end
# end
#
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index f3b7357e64..3794e277f6 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -9,7 +9,7 @@ module ActionController #:nodoc:
# respond_to :html, :xml, :json
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
# respond_with(@people)
# end
# end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index fd2970b8e2..36a0a20066 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -194,7 +194,7 @@ module ActionView #:nodoc:
end
def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
- @_config = {}
+ @_config = ActiveSupport::InheritableOptions.new
# Handle all these for backwards compatibility.
# TODO Provide a new API for AV::Base and deprecate this one.
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 0c3f011c92..509c29844a 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -1,6 +1,7 @@
require 'action_view/helpers/asset_tag_helpers/javascript_tag_helpers'
require 'action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers'
require 'action_view/helpers/asset_tag_helpers/asset_paths'
+require 'action_view/helpers/tag_helper'
module ActionView
# = Action View Asset Tag Helpers
@@ -191,6 +192,7 @@ module ActionView
# RewriteEngine On
# RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L]
module AssetTagHelper
+ include TagHelper
include JavascriptTagHelpers
include StylesheetTagHelpers
# Returns a link tag that browsers and news readers can use to auto-detect
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index a087688a2c..39c37b25dc 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -20,7 +20,7 @@ module ActionView
# # GET /posts.html
# # GET /posts.atom
# def index
- # @posts = Post.find(:all)
+ # @posts = Post.all
#
# respond_to do |format|
# format.html
diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb
index c28bdc3061..e0124def2b 100644
--- a/actionpack/lib/sprockets/railtie.rb
+++ b/actionpack/lib/sprockets/railtie.rb
@@ -70,8 +70,13 @@ module Sprockets
if assets.compress
# temporarily hardcode default JS compressor to uglify. Soon, it will work
# the same as SCSS, where a default plugin sets the default.
- env.js_compressor = LazyCompressor.new { expand_js_compressor(assets.js_compressor || :uglifier) }
- env.css_compressor = LazyCompressor.new { expand_css_compressor(assets.css_compressor) }
+ unless assets.js_compressor == false
+ env.js_compressor = LazyCompressor.new { expand_js_compressor(assets.js_compressor || :uglifier) }
+ end
+
+ unless assets.css_compressor == false
+ env.css_compressor = LazyCompressor.new { expand_css_compressor(assets.css_compressor) }
+ end
end
env
diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb
index f463675a2e..a75f1bc981 100644
--- a/actionpack/test/template/test_case_test.rb
+++ b/actionpack/test/template/test_case_test.rb
@@ -45,6 +45,10 @@ module ActionView
assert_same _view, view
end
+ test "retrieve non existing config values" do
+ assert_equal nil, ActionView::Base.new.config.something_odd
+ end
+
test "works without testing a helper module" do
assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy'))
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 2cbfa42718..029d7a9b15 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -191,7 +191,7 @@ module ActiveRecord
# * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
# * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
# * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
- # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt>
+ # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.all(options),</tt>
# <tt>Project#milestones.build, Project#milestones.create</tt>
# * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
# <tt>Project#categories.delete(category1)</tt>
@@ -420,7 +420,7 @@ module ActiveRecord
# end
# end
#
- # person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
+ # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
# person.first_name # => "David"
# person.last_name # => "Heinemeier Hansson"
#
@@ -477,7 +477,7 @@ module ActiveRecord
# belongs_to :book
# end
#
- # @author = Author.find :first
+ # @author = Author.first
# @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
# @author.books # selects all books by using the Authorship join model
#
@@ -497,7 +497,7 @@ module ActiveRecord
# belongs_to :client
# end
#
- # @firm = Firm.find :first
+ # @firm = Firm.first
# @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
# @firm.invoices # selects all invoices by going through the Client join model
#
@@ -669,7 +669,7 @@ module ActiveRecord
# To iterate over these one hundred posts, we'll generate 201 database queries. Let's
# first just optimize it for retrieving the author:
#
- # Post.find(:all, :include => :author).each do |post|
+ # Post.includes(:author).each do |post|
#
# This references the name of the +belongs_to+ association that also used the <tt>:author</tt>
# symbol. After loading the posts, find will collect the +author_id+ from each one and load
@@ -678,7 +678,7 @@ module ActiveRecord
#
# We can improve upon the situation further by referencing both associations in the finder with:
#
- # Post.find(:all, :include => [ :author, :comments ]).each do |post|
+ # Post.includes(:author, :comments).each do |post|
#
# This will load all comments with a single query. This reduces the total number of queries
# to 3. More generally the number of queries will be 1 plus the number of associations
@@ -686,7 +686,7 @@ module ActiveRecord
#
# To include a deep hierarchy of associations, use a hash:
#
- # Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ]).each do |post|
+ # Post.includes(:author, {:comments => {:author => :gravatar}}).each do |post|
#
# That'll grab not only all the comments but all their authors and gravatar pictures.
# You can mix and match symbols, arrays and hashes in any combination to describe the
@@ -714,13 +714,13 @@ module ActiveRecord
# <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
#
# If you do want eager load only some members of an association it is usually more natural
- # to <tt>:include</tt> an association which has conditions defined on it:
+ # to include an association which has conditions defined on it:
#
# class Post < ActiveRecord::Base
# has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true]
# end
#
- # Post.find(:all, :include => :approved_comments)
+ # Post.includes(:approved_comments)
#
# This will load posts and eager load the +approved_comments+ association, which contains
# only those comments that have been approved.
@@ -732,7 +732,7 @@ module ActiveRecord
# has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
# end
#
- # Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
+ # Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments.
#
# When eager loaded, conditions are interpolated in the context of the model class, not
# the model instance. Conditions are lazily interpolated before the actual model exists.
@@ -745,7 +745,7 @@ module ActiveRecord
#
# A call that tries to eager load the addressable model
#
- # Address.find(:all, :include => :addressable)
+ # Address.includes(:addressable)
#
# This will execute one query to load the addresses and load the addressables with one
# query per addressable type.
@@ -763,43 +763,43 @@ module ActiveRecord
# second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
# Indexes are appended for any more successive uses of the table name.
#
- # Post.find :all, :joins => :comments
+ # Post.joins(:comments)
# # => SELECT ... FROM posts INNER JOIN comments ON ...
- # Post.find :all, :joins => :special_comments # STI
+ # Post.joins(:special_comments) # STI
# # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
- # Post.find :all, :joins => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
+ # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
# # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
#
# Acts as tree example:
#
- # TreeMixin.find :all, :joins => :children
+ # TreeMixin.joins(:children)
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
- # TreeMixin.find :all, :joins => {:children => :parent}
+ # TreeMixin.joins(:children => :parent)
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
# INNER JOIN parents_mixins ...
- # TreeMixin.find :all, :joins => {:children => {:parent => :children}}
+ # TreeMixin.joins(:children => {:parent => :children})
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
# INNER JOIN parents_mixins ...
# INNER JOIN mixins childrens_mixins_2
#
# Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
#
- # Post.find :all, :joins => :categories
+ # Post.joins(:categories)
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
- # Post.find :all, :joins => {:categories => :posts}
+ # Post.joins(:categories => :posts)
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
- # Post.find :all, :joins => {:categories => {:posts => :categories}}
+ # Post.joins(:categories => {:posts => :categories})
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
# INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
#
- # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table
+ # If you wish to specify your own custom joins using <tt>joins</tt> method, those table
# names will take precedence over the eager associations:
#
- # Post.find :all, :joins => :comments, :joins => "inner join comments ..."
+ # Post.joins(:comments).joins("inner join comments ...")
# # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
- # Post.find :all, :joins => [:comments, :special_comments], :joins => "inner join comments ..."
+ # Post.joins(:comments, :special_comments).joins("inner join comments ...")
# # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
# INNER JOIN comments special_comments_posts ...
# INNER JOIN comments ...
@@ -1031,7 +1031,7 @@ module ActiveRecord
# === Example
#
# Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
- # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => ["firm_id = ?", id]</tt>)
+ # * <tt>Firm#clients</tt> (similar to <tt>Clients.all :conditions => ["firm_id = ?", id]</tt>)
# * <tt>Firm#clients<<</tt>
# * <tt>Firm#clients.delete</tt>
# * <tt>Firm#clients=</tt>
@@ -1195,7 +1195,7 @@ module ActiveRecord
# === Example
#
# An Account class declares <tt>has_one :beneficiary</tt>, which will add:
- # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
+ # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.first(:conditions => "account_id = #{id}")</tt>)
# * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
# * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
# * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index d7632b2ea9..30fc44b4c2 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -7,24 +7,22 @@ module ActiveRecord::Associations::Builder
def build
reflection = super
check_validity(reflection)
- define_after_destroy_method
+ define_destroy_hook
reflection
end
private
- def define_after_destroy_method
+ def define_destroy_hook
name = self.name
- model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
- def #{after_destroy_method_name}
- association(#{name.to_sym.inspect}).delete_all
- end
- eoruby
- model.after_destroy after_destroy_method_name
- end
-
- def after_destroy_method_name
- "has_and_belongs_to_many_after_destroy_for_#{name}"
+ model.send(:include, Module.new {
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def destroy_associations
+ association(#{name.to_sym.inspect}).delete_all
+ super
+ end
+ RUBY
+ })
end
# TODO: These checks should probably be moved into the Reflection, and we should not be
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 7f7dec467a..827dfb7ccb 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -12,7 +12,7 @@ module ActiveRecord
# has_many :posts
# end
#
- # blog = Blog.find(:first)
+ # blog = Blog.first
#
# the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
# <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index 6b010064d5..a1a921bcb4 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -18,11 +18,11 @@ module ActiveRecord
end
def create(attributes = {}, options = {}, &block)
- build(attributes, options, &block).tap { |record| record.save }
+ create_record(attributes, options, &block)
end
def create!(attributes = {}, options = {}, &block)
- build(attributes, options, &block).tap { |record| record.save! }
+ create_record(attributes, options, true, &block)
end
def build(attributes = {}, options = {})
@@ -50,6 +50,15 @@ module ActiveRecord
def set_new_record(record)
replace(record)
end
+
+ def create_record(attributes, options, raise_error = false)
+ record = build_record(attributes, options)
+ yield(record) if block_given?
+ saved = record.save
+ set_new_record(record)
+ raise RecordInvalid.new(record) if !saved && raise_error
+ record
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index e3f221c773..8bd898d126 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -7,7 +7,7 @@ module ActiveRecord
# the record is not persisted? or has just been destroyed.
def to_key
key = send(self.class.primary_key)
- [key] if key
+ persisted? && key ? [key] : nil
end
module ClassMethods
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 29dd0a9ea3..a84f73c73f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -670,7 +670,7 @@ module ActiveRecord
# If the schema is not specified as part of +name+ then it will only find tables within
# the current schema search path (regardless of permissions to access tables in other schemas)
def table_exists?(name)
- schema, table = extract_schema_and_table(name.to_s)
+ schema, table = Utils.extract_schema_and_table(name.to_s)
return false unless table
binds = [[nil, table]]
@@ -947,6 +947,23 @@ module ActiveRecord
"DISTINCT #{columns}, #{order_columns * ', '}"
end
+ module Utils
+ # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
+ # +schema_name+ is nil if not specified in +name+.
+ # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
+ # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
+ #
+ # * <tt>table_name</tt>
+ # * <tt>"table.name"</tt>
+ # * <tt>schema_name.table_name</tt>
+ # * <tt>schema_name."table.name"</tt>
+ # * <tt>"schema.name"."table name"</tt>
+ def self.extract_schema_and_table(name)
+ table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
+ [schema, table]
+ end
+ end
+
protected
# Returns the version of the connected PostgreSQL server.
def postgresql_version
@@ -1085,21 +1102,6 @@ module ActiveRecord
end
end
- # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
- # +schema_name+ is nil if not specified in +name+.
- # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
- # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
- #
- # * <tt>table_name</tt>
- # * <tt>"table.name"</tt>
- # * <tt>schema_name.table_name</tt>
- # * <tt>schema_name."table.name"</tt>
- # * <tt>"schema.name"."table name"</tt>
- def extract_schema_and_table(name)
- table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
- [schema, table]
- end
-
def extract_table_ref_from_insert_sql(sql)
sql[/into\s+([^\(]*).*values\s*\(/i]
$1.strip if $1
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index ea1709cb1f..ad7d8cd63c 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -87,7 +87,7 @@ module ActiveRecord
#
# For example, in
#
- # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
+ # Location.where("lat = ? AND lng = ?", 53.7362)
#
# two placeholders are given but only one variable to fill them.
class PreparedStatementInvalid < ActiveRecordError
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 819743a361..3f36dcde14 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -149,13 +149,13 @@ class FixturesFileNotFound < StandardError; end
# self.use_transactional_fixtures = true
#
# test "godzilla" do
-# assert !Foo.find(:all).empty?
+# assert !Foo.all.empty?
# Foo.destroy_all
-# assert Foo.find(:all).empty?
+# assert Foo.all.empty?
# end
#
# test "godzilla aftermath" do
-# assert !Foo.find(:all).empty?
+# assert !Foo.all.empty?
# end
# end
#
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index f425118f59..d9cd7987b0 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -79,6 +79,8 @@ module ActiveRecord
# Deletes the record in the database and freezes this instance to reflect
# that no changes should be made (since they can't be persisted).
def destroy
+ destroy_associations
+
if persisted?
IdentityMap.remove(self) if IdentityMap.enabled?
pk = self.class.primary_key
@@ -284,6 +286,11 @@ module ActiveRecord
end
private
+
+ # A hook to be overriden by association modules.
+ def destroy_associations
+ end
+
def create_or_update
raise ReadOnlyRecord if readonly?
result = new_record? ? create : update
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index aa7f7d6cea..2d0861d5c9 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -250,8 +250,7 @@ module ActiveRecord
# Person.update(people.keys, people.values)
def update(id, attributes)
if id.is_a?(Array)
- idx = -1
- id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
+ id.each.with_index.map {|one_id, idx| update(one_id, attributes[idx])}
else
object = find(id)
object.update_attributes(attributes)
@@ -409,7 +408,17 @@ module ActiveRecord
end
def eager_loading?
- @should_eager_load ||= (@eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?))
+ @should_eager_load ||=
+ @eager_load_values.any? ||
+ @includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
+ end
+
+ # Joins that are also marked for preloading. In which case we should just eager load them.
+ # Note that this is a naive implementation because we could have strings and symbols which
+ # represent the same association, but that aren't matched by this. Also, we could have
+ # nested hashes which partially match, e.g. { :a => :b } & { :a => [:b, :c] }
+ def joined_includes_values
+ @includes_values & @joins_values
end
def ==(other)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 422d89060b..283115a448 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -193,8 +193,8 @@ module ActiveRecord
else
relation = relation.where(table[primary_key].eq(id)) if id
end
-
- connection.select_value(relation.to_sql) ? true : false
+
+ connection.select_value(relation.to_sql, "#{name} Exists") ? true : false
end
protected
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index d17861f407..8bd4732c0c 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -96,11 +96,11 @@ module ActiveRecord
relation
end
- def having(*args)
- return self if args.blank?
+ def having(opts, *rest)
+ return self if opts.blank?
relation = clone
- relation.having_values += build_where(*args)
+ relation.having_values += build_where(opts, rest)
relation
end
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 07de544868..4c65193d75 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -146,4 +146,18 @@ class AdapterTest < ActiveRecord::TestCase
end
end
end
+
+ def test_disable_referential_integrity
+ assert_nothing_raised do
+ @connection.disable_referential_integrity do
+ # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
+ if @connection.prefetch_primary_key?
+ id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
+ @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
+ else
+ @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ end
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 27e7b1a1c3..3a7f1badf0 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -229,8 +229,8 @@ class SchemaTest < ActiveRecord::TestCase
%("schema"."table_name") => %w{schema table_name},
%("even spaces".table) => ['even spaces','table'],
%(schema."table.name") => ['schema', 'table.name']
- }.each do |given,expect|
- assert_equal expect, @connection.send(:extract_schema_and_table, given)
+ }.each do |given, expect|
+ assert_equal expect, ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::Utils.extract_schema_and_table(given)
end
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 7338513b41..4190f6d76d 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -667,4 +667,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
firm = client.create_firm!{ |f| f.name = 'Agency Company' }
assert_equal 'Agency Company', firm.name
end
+
+ def test_should_set_foreign_key_on_create_association
+ client = Client.create! :name => "fuu"
+
+ firm = client.create_firm :name => "baa"
+ assert_equal firm.id, client.client_of
+ end
+
+ def test_should_set_foreign_key_on_create_association!
+ client = Client.create! :name => "fuu"
+
+ firm = client.create_firm! :name => "baa"
+ assert_equal firm.id, client.client_of
+ end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 325fc58958..f1d061133c 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1040,4 +1040,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
}
assert_no_queries { assert_equal groucho, sponsor.thing }
end
+
+ def test_joins_with_includes_should_preload_via_joins
+ post = assert_queries(1) { Post.includes(:comments).joins(:comments).order('posts.id desc').to_a.first }
+
+ assert_queries(0) do
+ assert_not_equal 0, post.comments.to_a.count
+ end
+ end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 5fef3faab9..9feb7b010f 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -53,6 +53,16 @@ class Weird < ActiveRecord::Base; end
class Boolean < ActiveRecord::Base; end
+class LintTest < ActiveRecord::TestCase
+ include ActiveModel::Lint::Tests
+
+ class LintModel < ActiveRecord::Base; end
+
+ def setup
+ @model = LintModel.new
+ end
+end
+
class BasicsTest < ActiveRecord::TestCase
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 4e75eafe3d..3d2a03d2b9 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -140,23 +140,30 @@ class FinderTest < ActiveRecord::TestCase
def test_find_with_group
- developers = Developer.find(:all, :group => "salary", :select => "salary")
+ developers = Developer.find(:all, :group => "salary", :select => "salary")
assert_equal 4, developers.size
assert_equal 4, developers.map(&:salary).uniq.size
end
def test_find_with_group_and_having
- developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary")
+ developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary")
assert_equal 3, developers.size
assert_equal 3, developers.map(&:salary).uniq.size
- assert developers.all? { |developer| developer.salary > 10000 }
+ assert developers.all? { |developer| developer.salary > 10000 }
end
def test_find_with_group_and_sanitized_having
- developers = Developer.find(:all, :group => "salary", :having => ["sum(salary) > ?", 10000], :select => "salary")
+ developers = Developer.find(:all, :group => "salary", :having => ["sum(salary) > ?", 10000], :select => "salary")
assert_equal 3, developers.size
assert_equal 3, developers.map(&:salary).uniq.size
- assert developers.all? { |developer| developer.salary > 10000 }
+ assert developers.all? { |developer| developer.salary > 10000 }
+ end
+
+ def test_find_with_group_and_sanitized_having_method
+ developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select('salary').all
+ assert_equal 3, developers.size
+ assert_equal 3, developers.map(&:salary).uniq.size
+ assert developers.all? { |developer| developer.salary > 10000 }
end
def test_find_with_entire_select_statement
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 854eb86c09..842e8a0049 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -178,11 +178,9 @@ class FixturesTest < ActiveRecord::TestCase
assert_nothing_raised do
fixtures = ActiveRecord::Fixtures.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered")
- i = 0
- fixtures.each do |name, fixture|
+ fixtures.each.with_index do |(name, fixture), i|
assert_equal "fixture_no_#{i}", name
assert_equal "Category #{i}", fixture['name']
- i += 1
end
end
end
diff --git a/activerecord/test/cases/habtm_destroy_order_test.rb b/activerecord/test/cases/habtm_destroy_order_test.rb
index f2b91d977e..2ce0de360e 100644
--- a/activerecord/test/cases/habtm_destroy_order_test.rb
+++ b/activerecord/test/cases/habtm_destroy_order_test.rb
@@ -16,6 +16,16 @@ class HabtmDestroyOrderTest < ActiveRecord::TestCase
assert !sicp.destroyed?
end
+ test 'should not raise error if have foreign key in the join table' do
+ student = Student.new(:name => "Ben Bitdiddle")
+ lesson = Lesson.new(:name => "SICP")
+ lesson.students << student
+ lesson.save!
+ assert_nothing_raised do
+ student.destroy
+ end
+ end
+
test "not destroying a student with lessons leaves student<=>lesson association intact" do
# test a normal before_destroy doesn't destroy the habtm joins
begin
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index c6c6079490..9e8475465e 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -63,6 +63,14 @@ class LogSubscriberTest < ActiveRecord::TestCase
assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last)
end
+ def test_exists_query_logging
+ Developer.exists? 1
+ wait
+ assert_equal 1, @logger.logged(:debug).size
+ assert_match(/Developer Exists/, @logger.logged(:debug).last)
+ assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last)
+ end
+
def test_cached_queries
ActiveRecord::Base.cache do
Developer.all
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 05a41d8a0a..7e3da145e5 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -26,7 +26,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase
def test_to_key_with_primary_key_after_destroy
topic = Topic.find(1)
topic.destroy
- assert_equal [1], topic.to_key
+ assert_equal nil, topic.to_key
end
def test_integer_key
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 4fe311b441..e6cd274586 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -305,6 +305,8 @@ ActiveRecord::Schema.define do
t.references :student
end
+ create_table :lint_models, :force => true
+
create_table :line_items, :force => true do |t|
t.integer :invoice_id
t.integer :amount
@@ -719,6 +721,8 @@ ActiveRecord::Schema.define do
end
execute "ALTER TABLE fk_test_has_fk ADD CONSTRAINT fk_name FOREIGN KEY (#{quote_column_name 'fk_id'}) REFERENCES #{quote_table_name 'fk_test_has_pk'} (#{quote_column_name 'id'})"
+
+ execute "ALTER TABLE lessons_students ADD CONSTRAINT student_id_fk FOREIGN KEY (#{quote_column_name 'student_id'}) REFERENCES #{quote_table_name 'students'} (#{quote_column_name 'id'})"
end
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 85692428e3..ac88c82709 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -557,7 +557,7 @@ module ActiveSupport
@expires_in = options[:expires_in]
@expires_in = @expires_in.to_f if @expires_in
@created_at = Time.now.to_f
- if value
+ if defined?(value)
if should_compress?(value, options)
@value = Zlib::Deflate.deflate(Marshal.dump(value))
@compressed = true
@@ -576,7 +576,7 @@ module ActiveSupport
# Get the value stored in the cache.
def value
- if @value
+ if defined?(@value)
val = compressed? ? Marshal.load(Zlib::Inflate.inflate(@value)) : @value
unless val.frozen?
val.freeze rescue nil
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 64d2c3bff6..e07294178b 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -183,6 +183,14 @@ module ActiveSupport
# Provide support for raw values in the local cache strategy.
module LocalCacheWithRaw # :nodoc:
protected
+ def read_entry(key, options)
+ entry = super
+ if options[:raw] && local_cache && entry
+ entry = deserialize_entry(entry.value)
+ end
+ entry
+ end
+
def write_entry(key, entry, options) # :nodoc:
retval = super
if options[:raw] && local_cache && retval
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 102378a029..5f07bb4f5a 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -95,7 +95,7 @@ class Hash
case value.class.to_s
when 'Hash'
if value['type'] == 'array'
- _, entries = Array.wrap(value.detect { |k,v| k != 'type' })
+ _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
if entries.nil? || (c = value['__content__'] && c.blank?)
[]
else
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 15a3717ea1..59ffd24698 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -6,6 +6,8 @@ require 'active_support/core_ext/hash/keys'
module ActiveSupport
class HashWithIndifferentAccess < Hash
+
+ # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
def extractable_options?
true
end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index ed5ccb44de..8b3e4800c3 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -188,6 +188,11 @@ module CacheStoreBehavior
assert_equal nil, @cache.read('foo')
end
+ def test_should_read_and_write_false
+ assert_equal true, @cache.write('foo', false)
+ assert_equal false, @cache.read('foo')
+ end
+
def test_read_multi
@cache.write('foo', 'bar')
@cache.write('fu', 'baz')
@@ -630,7 +635,14 @@ uses_memcached 'memcached backed store' do
cache.write("foo", 2)
assert_equal "2", cache.read("foo")
end
-
+
+ def test_raw_values_with_marshal
+ cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
+ cache.clear
+ cache.write("foo", Marshal.dump([]))
+ assert_equal [], cache.read("foo")
+ end
+
def test_local_cache_raw_values
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
cache.clear
@@ -639,6 +651,15 @@ uses_memcached 'memcached backed store' do
assert_equal "2", cache.read("foo")
end
end
+
+ def test_local_cache_raw_values_with_marshal
+ cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
+ cache.clear
+ cache.with_local_cache do
+ cache.write("foo", Marshal.dump([]))
+ assert_equal [], cache.read("foo")
+ end
+ end
end
end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 0b3f18faec..1813ba2a4d 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -670,6 +670,55 @@ class HashToXmlTest < Test::Unit::TestCase
assert_match %r{<local-created-at type=\"datetime\">1999-02-01T19:00:00-05:00</local-created-at>}, xml
end
+ def test_multiple_records_from_xml_with_attributes_other_than_type_ignores_them_without_exploding
+ topics_xml = <<-EOT
+ <topics type="array" page="1" page-count="1000" per-page="2">
+ <topic>
+ <title>The First Topic</title>
+ <author-name>David</author-name>
+ <id type="integer">1</id>
+ <approved type="boolean">false</approved>
+ <replies-count type="integer">0</replies-count>
+ <replies-close-in type="integer">2592000000</replies-close-in>
+ <written-on type="date">2003-07-16</written-on>
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
+ <content>Have a nice day</content>
+ <author-email-address>david@loudthinking.com</author-email-address>
+ <parent-id nil="true"></parent-id>
+ </topic>
+ <topic>
+ <title>The Second Topic</title>
+ <author-name>Jason</author-name>
+ <id type="integer">1</id>
+ <approved type="boolean">false</approved>
+ <replies-count type="integer">0</replies-count>
+ <replies-close-in type="integer">2592000000</replies-close-in>
+ <written-on type="date">2003-07-16</written-on>
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
+ <content>Have a nice day</content>
+ <author-email-address>david@loudthinking.com</author-email-address>
+ <parent-id></parent-id>
+ </topic>
+ </topics>
+ EOT
+
+ expected_topic_hash = {
+ :title => "The First Topic",
+ :author_name => "David",
+ :id => 1,
+ :approved => false,
+ :replies_count => 0,
+ :replies_close_in => 2592000000,
+ :written_on => Date.new(2003, 7, 16),
+ :viewed_at => Time.utc(2003, 7, 16, 9, 28),
+ :content => "Have a nice day",
+ :author_email_address => "david@loudthinking.com",
+ :parent_id => nil
+ }.stringify_keys
+
+ assert_equal expected_topic_hash, Hash.from_xml(topics_xml)["topics"].first
+ end
+
def test_single_record_from_xml
topic_xml = <<-EOT
<topic>
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 8937a0c172..082f9eda7d 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -256,7 +256,7 @@ The above will yield the supplied block with +1000+ invoices every time.
h3. Conditions
-The +find+ method allows you to specify conditions to limit the records returned, representing the +WHERE+-part of the SQL statement. Conditions can either be specified as a string, array, or hash.
+The +where+ method allows you to specify conditions to limit the records returned, representing the +WHERE+-part of the SQL statement. Conditions can either be specified as a string, array, or hash.
h4. Pure String Conditions
diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile
index d2441727ee..563c1c79ae 100644
--- a/railties/guides/source/asset_pipeline.textile
+++ b/railties/guides/source/asset_pipeline.textile
@@ -32,7 +32,7 @@ h4. Main Features
The first is to concatenate of assets. This is important in a production environment to reduce the number of requests that a client browser has to make to render a web page. While Rails already has a feature to concatenate these types of asset--by placing +:cache => true+ at the end of tags such as +javascript_include_tag+ and +stylesheet_link_tag+--, many people do not use it.
-The default behavior in 3.1 and onward is to concatenate all files into one master file each for JS and CSS, however you can separate files or groups of files if required (see below). In production an MD5 fingerprint is inserted into each filename.
+The default behavior in Rails 3.1 and onward is to concatenate all files into one master file each for JS and CSS, however you can separate files or groups of files if required (see below). In production an MD5 fingerprint is inserted into each filename.
The second feature of the pipeline is to minify or compress. For CSS this usually involves removing whitespace and comments. For Javascript more complex processes can be applied.
@@ -42,7 +42,7 @@ The third feature is the ability to code these assets using another language, or
h4. What is fingerprinting and why should I care?
-Fingerprinting is a technique where the filenames of content that is static or infrequently updated is altered to be unique to the content contained in the file.
+Fingerprinting is a technique where the filenames of content that is static or infrequently updated is altered to be unique to the content contained in the file.
When a filename is unique and based on its content, http headers can be set to encourage caches everywhere (at ISPs, in browsers) to keep there own copy of the content. When the content is updated, the fingerprint will change and the remote clients will request the new file. This is generally known as _cachebusting_.
@@ -57,8 +57,8 @@ This is the strategy adopted by the Rails asset pipeline.
Rails old strategy was to append a query string to every asset linked with a built-in helper. In the source the generated code looked like this:
<plain>
-/stylesheets/global.css?1309495796
-</plain>
+/stylesheets/global.css?1309495796
+</plain>
This has several disadvantages:
@@ -88,8 +88,7 @@ This is not to say that assets can (or should) no longer be placed in +public+.
When a scaffold or controller is generated for the application, Rails will also generate a JavaScript file (or CoffeeScript if the +coffee-script+ gem is in the +Gemfile+) and a Cascading Style Sheet file (or SCSS if +sass-rails+ is in the +Gemfile+) file for that controller.
-For example, if a +ProjectsController+ is generated, there will be a new file at +app/assets/javascripts/projects.js.coffee+ and another at +app/assets/stylesheets/projects.css.scss+. You can put any JavaScript or CSS unique to a controller inside their respective asset files.
-
+For example, if a +ProjectsController+ is generated, there will be a new file at +app/assets/javascripts/projects.js.coffee+ and another at +app/assets/stylesheets/projects.css.scss+. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as +<%= javascript_include_tag params[:controller] %>+ or +<%= stylesheet_link_tag params[:controller] %>+.
h4. Asset Organization
@@ -184,6 +183,31 @@ In the development environment assets are compiled and cached on the first reque
If any of the files in the manifest have changed between requests, the server will respond with a new compiled file.
+h4. Debugging Assets
+
+You can put +?debug_assets=true+ or +?debug_assets=1+ at the end of a URL and Sprockets will expand the lines which load the assets. For example, if we had an +app/assets/javascripts/application.js+ file containing these lines:
+
+<plain>
+//= require "projects"
+//= require "tickets"
+</plain>
+
+By default, this would only render this line when used with +<%= javascript_include_tag "application" %>+ in a view or layout:
+
+<html>
+ <script src='/assets/application.js'></script>
+</html>
+
+When the +debug_assets+ parameter is set, this line will be expanded out into three separate lines, separating out the combined file into their parts.
+
+<html>
+ <script src='/assets/application.js'></script>
+ <script src='/assets/projects.js'></script>
+ <script src='/assets/tickets.js'></script>
+</html>
+
+This allows the individual parts of an asset to be rendered and debugged separately.
+
h3. In Production
In the production environment, assets are served slightly differently.
@@ -308,3 +332,4 @@ h3. Making Your Library or Gem a Pre-Processor
"You should be able to register [your gems] on Tilt and Sprockets will find them." - Josh
Tilt: https://github.com/rtomayko/tilt
+
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index 6f028805d6..f5bee52b5a 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -127,7 +127,7 @@ When something is logged it's printed into the corresponding log if the log leve
The available log levels are: +:debug+, +:info+, +:warn+, +:error+, and +:fatal+, corresponding to the log level numbers from 0 up to 4 respectively. To change the default log level, use
<ruby>
-config.log_level = Logger::WARN # In any environment initializer, or
+config.log_level = :warn # In any environment initializer, or
Rails.logger.level = 0 # at any time
</ruby>
@@ -480,11 +480,7 @@ class Author < ActiveRecord::Base
def find_recent_comments(limit = 10)
debugger
- @recent_comments ||= comments.find(
- :all,
- :conditions => ["created_at > ?", 1.week.ago],
- :limit => limit
- )
+ @recent_comments ||= comments.where("created_at > ?", 1.week.ago).limit(limit)
end
end
</ruby>
@@ -493,7 +489,7 @@ TIP: You can use ruby-debug while using +rails console+. Just remember to +requi
<shell>
$ rails console
-Loading development environment (Rails 2.1.0)
+Loading development environment (Rails 3.1.0)
>> require "ruby-debug"
=> []
>> author = Author.first
@@ -507,15 +503,15 @@ With the code stopped, take a look around:
<shell>
(rdb:1) list
-[6, 15] in /PathTo/project/app/models/author.rb
+[2, 9] in /PathTo/project/app/models/author.rb
+ 2 has_one :editorial
+ 3 has_many :comments
+ 4
+ 5 def find_recent_comments(limit = 10)
6 debugger
- 7 @recent_comments ||= comments.find(
- 8 :all,
- 9 :conditions => ["created_at > ?", 1.week.ago],
- 10 :limit => limit
-=> 11 )
- 12 end
- 13 end
+=> 7 @recent_comments ||= comments.where("created_at > ?", 1.week.ago).limit(limit)
+ 8 end
+ 9 end
</shell>
You are at the end of the line, but... was this line executed? You can inspect the instance variables.
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index 4314d00f2e..9051ede9dd 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -589,7 +589,7 @@ def upload
end
</ruby>
-Once a file has been uploaded, there are a multitude of potential tasks, ranging from where to store the files (on disk, Amazon S3, etc) and associating them with models to resizing image files and generating thumbnails. The intricacies of this are beyond the scope of this guide, but there are several plugins designed to assist with these. Two of the better known ones are "Attachment-Fu":https://github.com/technoweenie/attachment_fu and "Paperclip":http://www.thoughtbot.com/projects/paperclip.
+Once a file has been uploaded, there are a multitude of potential tasks, ranging from where to store the files (on disk, Amazon S3, etc) and associating them with models to resizing image files and generating thumbnails. The intricacies of this are beyond the scope of this guide, but there are several libraries designed to assist with these. Two of the better known ones are "CarrierWave":https://github.com/jnicklas/carrierwave and "Paperclip":http://www.thoughtbot.com/projects/paperclip.
NOTE: If the user has not selected a file the corresponding parameter will be an empty string.
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index 412ded1d0b..908f3f125a 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -649,7 +649,7 @@ h5(#sql-injection-introduction). Introduction
SQL injection attacks aim at influencing database queries by manipulating web application parameters. A popular goal of SQL injection attacks is to bypass authorization. Another goal is to carry out data manipulation or reading arbitrary data. Here is an example of how not to use user input data in a query:
<ruby>
-Project.all(:conditions => "name = '#{params[:name]}'")
+Project.where("name = '#{params[:name]}'")
</ruby>
This could be in a search action and the user may enter a project's name that he wants to find. If a malicious user enters ' OR 1 --, the resulting SQL query will be:
@@ -681,7 +681,7 @@ h5. Unauthorized Reading
The UNION statement connects two SQL queries and returns the data in one set. An attacker can use it to read arbitrary data from the database. Let's take the example from above:
<ruby>
-Project.all(:conditions => "name = '#{params[:name]}'")
+Project.where("name = '#{params[:name]}'")
</ruby>
And now let's inject another query using the UNION statement:
@@ -703,18 +703,18 @@ Also, the second query renames some columns with the AS statement so that the we
h5(#sql-injection-countermeasures). Countermeasures
-Ruby on Rails has a built-in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. <em class="highlight">Using +Model.find(id)+ or +Model.find_by_some thing(something)+ automatically applies this countermeasure</em>. But in SQL fragments, especially <em class="highlight">in conditions fragments (+:conditions => "..."+), the +connection.execute()+ or +Model.find_by_sql()+ methods, it has to be applied manually</em>.
+Ruby on Rails has a built-in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. <em class="highlight">Using +Model.find(id)+ or +Model.find_by_some thing(something)+ automatically applies this countermeasure</em>. But in SQL fragments, especially <em class="highlight">in conditions fragments (+where("...")+), the +connection.execute()+ or +Model.find_by_sql()+ methods, it has to be applied manually</em>.
Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings like this:
<ruby>
-Model.first(:conditions => ["login = ? AND password = ?", entered_user_name, entered_password])
+Model.where("login = ? AND password = ?", entered_user_name, entered_password).first
</ruby>
As you can see, the first part of the array is an SQL fragment with question marks. The sanitized versions of the variables in the second part of the array replace the question marks. Or you can pass a hash for the same result:
<ruby>
-Model.first(:conditions => {:login => entered_user_name, :password => entered_password})
+Model.where(:login => entered_user_name, :password => entered_password).first
</ruby>
The array or hash form is only available in model instances. You can try +sanitize_sql()+ elsewhere. _(highlight)Make it a habit to think about the security consequences when using an external string in SQL_.
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index df92934457..603ede3d1e 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -4,6 +4,7 @@ require 'pathname'
require 'active_support'
require 'active_support/core_ext/kernel/reporting'
+require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/logger'
require 'rails/application'
@@ -91,7 +92,7 @@ module Rails
#
# * The Rails environment;
# * The environment variable RAILS_GROUPS;
- # * The optional hash given as argument with group dependencies;
+ # * The optional envs given as argument and the hash with group dependencies;
#
# == Examples
#
@@ -101,12 +102,14 @@ module Rails
# # => [:default, :development, :assets] for Rails.env == "development"
# # => [:default, :production] for Rails.env == "production"
#
- def groups(hash={})
+ def groups(*groups)
+ hash = groups.extract_options!
env = Rails.env
- groups = [:default, env]
+ groups.unshift(:default, env)
groups.concat ENV["RAILS_GROUPS"].to_s.split(",")
groups.concat hash.map { |k,v| k if v.map(&:to_s).include?(env) }
groups.compact!
+ groups.uniq!
groups
end
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index fe29668c72..559739b2ec 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -106,6 +106,15 @@ module Rails
self
end
+ # Rails.application.env_config stores some of the Rails initial environment parameters.
+ # Currently stores:
+ #
+ # * action_dispatch.parameter_filter" => config.filter_parameters,
+ # * action_dispatch.secret_token" => config.secret_token,
+ # * action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions
+ #
+ # These parameters will be used by middlewares and engines to configure themselves.
+ #
def env_config
@env_config ||= super.merge({
"action_dispatch.parameter_filter" => config.filter_parameters,
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index eaa31e7386..0460009954 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -15,7 +15,7 @@ require "active_resource/railtie"
# If you have a Gemfile, require the default gems, the ones in the
# current environment and also include :assets gems if in development
# or test environments.
-Bundler.require *Rails.groups(:assets => %w(development test)) if defined?(Bundler)
+Bundler.require *Rails.groups(:assets) if defined?(Bundler)
module <%= app_const_base %>
class Application < Rails::Application
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
index 9e8b0131f8..5d8d9be237 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
@@ -8,3 +8,8 @@
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
+#
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 2547863d12..448982f9de 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -45,7 +45,7 @@ module ApplicationTests
Rails.env = "development"
assert_equal [:default, "development"], Rails.groups
assert_equal [:default, "development", :assets], Rails.groups(:assets => [:development])
- assert_equal [:default, "development", :assets], Rails.groups(:assets => %w(development))
+ assert_equal [:default, "development", :another, :assets], Rails.groups(:another, :assets => %w(development))
Rails.env = "test"
assert_equal [:default, "test"], Rails.groups(:assets => [:development])
@@ -516,5 +516,14 @@ module ApplicationTests
get "/", { :format => :xml }, "HTTP_ACCEPT" => "application/xml"
assert_equal 'XML', last_response.body
end
+
+ test "Rails.application#env_config exists and include some existing parameters" do
+ make_basic_app
+
+ assert_respond_to app, :env_config
+ assert_equal app.env_config['action_dispatch.parameter_filter'], app.config.filter_parameters
+ assert_equal app.env_config['action_dispatch.secret_token'], app.config.secret_token
+ assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions
+ end
end
end