Skip to content

Commit

Permalink
Merge branch 'master' of github.com:lifo/docrails
Browse files Browse the repository at this point in the history
  • Loading branch information
vijaydev committed Dec 10, 2010
2 parents 638b409 + dbf955c commit 6acd130
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 260 deletions.
68 changes: 41 additions & 27 deletions actionpack/lib/action_controller/caching/actions.rb
Expand Up @@ -4,25 +4,26 @@ module ActionController #:nodoc:
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 the Action Pack. The key benefit
# of this is that filters are run before the cache is served, which
# allows for authentication and other restrictions on whether someone
# is allowed to see the cache. Example:
# 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:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
#
# caches_page :public
# caches_action :index, :show, :feed
# caches_action :index, :show
# end
#
# In this example, the public action doesn't require authentication,
# so it's possible to use the faster page caching method. But both
# the show and feed action are to be shielded behind the authenticate
# filter, so we need to implement those as action caches.
# In this example, the +public+ action doesn't require authentication
# so it's possible to use the faster page caching. On the other hand
# +index+ and +show+ require authentication. They can still be cached,
# but we need action caching for them.
#
# Action caching internally uses the fragment caching and an around
# filter to do the job. The fragment cache is named according to both
# the current host and the path. So a page that is accessed at
# Action caching uses fragment caching internally and an around
# filter to do the job. The fragment cache is named according to
# the host and path of the request. A page that is accessed at
# <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
# <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
# differentiate between <tt>david.example.com/lists/</tt> and
Expand All @@ -38,39 +39,52 @@ module Caching
# <tt>:action => 'list', :format => :xml</tt>.
#
# You can set modify the default action cache path by passing a
# :cache_path option. This will be passed directly to
# ActionCachePath.path_for. This is handy for actions with multiple
# possible routes that should be cached differently. If a block is
# given, it is called with the current controller instance.
# <tt>:cache_path</tt> option. This will be passed directly to
# <tt>ActionCachePath.path_for</tt>. This is handy for actions with
# multiple possible routes that should be cached differently. If a
# block is given, it is called with the current controller instance.
#
# And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
# proc that specifies when the action should be cached.
#
# And you can also use :if (or :unless) to pass a Proc that
# specifies when the action should be cached.
# Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
#
# Finally, if you are using memcached, you can also pass :expires_in.
# The following example depicts some of the points made above:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# caches_page :public
#
# caches_page :public
#
# caches_action :index, :if => proc do |c|
# !c.request.format.json? # cache if is not a JSON request
# end
#
# caches_action :show, :cache_path => { :project => 1 },
# :expires_in => 1.hour
#
# caches_action :feed, :cache_path => proc do |controller|
# if controller.params[:user_id]
# controller.send(:user_list_url,
# controller.params[:user_id], controller.params[:id])
# caches_action :feed, :cache_path => proc do |c|
# if c.params[:user_id]
# c.send(:user_list_url,
# c.params[:user_id], c.params[:id])
# else
# controller.send(:list_url, controller.params[:id])
# c.send(:list_url, c.params[:id])
# end
# end
# end
#
# If you pass :layout => false, it will only cache your action
# content. It is useful when your layout has dynamic information.
# 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
# header the Content-Type of the cached response could be wrong because
# no information about the MIME type is stored in the cache key. So, if
# you first ask for MIME type M in the Accept header, a cache entry is
# created, and then perform a second resquest to the same resource asking
# for a different MIME type, you'd get the content cached for M.
#
# The <tt>:format</tt> parameter is taken into account though. The safest
# way to cache by MIME type is to pass the format in the route.
module Actions
extend ActiveSupport::Concern

Expand Down
15 changes: 10 additions & 5 deletions actionpack/lib/action_dispatch/routing/mapper.rb
Expand Up @@ -247,7 +247,11 @@ def initialize(set) #:nodoc:
#
# root :to => 'pages#main'
#
# You should put the root route at the end of <tt>config/routes.rb</tt>.
# For options, see the +match+ method's documentation, as +root+ uses it internally.
#
# You should put the root route at the top of <tt>config/routes.rb</tt>,
# because this means it will be matched first. As this is the most popular route
# of most Rails applications, this is beneficial.
def root(options = {})
match '/', options.reverse_merge(:as => :root)
end
Expand All @@ -269,18 +273,18 @@ def match(path, options=nil)

