Skip to content

Commit

Permalink
Merge commit 'mainstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
lifo committed Sep 13, 2008
2 parents 25fd841 + 9605541 commit f6c0f68
Show file tree
Hide file tree
Showing 35 changed files with 379 additions and 102 deletions.
32 changes: 17 additions & 15 deletions actionpack/lib/action_view/helpers/asset_tag_helper.rb
Expand Up @@ -463,7 +463,8 @@ def image_tag(source, options = {})
end

private
COMPUTED_PUBLIC_PATHS = ActiveSupport::Cache::MemoryStore.new.silence!
COMPUTED_PUBLIC_PATHS = {}
COMPUTED_PUBLIC_PATHS_GUARD = Mutex.new

# Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
Expand All @@ -483,23 +484,24 @@ def compute_public_path(source, dir, ext = nil, include_host = true)
dir, source, ext, include_host ].join
end

source = COMPUTED_PUBLIC_PATHS.fetch(cache_key) do
begin
source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))

if source =~ %r{^[-a-z]+://}
source
else
source = "/#{dir}/#{source}" unless source[0] == ?/
if has_request
unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
source = "#{ActionController::Base.relative_url_root}#{source}"
COMPUTED_PUBLIC_PATHS_GUARD.synchronize do
source = COMPUTED_PUBLIC_PATHS[cache_key] ||=
begin
source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))

if source =~ %r{^[-a-z]+://}
source
else
source = "/#{dir}/#{source}" unless source[0] == ?/
if has_request
unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
source = "#{ActionController::Base.relative_url_root}#{source}"
end
end
end

rewrite_asset_path(source)
rewrite_asset_path(source)
end
end
end
end

if include_host && source !~ %r{^[-a-z]+://}
Expand Down
13 changes: 8 additions & 5 deletions activerecord/lib/active_record/association_preload.rb
Expand Up @@ -95,7 +95,7 @@ def preload_has_and_belongs_to_many_association(records, reflection, preload_opt
records.each {|record| record.send(reflection.name).loaded}
options = reflection.options

conditions = "t0.#{reflection.primary_key_name} IN (?)"
conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
conditions << append_conditions(options, preload_options)

associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
Expand Down Expand Up @@ -222,8 +222,6 @@ def preload_belongs_to_association(records, reflection, preload_options={})

table_name = klass.quoted_table_name
primary_key = klass.primary_key
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} IN (?)"
conditions << append_conditions(options, preload_options)
column_type = klass.columns.detect{|c| c.name == primary_key}.type
ids = id_map.keys.uniq.map do |id|
if column_type == :integer
Expand All @@ -234,6 +232,8 @@ def preload_belongs_to_association(records, reflection, preload_options={})
id
end
end
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}"
conditions << append_conditions(options, preload_options)
associated_records = klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
:select => options[:select],
Expand All @@ -248,10 +248,10 @@ def find_associated_records(ids, reflection, preload_options)
table_name = reflection.klass.quoted_table_name

if interface = reflection.options[:as]
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
else
foreign_key = reflection.primary_key_name
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} IN (?)"
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
end

conditions << append_conditions(options, preload_options)
Expand All @@ -277,6 +277,9 @@ def append_conditions(options, preload_options)
sql
end

def in_or_equals_for_ids(ids)
ids.size > 1 ? "IN (?)" : "= ?"
end
end
end
end
9 changes: 8 additions & 1 deletion activerecord/lib/active_record/associations.rb
Expand Up @@ -1237,7 +1237,7 @@ def association_accessor_methods(reflection, association_proxy_class)

association = instance_variable_get(ivar) if instance_variable_defined?(ivar)

if association.nil? || force_reload
if association.nil? || !association.loaded? || force_reload
association = association_proxy_class.new(self, reflection)
retval = association.reload
if retval.nil? and association_proxy_class == BelongsToAssociation
Expand Down Expand Up @@ -1266,6 +1266,13 @@ def association_accessor_methods(reflection, association_proxy_class)
end
end

if association_proxy_class == BelongsToAssociation
define_method("#{reflection.primary_key_name}=") do |target_id|
instance_variable_get(ivar).reset if instance_variable_defined?(ivar)
write_attribute(reflection.primary_key_name, target_id)
end
end

