Skip to content

Commit

Permalink
Merge branch 'master' of git@github.com:rails/rails
Browse files Browse the repository at this point in the history
  • Loading branch information
dhh committed Feb 6, 2009
2 parents 81c7a5d + be098f8 commit 7a5da7c
Show file tree
Hide file tree
Showing 134 changed files with 18,704 additions and 41 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ railties/pkg
railties/test/500.html
railties/doc/guides/html/images
railties/doc/guides/html/stylesheets
railties/guides/output
*.rbc
*.swp
*.swo
7 changes: 2 additions & 5 deletions actionpack/lib/action_controller/dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ def define_dispatcher_callbacks(cache_classes)
unless cache_classes
# Development mode callbacks
before_dispatch :reload_application
after_dispatch :cleanup_application

ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end
Expand Down Expand Up @@ -93,11 +92,9 @@ def reload_application
run_callbacks :prepare_dispatch

Routing::Routes.reload
end

# Cleanup the application by clearing out loaded classes so they can
# be reloaded on the next request without restarting the server.
def cleanup_application
# Cleanup the application by clearing out loaded classes so they can
# be reloaded on the next request without restarting the server.
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
ActiveSupport::Dependencies.clear
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
Expand Down
2 changes: 1 addition & 1 deletion actionpack/lib/action_controller/layout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def layout_conditions #:nodoc:
end

def default_layout(format) #:nodoc:
layout = read_inheritable_attribute(:layout)
layout = read_inheritable_attribute(:layout) unless format == :js
return layout unless read_inheritable_attribute(:auto_layout)
find_layout(layout, format)
end
Expand Down
25 changes: 22 additions & 3 deletions actionpack/lib/action_controller/session/abstract_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,28 @@ def loaded?
end

def load!
@id, session = @by.send(:load_session, @env)
replace(session)
@loaded = true
stale_session_check! do
@id, session = @by.send(:load_session, @env)
replace(session)
@loaded = true
end
end

def stale_session_check!
yield
rescue ArgumentError => argument_error
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
begin
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
end

retry
else
raise
end
end
end

Expand Down
3 changes: 2 additions & 1 deletion actionpack/lib/action_view/helpers/asset_tag_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,8 @@ def self.cache_asset_timestamps=(value)
def compute_public_path(source, dir, ext = nil, include_host = true)
has_request = @controller.respond_to?(:request)

if ext && (File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}")))
source_ext = File.extname(source)[1..-1]
if ext && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))))
source += ".#{ext}"
end

Expand Down
3 changes: 2 additions & 1 deletion actionpack/lib/action_view/helpers/form_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,8 @@ def fields_for_with_nested_attributes(association_name, args, block)
@template.fields_for(child_name, child, *args, &block)
end.join
else
@template.fields_for(name, association, *args, &block)
object = args.first.respond_to?(:new_record?) ? args.first : association
@template.fields_for(name, object, *args, &block)
end
end

Expand Down
6 changes: 5 additions & 1 deletion actionpack/lib/action_view/paths.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ module ActionView #:nodoc:
class PathSet < Array #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
Template::EagerPath.new(obj)
if !Object.const_defined?(:Rails) || Rails.configuration.cache_classes
Template::EagerPath.new(obj)
else
Template::Path.new(obj)
end
else
obj
end
Expand Down
11 changes: 10 additions & 1 deletion actionpack/test/controller/render_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,9 @@ def render_explicit_html_template
def render_implicit_html_template_from_xhr_request
end

def render_implicit_js_template_without_layout
end

def formatted_html_erb
end

Expand Down Expand Up @@ -681,7 +684,8 @@ def determine_layout
"render_with_explicit_string_template",
"render_js_with_explicit_template",
"render_js_with_explicit_action_template",
"delete_with_js", "update_page", "update_page_with_instance_variables"
"delete_with_js", "update_page", "update_page_with_instance_variables",
"render_implicit_js_template_without_layout"

"layouts/standard"
when "action_talk_to_layout", "layout_overriding_layout"
Expand Down Expand Up @@ -1018,6 +1022,11 @@ def test_should_implicitly_render_html_template_from_xhr_request
assert_equal "Hello HTML!", @response.body
end

def test_should_implicitly_render_js_template_without_layout
get :render_implicit_js_template_without_layout, :format => :js
assert_no_match /<html>/, @response.body
end