# Mount a Rack-based application to be used within the application.
#
# mount SomeRackApp, :at => "some_route"
# mount SomeRackApp, :at => "some_route"
#
# Alternatively:
#
# mount(SomeRackApp => "some_route")
# mount(SomeRackApp => "some_route")
#
# All mounted applications come with routing helpers to access them.
# These are named after the class specified, so for the above example
# the helper is either +some_rack_app_path+ or +some_rack_app_url+.
# To customize this helper's name, use the +:as+ option:
#
# mount(SomeRackApp => "some_route", :as => "exciting")
# mount(SomeRackApp => "some_route", :as => "exciting")
#
# This will generate the +exciting_path+ and +exciting_url+ helpers
# which can be used to navigate to this mounted app.
Expand Down Expand Up @@ -563,7 +567,7 @@ def controller(controller, options={})
# admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
# === Supported options
#
# The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ all default to the name of the namespace.
# The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ options all default to the name of the namespace.
#
# [:path]
# The path prefix for the routes.
Expand Down Expand Up @@ -1085,6 +1089,7 @@ def nested
end
end

# See ActionDispatch::Routing::Mapper::Scoping#namespace
def namespace(path, options = {})
if resource_scope?
nested { super }
Expand Down
28 changes: 14 additions & 14 deletions activemodel/lib/active_model/lint.rb
@@ -1,19 +1,19 @@
# == Active Model Lint Tests
#
# You can test whether an object is compliant with the Active Model API by
# including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will include
# tests that tell you whether your object is fully compliant, or if not,
# which aspects of the API are not implemented.
#
# These tests do not attempt to determine the semantic correctness of the
# returned values. For instance, you could implement valid? to always
# return true, and the tests would pass. It is up to you to ensure that
# the values are semantically meaningful.
#
# Objects you pass in are expected to return a compliant object from a
# call to to_model. It is perfectly fine for to_model to return self.
module ActiveModel
module Lint
# == Active Model Lint Tests
#
# You can test whether an object is compliant with the Active Model API by
# including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will include
# tests that tell you whether your object is fully compliant, or if not,
# which aspects of the API are not implemented.
#
# These tests do not attempt to determine the semantic correctness of the
# returned values. For instance, you could implement valid? to always
# return true, and the tests would pass. It is up to you to ensure that
# the values are semantically meaningful.
#
# Objects you pass in are expected to return a compliant object from a
# call to to_model. It is perfectly fine for to_model to return self.
module Tests

# == Responds to <tt>to_key</tt>
Expand Down
17 changes: 17 additions & 0 deletions activemodel/test/cases/translation_test.rb
Expand Up @@ -17,6 +17,23 @@ def test_translated_model_attributes_with_default
assert_equal 'name default attribute', Person.human_attribute_name('name')
end

def test_translated_model_attributes_using_default_option
assert_equal 'name default attribute', Person.human_attribute_name('name', :default => "name default attribute")
end

def test_translated_model_attributes_using_default_option_as_symbol
I18n.backend.store_translations 'en', :default_name => 'name default attribute'
assert_equal 'name default attribute', Person.human_attribute_name('name', :default => :default_name)
end

def test_translated_model_attributes_falling_back_to_default
assert_equal 'Name', Person.human_attribute_name('name')
end

def test_translated_model_attributes_using_default_option_as_symbol_and_falling_back_to_default
assert_equal 'Name', Person.human_attribute_name('name', :default => :default_name)
end

def test_translated_model_attributes_with_symbols
I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } }
assert_equal 'person name attribute', Person.human_attribute_name(:name)
Expand Down
Expand Up @@ -6,14 +6,17 @@ module ActiveRecord
module Associations
module ClassMethods
class JoinDependency # :nodoc:
attr_reader :join_parts, :reflections, :table_aliases
attr_reader :join_parts, :reflections, :table_aliases, :active_record