define_method("set_#{reflection.name}_target") do |target|
return if target.nil? and association_proxy_class == BelongsToAssociation
association = association_proxy_class.new(self, reflection)
Expand Down
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/base.rb
Expand Up @@ -2152,7 +2152,7 @@ def expand_range_bind_variables(bind_vars) #:nodoc:
end

def quote_bound_value(value) #:nodoc:
if value.respond_to?(:map) && !value.is_a?(String)
if value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
connection.quote(nil)
else
Expand Down
20 changes: 19 additions & 1 deletion activerecord/lib/active_record/dirty.rb
Expand Up @@ -34,8 +34,10 @@ module ActiveRecord
# person.name << 'by'
# person.name_change # => ['uncle bob', 'uncle bobby']
module Dirty
DIRTY_SUFFIXES = ['_changed?', '_change', '_will_change!', '_was']

def self.included(base)
base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
base.attribute_method_suffix *DIRTY_SUFFIXES
base.alias_method_chain :write_attribute, :dirty
base.alias_method_chain :save, :dirty
base.alias_method_chain :save!, :dirty
Expand All @@ -44,6 +46,8 @@ def self.included(base)

base.superclass_delegating_accessor :partial_updates
base.partial_updates = true

base.send(:extend, ClassMethods)
end

# Do any attributes have unsaved changes?
Expand Down Expand Up @@ -161,5 +165,19 @@ def field_changed?(attr, old, value)
old != value
end

module ClassMethods
def self.extended(base)
base.metaclass.alias_method_chain(:alias_attribute, :dirty)
end

def alias_attribute_with_dirty(new_name, old_name)
alias_attribute_without_dirty(new_name, old_name)
DIRTY_SUFFIXES.each do |suffix|
module_eval <<-STR, __FILE__, __LINE__+1
def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end
STR
end
end
end
end
end
8 changes: 6 additions & 2 deletions activerecord/test/cases/aggregations_test.rb
Expand Up @@ -130,12 +130,16 @@ def test_conversion_block_is_deprecated
end

def test_conversion_block_used_when_converter_option_is_nil
Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
assert_deprecated 'conversion block has been deprecated' do
Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
end
assert_raise(NoMethodError) { Person.new.balance = 5 }
end

def test_converter_option_overrides_conversion_block
Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money }
assert_deprecated 'conversion block has been deprecated' do
Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money }
end

person = Person.new
assert_nothing_raised { person.balance = 5 }
Expand Down
Expand Up @@ -47,6 +47,19 @@ def test_natural_assignment
assert_equal apple.id, citibank.firm_id
end

def test_foreign_key_assignment
# Test using an existing record
signals37 = accounts(:signals37)
assert_equal companies(:first_firm), signals37.firm
signals37.firm_id = companies(:another_firm).id
assert_equal companies(:another_firm), signals37.firm

# Test using a new record
account = Account.new
account.firm_id = companies(:another_firm).id
assert_equal companies(:another_firm), account.firm
end

def test_no_unexpected_aliasing
first_firm = companies(:first_firm)
another_firm = companies(:another_firm)
Expand Down
Expand Up @@ -636,6 +636,18 @@ def test_join_with_group
assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
end

def test_find_grouped
all_posts_from_category1 = Post.find(:all, :conditions => "category_id = 1", :joins => :categories)
grouped_posts_of_category1 = Post.find(:all, :conditions => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories)
assert_equal 4, all_posts_from_category1.size
assert_equal 1, grouped_posts_of_category1.size
end

def test_find_scoped_grouped
assert_equal 4, categories(:general).posts_gruoped_by_title.size
assert_equal 1, categories(:technology).posts_gruoped_by_title.size
end

def test_get_ids
assert_equal projects(:active_record, :action_controller).map(&:id).sort, developers(:david).project_ids.sort
assert_equal [projects(:active_record).id], developers(:jamis).project_ids
Expand Down
Expand Up @@ -248,6 +248,11 @@ def test_find_grouped
assert_equal 1, grouped_clients_of_firm1.size
end

def test_find_scoped_grouped
assert_equal 1, companies(:first_firm).clients_grouped_by_firm_id.length
assert_equal 2, companies(:first_firm).clients_grouped_by_name.length
end