def test_should_render_formatted_template
get :formatted_html_erb
assert_equal 'formatted html erb', @response.body
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alert('hello');
9 changes: 9 additions & 0 deletions actionpack/test/template/form_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,15 @@ def test_nested_fields_for_with_a_new_record_on_a_nested_attributes_one_to_one_a
assert_dom_equal expected, output_buffer
end

def test_nested_fields_for_with_explicitly_passed_object_on_a_nested_attributes_one_to_one_association
form_for(:post, @post) do |f|
f.fields_for(:author, Author.new(123)) do |af|
assert_not_nil af.object
assert_equal 123, af.object.id
end
end
end

def test_nested_fields_for_with_an_existing_record_on_a_nested_attributes_one_to_one_association
@post.author = Author.new(321)

Expand Down
16 changes: 16 additions & 0 deletions activerecord/lib/active_record/associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,22 @@ def belongs_to(association_id, options = {})
# but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
# custom <tt>:join_table</tt> option if you need to.
#
# The join table should not have a primary key or a model associated with it. You must manually generate the
# join table with a migration such as this:
#
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration
# def self.up
# create_table :developers_projects, :id => false do |t|
# t.integer :developer_id
# t.integer :project_id
# end
# end
#
# def self.down
# drop_table :developers_projects
# end
# end
#
# Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
# +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as
# readonly (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any
Expand Down
1 change: 1 addition & 0 deletions activerecord/lib/active_record/attribute_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ def query_attribute(attr_name)
if Numeric === value || value !~ /[^0-9]/
!value.to_i.zero?
else
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
!value.blank?
end
elsif column.number?
Expand Down
12 changes: 12 additions & 0 deletions activerecord/lib/active_record/autosave_association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ def self.included(base)
base.class_eval do
alias_method_chain :reload, :autosave_associations
alias_method_chain :save, :autosave_associations
alias_method_chain :save!, :autosave_associations
alias_method_chain :valid?, :autosave_associations

%w{ has_one belongs_to has_many has_and_belongs_to_many }.each do |type|
Expand Down Expand Up @@ -161,6 +162,17 @@ def save_with_autosave_associations(perform_validation = true)
end
end

# Attempts to save the record just like save_with_autosave_associations but
# will raise a RecordInvalid exception instead of returning false if the
# record is not valid.
def save_with_autosave_associations!
if valid_with_autosave_associations?
save_with_autosave_associations(false) || raise(RecordNotSaved)
else
raise RecordInvalid.new(self)
end
end

# Returns whether or not the parent, <tt>self</tt>, and any loaded autosave associations are valid.
def valid_with_autosave_associations?
if valid_without_autosave_associations?
Expand Down
16 changes: 10 additions & 6 deletions activerecord/lib/active_record/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1992,12 +1992,16 @@ def all_attributes_exists?(attribute_names)
attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
end

def attribute_condition(argument)
def attribute_condition(quoted_column_name, argument)
case argument
when nil then "IS ?"
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
when Range then "BETWEEN ? AND ?"
else "= ?"
when nil then "#{quoted_column_name} IS ?"
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "#{quoted_column_name} IN (?)"
when Range then if argument.exclude_end?
"#{quoted_column_name} >= ? AND #{quoted_column_name} < ?"
else
"#{quoted_column_name} BETWEEN ? AND ?"
end
else "#{quoted_column_name} = ?"
end
end

Expand Down Expand Up @@ -2307,7 +2311,7 @@ def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name)
table_name = connection.quote_table_name(table_name)
end

"#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"
attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value)
else
sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module ConnectionAdapters #:nodoc:
# An abstract definition of a column in a table.
class Column
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set

module Format
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
Expand Down
8 changes: 6 additions & 2 deletions activerecord/lib/active_record/nested_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ def self.included(base)
# For each key in the hash that starts with the string 'new' a new model
# will be instantiated. When the proc given with the <tt>:reject_if</tt>
# option evaluates to +false+ for a certain attribute hash no record will
# be built for that hash.
# be built for that hash. (Rejecting new records can alternatively be done
# by utilizing the <tt>'_delete'</tt> key. Scroll down for more info.)
#
# params = { 'member' => {
# 'name' => 'joe', 'posts_attributes' => {
Expand Down Expand Up @@ -258,11 +259,14 @@ def should_destroy_nested_attributes_record?(allow_destroy, attributes)
# If a <tt>:reject_if</tt> proc exists for this association, it will be
# called with the attributes as its argument. If the proc returns a truthy
# value, the record is _not_ build.
#
# Alternatively, you can specify the <tt>'_delete'</tt> key to _not_ build
# a record. See should_destroy_nested_attributes_record? for more info.
def build_new_nested_attributes_record(association_name, attributes)
if reject_proc = self.class.reject_new_nested_attributes_procs[association_name]
return if reject_proc.call(attributes)
end
send(association_name).build(attributes)
send(association_name).build(attributes) unless should_destroy_nested_attributes_record?(true, attributes)
end