def initialize(base, associations, joins)
@table_joins = joins || ''
@active_record = base
@table_joins = joins
@join_parts = [JoinBase.new(base)]
@associations = {}
@reflections = []
@table_aliases = Hash.new(0)
@table_aliases = Hash.new do |h,name|
h[name] = count_aliases_from_table_joins(name)
end
@table_aliases[base.table_name] = 1
build(associations)
end
Expand Down Expand Up @@ -44,12 +47,26 @@ def columns
end

def count_aliases_from_table_joins(name)
return 0 if !@table_joins || Arel::Table === @table_joins

@table_joins.grep(Arel::Nodes::Join).map { |join|
right = join.right
case right
when Arel::Table
right.name.downcase == name ? 1 : 0
when String
count_aliases_from_string(right.downcase, name)
else
0
end
}.sum
end

def count_aliases_from_string(join_sql, name)
# quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase
join_sql = @table_joins.downcase
join_sql.blank? ? 0 :
# Table names
join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size +
quoted_name = active_record.connection.quote_table_name(name.downcase).downcase
# Table names
join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size +
# Table aliases
join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size
end
Expand All @@ -61,11 +78,11 @@ def instantiate(rows)
records = rows.map { |model|
primary_id = model[primary_key]
parent = parents[primary_id] ||= join_base.instantiate(model)
construct(parent, @associations, join_associations.dup, model)
construct(parent, @associations, join_associations, model)
parent
}.uniq

remove_duplicate_results!(join_base.active_record, records, @associations)
remove_duplicate_results!(active_record, records, @associations)
records
end

Expand Down
Expand Up @@ -67,8 +67,7 @@ def join_relation(joining_relation)

def table
@table ||= Arel::Table.new(
table_name, :as => aliased_table_name,
:engine => arel_engine, :columns => active_record.columns
table_name, :as => aliased_table_name, :engine => arel_engine
)
end

Expand All @@ -78,23 +77,18 @@ def table
protected

def aliased_table_name_for(name, suffix = nil)
if @join_dependency.table_aliases[name].zero?
@join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name)
end
aliases = @join_dependency.table_aliases

if !@join_dependency.table_aliases[name].zero? # We need an alias
name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
@join_dependency.table_aliases[name] += 1
if @join_dependency.table_aliases[name] == 1 # First time we've seen this name
# Also need to count the aliases from the table_aliases to avoid incorrect count
@join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name)
end
table_index = @join_dependency.table_aliases[name]
name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1
else
@join_dependency.table_aliases[name] += 1
if aliases[name] != 0 # We need an alias
connection = active_record.connection

name = connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
table_index = aliases[name] + 1
name = name[0, connection.table_alias_length-3] + "_#{table_index}" if table_index > 1
end

aliases[name] += 1

name
end

Expand Down
Expand Up @@ -13,7 +13,7 @@ def aliased_prefix
end

def table
Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns)
Arel::Table.new(table_name, arel_engine)
end

def aliased_table_name
Expand Down
4 changes: 1 addition & 3 deletions activerecord/lib/active_record/base.rb
Expand Up @@ -1500,9 +1500,7 @@ def attributes=(new_attributes, guard_protected_attributes = true)

# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
def attributes
attrs = {}
attribute_names.each { |name| attrs[name] = read_attribute(name) }
attrs
Hash[@attributes.map { |name, _| [name, read_attribute(name)] }]
end

# Returns an <tt>#inspect</tt>-like string for the value of the
Expand Down
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/relation/finder_methods.rb
Expand Up @@ -196,7 +196,7 @@ def find_with_associations

def construct_relation_for_association_calculations
including = (@eager_load_values + @includes_values).uniq
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.join_sql)
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.froms.first)
relation = except(:includes, :eager_load, :preload)
apply_join_dependency(relation, join_dependency)
end
Expand Down

0 comments on commit 6acd130

Please sign in to comment.