def test_adding
force_signal37_to_load_all_clients_of_firm
natural = Client.new("name" => "Natural Company")
Expand Down
12 changes: 12 additions & 0 deletions activerecord/test/cases/associations_test.rb
Expand Up @@ -182,6 +182,18 @@ def test_failed_reset_returns_nil
assert_nil p.author.reset
end

def test_reset_loads_association_next_time
welcome = posts(:welcome)
david = authors(:david)
author_assoc = welcome.author

assert_equal david, welcome.author # So we can be sure the test works correctly
author_assoc.reset
assert !author_assoc.loaded?
assert_nil author_assoc.target
assert_equal david, welcome.author
end

def test_reload_returns_assocition
david = developers(:david)
assert_nothing_raised do
Expand Down
13 changes: 13 additions & 0 deletions activerecord/test/cases/dirty_test.rb
Expand Up @@ -45,6 +45,19 @@ def test_attribute_changes
assert_nil pirate.catchphrase_change
end

def test_aliased_attribute_changes
# the actual attribute here is name, title is an
# alias setup via alias_attribute
parrot = Parrot.new
assert !parrot.title_changed?
assert_nil parrot.title_change

parrot.name = 'Sam'
assert parrot.title_changed?
assert_nil parrot.title_was
assert_equal parrot.name_change, parrot.title_change
end

def test_nullable_integer_not_marked_as_changed_if_new_value_is_blank
pirate = Pirate.new

Expand Down
18 changes: 16 additions & 2 deletions activerecord/test/cases/finder_test.rb
Expand Up @@ -169,6 +169,12 @@ def test_find_with_limit_and_condition
assert_equal("fixture_3", developers.first.name)
end

def test_find_with_group
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_entire_select_statement
topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"

Expand Down Expand Up @@ -297,7 +303,6 @@ def test_find_on_multiple_hash_conditions
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
end


def test_condition_interpolation
assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
Expand Down Expand Up @@ -392,7 +397,7 @@ def test_bind_variables
Company.find(:first, :conditions => ["id=? AND name = ?", 2])
}
assert_raises(ActiveRecord::PreparedStatementInvalid) {
Company.find(:first, :conditions => ["id=?", 2, 3, 4])
Company.find(:first, :conditions => ["id=?", 2, 3, 4])
}
end

Expand Down Expand Up @@ -455,6 +460,15 @@ def test_bind_string
assert_equal ActiveRecord::Base.connection.quote(''), bind('?', '')
end

def test_bind_chars
quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".chars)
assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".chars)
end

def test_bind_record
o = Struct.new(:quoted_id).new(1)
assert_equal '1', bind('?', o)
Expand Down
2 changes: 1 addition & 1 deletion activerecord/test/cases/inheritance_test.rb
Expand Up @@ -193,7 +193,7 @@ def test_eager_load_belongs_to_something_inherited

def test_eager_load_belongs_to_primary_key_quoting
con = Account.connection
assert_sql(/\(#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} IN \(1\)\)/) do
assert_sql(/\(#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} = 1\)/) do
Account.find(1, :include => :firm)
end
end
Expand Down
4 changes: 2 additions & 2 deletions activerecord/test/cases/reflection_test.rb
Expand Up @@ -160,8 +160,8 @@ def test_association_reflection_in_modules

def test_reflection_of_all_associations
# FIXME these assertions bust a lot
assert_equal 24, Firm.reflect_on_all_associations.size
assert_equal 18, Firm.reflect_on_all_associations(:has_many).size
assert_equal 26, Firm.reflect_on_all_associations.size
assert_equal 20, Firm.reflect_on_all_associations(:has_many).size
assert_equal 6, Firm.reflect_on_all_associations(:has_one).size
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
end
Expand Down
25 changes: 25 additions & 0 deletions activerecord/test/cases/sanitize_test.rb
@@ -0,0 +1,25 @@
require "cases/helper"
require 'models/binary'

class SanitizeTest < ActiveRecord::TestCase
def setup
end

def test_sanitize_sql_array_handles_string_interpolation
quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi"])
assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi".chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper"])
assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper".chars])
end

def test_sanitize_sql_array_handles_bind_variables
quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi"])
assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi".chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper"])
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".chars])
end
end

0 comments on commit f6c0f68

Please sign in to comment.