# Assigns the attributes to the record specified by +id+. Or marks it for
Expand Down
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/session_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def self.find_by_session_id(*args)
define_method(:session_id=) { |session_id| self.sessid = session_id }
else
def self.find_by_session_id(session_id)
find :first, :conditions => ["session_id #{attribute_condition(session_id)}", session_id]
find :first, :conditions => {:session_id=>session_id}
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@ def validates_uniqueness_of(*attr_names)
if scope = configuration[:scope]
Array(scope).map do |scope_item|
scope_value = record.send(scope_item)
condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}"
condition_sql << " AND " << attribute_condition("#{record.class.quoted_table_name}.#{scope_item}", scope_value)
condition_params << scope_value
end
end
Expand Down
12 changes: 12 additions & 0 deletions activerecord/test/cases/attribute_methods_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ def test_should_unserialize_attributes_for_frozen_records
assert_equal myobj, topic.content
end

def test_typecast_attribute_from_select_to_false
topic = Topic.create(:title => 'Budget')
topic = Topic.find(:first, :select => "topics.*, 1=2 as is_test")
assert !topic.is_test?
end

def test_typecast_attribute_from_select_to_true
topic = Topic.create(:title => 'Budget')
topic = Topic.find(:first, :select => "topics.*, 2=2 as is_test")
assert topic.is_test?
end

def test_kernel_methods_not_implemented_in_activerecord
%w(test name display y).each do |method|
assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
Expand Down
24 changes: 23 additions & 1 deletion activerecord/test/cases/autosave_association_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ def test_should_automatically_save_the_associated_model
assert_equal 'The Vile Insanity', @pirate.reload.ship.name
end

def test_should_automatically_save_bang_the_associated_model
@pirate.ship.name = 'The Vile Insanity'
@pirate.save!
assert_equal 'The Vile Insanity', @pirate.reload.ship.name
end

def test_should_automatically_validate_the_associated_model
@pirate.ship.name = ''
assert !@pirate.valid?
Expand Down Expand Up @@ -245,6 +251,12 @@ def test_should_automatically_save_the_associated_model
assert_equal 'Arr', @ship.reload.pirate.catchphrase
end

def test_should_automatically_save_bang_the_associated_model
@ship.pirate.catchphrase = 'Arr'
@ship.save!
assert_equal 'Arr', @ship.reload.pirate.catchphrase
end

def test_should_automatically_validate_the_associated_model
@ship.pirate.catchphrase = ''
assert !@ship.valid?
Expand Down Expand Up @@ -298,6 +310,14 @@ def test_should_automatically_save_the_associated_models
assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
end

def test_should_automatically_save_bang_the_associated_models
new_names = ['Grace OMalley', 'Privateers Greed']
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }

@pirate.save!
assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
end

def test_should_automatically_validate_the_associated_models
@pirate.send(@association_name).each { |child| child.name = '' }

Expand Down Expand Up @@ -347,7 +367,9 @@ def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_t
def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet
assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }

assert_queries(2) do
@pirate.send(@association_name).class # hack to load the target

assert_queries(3) do
@pirate.catchphrase = 'Yarr'
new_names = ['Grace OMalley', 'Privateers Greed']
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
Expand Down
1 change: 1 addition & 0 deletions activerecord/test/cases/datatype_test_postgresql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase

def setup
@connection = ActiveRecord::Base.connection
@connection.execute("set lc_monetary = 'C'")

@connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )")
@first_array = PostgresqlArray.find(1)
Expand Down
6 changes: 6 additions & 0 deletions activerecord/test/cases/finder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,12 @@ def test_find_on_hash_conditions_with_range
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
end

def test_find_on_hash_conditions_with_end_exclusive_range
assert_equal [1,2,3], Topic.find(:all, :conditions => { :id => 1..3 }).map(&:id).sort
assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1...3 }).map(&:id).sort
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(3, :conditions => { :id => 2...3 }) }
end

def test_find_on_hash_conditions_with_multiple_ranges
assert_equal [1,2,3], Comment.find(:all, :conditions => { :id => 1..3, :post_id => 1..2 }).map(&:id).sort
assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort
Expand Down
Loading

0 comments on commit 7a5da7c

Please sign in to comment.