mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge branch 'master' of git@github.com:rails/rails
This commit is contained in:
commit
df2adc4c51
55 changed files with 911 additions and 644 deletions
|
@ -1,4 +1,4 @@
|
|||
*Edge*
|
||||
*2.3.1 [RC2] (February 27th, 2009)*
|
||||
|
||||
* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones]
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ spec = Gem::Specification.new do |s|
|
|||
s.rubyforge_project = "actionmailer"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 2.3.1' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
|
|
@ -2,7 +2,7 @@ module ActionMailer
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 0
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
*Edge*
|
||||
*2.3.1 [RC2] (February 27th, 2009)*
|
||||
|
||||
* Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 [Eloy Duran/Nate Wiger]
|
||||
|
||||
* Added partial scoping to TranslationHelper#translate, so if you call translate(".foo") from the people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo") [DHH]
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ spec = Gem::Specification.new do |s|
|
|||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
||||
s.add_dependency('activesupport', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
|
||||
s.add_dependency('rack', '>= 0.9.0')
|
||||
|
||||
s.require_path = 'lib'
|
||||
|
|
|
@ -22,7 +22,7 @@ module ActionController #:nodoc:
|
|||
attr_reader :allowed_methods
|
||||
|
||||
def initialize(*allowed_methods)
|
||||
super("Only #{allowed_methods.to_sentence} requests are allowed.")
|
||||
super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
|
||||
@allowed_methods = allowed_methods
|
||||
end
|
||||
|
||||
|
@ -1298,7 +1298,7 @@ module ActionController #:nodoc:
|
|||
rescue ActionView::MissingTemplate => e
|
||||
# Was the implicit template missing, or was it another template?
|
||||
if e.path == default_template_name
|
||||
raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
|
||||
raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence(:locale => :en)}", caller
|
||||
else
|
||||
raise e
|
||||
end
|
||||
|
|
|
@ -32,7 +32,7 @@ module ActionController
|
|||
# <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS
|
||||
# constant above, an UnknownHttpMethod exception is raised.
|
||||
def request_method
|
||||
@request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
|
||||
@request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
|
||||
end
|
||||
|
||||
# Returns the HTTP request \method used for action processing as a
|
||||
|
|
|
@ -2,7 +2,7 @@ module ActionPack #:nodoc:
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 0
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
|
@ -5,17 +5,24 @@ require 'action_view/helpers/form_tag_helper'
|
|||
|
||||
module ActionView
|
||||
module Helpers
|
||||
# Form helpers are designed to make working with models much easier compared to using just standard HTML
|
||||
# elements by providing a set of methods for creating forms based on your models. This helper generates the HTML
|
||||
# for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form
|
||||
# is submitted (i.e., when the user hits the submit button or <tt>form.submit</tt> is called via JavaScript), the form inputs will be bundled into the <tt>params</tt> object and passed back to the controller.
|
||||
# Form helpers are designed to make working with models much easier
|
||||
# compared to using just standard HTML elements by providing a set of
|
||||
# methods for creating forms based on your models. This helper generates
|
||||
# the HTML for forms, providing a method for each sort of input
|
||||
# (e.g., text, password, select, and so on). When the form is submitted
|
||||
# (i.e., when the user hits the submit button or <tt>form.submit</tt> is
|
||||
# called via JavaScript), the form inputs will be bundled into the
|
||||
# <tt>params</tt> object and passed back to the controller.
|
||||
#
|
||||
# There are two types of form helpers: those that specifically work with model attributes and those that don't.
|
||||
# This helper deals with those that work with model attributes; to see an example of form helpers that don't work
|
||||
# with model attributes, check the ActionView::Helpers::FormTagHelper documentation.
|
||||
# There are two types of form helpers: those that specifically work with
|
||||
# model attributes and those that don't. This helper deals with those that
|
||||
# work with model attributes; to see an example of form helpers that don't
|
||||
# work with model attributes, check the ActionView::Helpers::FormTagHelper
|
||||
# documentation.
|
||||
#
|
||||
# The core method of this helper, form_for, gives you the ability to create a form for a model instance;
|
||||
# for example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it:
|
||||
# The core method of this helper, form_for, gives you the ability to create
|
||||
# a form for a model instance; for example, let's say that you have a model
|
||||
# <tt>Person</tt> and want to create a new instance of it:
|
||||
#
|
||||
# # Note: a @person variable will have been created in the controller.
|
||||
# # For example: @person = Person.new
|
||||
|
@ -40,17 +47,22 @@ module ActionView
|
|||
# <%= submit_tag 'Create' %>
|
||||
# <% end %>
|
||||
#
|
||||
# This example will render the <tt>people/_form</tt> partial, setting a local variable called <tt>form</tt> which references the yielded FormBuilder.
|
||||
#
|
||||
# The <tt>params</tt> object created when this form is submitted would look like:
|
||||
# This example will render the <tt>people/_form</tt> partial, setting a
|
||||
# local variable called <tt>form</tt> which references the yielded
|
||||
# FormBuilder. The <tt>params</tt> object created when this form is
|
||||
# submitted would look like:
|
||||
#
|
||||
# {"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
|
||||
#
|
||||
# The params hash has a nested <tt>person</tt> value, which can therefore be accessed with <tt>params[:person]</tt> in the controller.
|
||||
# If were editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than <tt>Person.new</tt> in the controller), the objects
|
||||
# attribute values are filled into the form (e.g., the <tt>person_first_name</tt> field would have that person's first name in it).
|
||||
# The params hash has a nested <tt>person</tt> value, which can therefore
|
||||
# be accessed with <tt>params[:person]</tt> in the controller. If were
|
||||
# editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than
|
||||
# <tt>Person.new</tt> in the controller), the objects attribute values are
|
||||
# filled into the form (e.g., the <tt>person_first_name</tt> field would
|
||||
# have that person's first name in it).
|
||||
#
|
||||
# If the object name contains square brackets the id for the object will be inserted. For example:
|
||||
# If the object name contains square brackets the id for the object will be
|
||||
# inserted. For example:
|
||||
#
|
||||
# <%= text_field "person[]", "name" %>
|
||||
#
|
||||
|
@ -58,8 +70,10 @@ module ActionView
|
|||
#
|
||||
# <input type="text" id="person_<%= @person.id %>_name" name="person[<%= @person.id %>][name]" value="<%= @person.name %>" />
|
||||
#
|
||||
# If the helper is being used to generate a repetitive sequence of similar form elements, for example in a partial
|
||||
# used by <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may come in handy. Example:
|
||||
# If the helper is being used to generate a repetitive sequence of similar
|
||||
# form elements, for example in a partial used by
|
||||
# <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may
|
||||
# come in handy. Example:
|
||||
#
|
||||
# <%= text_field "person", "name", "index" => 1 %>
|
||||
#
|
||||
|
@ -67,14 +81,17 @@ module ActionView
|
|||
#
|
||||
# <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
|
||||
#
|
||||
# An <tt>index</tt> option may also be passed to <tt>form_for</tt> and <tt>fields_for</tt>. This automatically applies
|
||||
# the <tt>index</tt> to all the nested fields.
|
||||
# An <tt>index</tt> option may also be passed to <tt>form_for</tt> and
|
||||
# <tt>fields_for</tt>. This automatically applies the <tt>index</tt> to
|
||||
# all the nested fields.
|
||||
#
|
||||
# There are also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
|
||||
# link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
|
||||
# There are also methods for helping to build form tags in
|
||||
# link:classes/ActionView/Helpers/FormOptionsHelper.html,
|
||||
# link:classes/ActionView/Helpers/DateHelper.html, and
|
||||
# link:classes/ActionView/Helpers/ActiveRecordHelper.html
|
||||
module FormHelper
|
||||
# Creates a form and a scope around a specific model object that is used as
|
||||
# a base for questioning about values for the fields.
|
||||
# Creates a form and a scope around a specific model object that is used
|
||||
# as a base for questioning about values for the fields.
|
||||
#
|
||||
# Rails provides succinct resource-oriented form generation with +form_for+
|
||||
# like this:
|
||||
|
@ -86,13 +103,15 @@ module ActionView
|
|||
# <%= f.text_field :author %><br />
|
||||
# <% end %>
|
||||
#
|
||||
# There, +form_for+ is able to generate the rest of RESTful form parameters
|
||||
# based on introspection on the record, but to understand what it does we
|
||||
# need to dig first into the alternative generic usage it is based upon.
|
||||
# There, +form_for+ is able to generate the rest of RESTful form
|
||||
# parameters based on introspection on the record, but to understand what
|
||||
# it does we need to dig first into the alternative generic usage it is
|
||||
# based upon.
|
||||
#
|
||||
# === Generic form_for
|
||||
#
|
||||
# The generic way to call +form_for+ yields a form builder around a model:
|
||||
# The generic way to call +form_for+ yields a form builder around a
|
||||
# model:
|
||||
#
|
||||
# <% form_for :person, :url => { :action => "update" } do |f| %>
|
||||
# <%= f.error_messages %>
|
||||
|
@ -103,8 +122,8 @@ module ActionView
|
|||
# <% end %>
|
||||
#
|
||||
# There, the first argument is a symbol or string with the name of the
|
||||
# object the form is about, and also the name of the instance variable the
|
||||
# object is stored in.
|
||||
# object the form is about, and also the name of the instance variable
|
||||
# the object is stored in.
|
||||
#
|
||||
# The form builder acts as a regular form helper that somehow carries the
|
||||
# model. Thus, the idea is that
|
||||
|
@ -137,17 +156,18 @@ module ActionView
|
|||
# In any of its variants, the rightmost argument to +form_for+ is an
|
||||
# optional hash of options:
|
||||
#
|
||||
# * <tt>:url</tt> - The URL the form is submitted to. It takes the same fields
|
||||
# you pass to +url_for+ or +link_to+. In particular you may pass here a
|
||||
# named route directly as well. Defaults to the current action.
|
||||
# * <tt>:url</tt> - The URL the form is submitted to. It takes the same
|
||||
# fields you pass to +url_for+ or +link_to+. In particular you may pass
|
||||
# here a named route directly as well. Defaults to the current action.
|
||||
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
|
||||
#
|
||||
# Worth noting is that the +form_for+ tag is called in a ERb evaluation block,
|
||||
# not an ERb output block. So that's <tt><% %></tt>, not <tt><%= %></tt>.
|
||||
# Worth noting is that the +form_for+ tag is called in a ERb evaluation
|
||||
# block, not an ERb output block. So that's <tt><% %></tt>, not
|
||||
# <tt><%= %></tt>.
|
||||
#
|
||||
# Also note that +form_for+ doesn't create an exclusive scope. It's still
|
||||
# possible to use both the stand-alone FormHelper methods and methods from
|
||||
# FormTagHelper. For example:
|
||||
# possible to use both the stand-alone FormHelper methods and methods
|
||||
# from FormTagHelper. For example:
|
||||
#
|
||||
# <% form_for :person, @person, :url => { :action => "update" } do |f| %>
|
||||
# First name: <%= f.text_field :first_name %>
|
||||
|
@ -156,16 +176,16 @@ module ActionView
|
|||
# Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
|
||||
# <% end %>
|
||||
#
|
||||
# This also works for the methods in FormOptionHelper and DateHelper that are
|
||||
# designed to work with an object as base, like FormOptionHelper#collection_select
|
||||
# and DateHelper#datetime_select.
|
||||
# This also works for the methods in FormOptionHelper and DateHelper that
|
||||
# are designed to work with an object as base, like
|
||||
# FormOptionHelper#collection_select and DateHelper#datetime_select.
|
||||
#
|
||||
# === Resource-oriented style
|
||||
#
|
||||
# As we said above, in addition to manually configuring the +form_for+ call,
|
||||
# you can rely on automated resource identification, which will use the conventions
|
||||
# and named routes of that approach. This is the preferred way to use +form_for+
|
||||
# nowadays.
|
||||
# As we said above, in addition to manually configuring the +form_for+
|
||||
# call, you can rely on automated resource identification, which will use
|
||||
# the conventions and named routes of that approach. This is the
|
||||
# preferred way to use +form_for+ nowadays.
|
||||
#
|
||||
# For example, if <tt>@post</tt> is an existing record you want to edit
|
||||
#
|
||||
|
@ -205,8 +225,10 @@ module ActionView
|
|||
#
|
||||
# === Customized form builders
|
||||
#
|
||||
# You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers,
|
||||
# then use your custom builder. For example, let's say you made a helper to automatically add labels to form inputs.
|
||||
# You can also build forms using a customized FormBuilder class. Subclass
|
||||
# FormBuilder and override or define some more helpers, then use your
|
||||
# custom builder. For example, let's say you made a helper to
|
||||
# automatically add labels to form inputs.
|
||||
#
|
||||
# <% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
|
||||
# <%= f.text_field :first_name %>
|
||||
|
@ -219,16 +241,23 @@ module ActionView
|
|||
#
|
||||
# <%= render :partial => f %>
|
||||
#
|
||||
# The rendered template is <tt>people/_labelling_form</tt> and the local variable referencing the form builder is called <tt>labelling_form</tt>.
|
||||
# The rendered template is <tt>people/_labelling_form</tt> and the local
|
||||
# variable referencing the form builder is called
|
||||
# <tt>labelling_form</tt>.
|
||||
#
|
||||
# In many cases you will want to wrap the above in another helper, so you could do something like the following:
|
||||
# The custom FormBuilder class is automatically merged with the options
|
||||
# of a nested fields_for call, unless it's explicitely set.
|
||||
#
|
||||
# In many cases you will want to wrap the above in another helper, so you
|
||||
# could do something like the following:
|
||||
#
|
||||
# def labelled_form_for(record_or_name_or_array, *args, &proc)
|
||||
# options = args.extract_options!
|
||||
# form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
|
||||
# end
|
||||
#
|
||||
# If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag.
|
||||
# If you don't need to attach a form to a model instance, then check out
|
||||
# FormTagHelper#form_tag.
|
||||
def form_for(record_or_name_or_array, *args, &proc)
|
||||
raise ArgumentError, "Missing block" unless block_given?
|
||||
|
||||
|
@ -910,6 +939,11 @@ module ActionView
|
|||
index = ""
|
||||
end
|
||||
|
||||
if options[:builder]
|
||||
args << {} unless args.last.is_a?(Hash)
|
||||
args.last[:builder] ||= options[:builder]
|
||||
end
|
||||
|
||||
case record_or_name_or_array
|
||||
when String, Symbol
|
||||
if nested_attributes_association?(record_or_name_or_array)
|
||||
|
|
|
@ -1001,6 +1001,47 @@ class FormHelperTest < ActionView::TestCase
|
|||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_labelled_builder_with_nested_fields_for_without_options_hash
|
||||
klass = nil
|
||||
|
||||
form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
|
||||
f.fields_for(:comments, Comment.new) do |nested_fields|
|
||||
klass = nested_fields.class
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal LabelledFormBuilder, klass
|
||||
end
|
||||
|
||||
def test_form_for_with_labelled_builder_with_nested_fields_for_with_options_hash
|
||||
klass = nil
|
||||
|
||||
form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
|
||||
f.fields_for(:comments, Comment.new, :index => 'foo') do |nested_fields|
|
||||
klass = nested_fields.class
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal LabelledFormBuilder, klass
|
||||
end
|
||||
|
||||
class LabelledFormBuilderSubclass < LabelledFormBuilder; end
|
||||
|
||||
def test_form_for_with_labelled_builder_with_nested_fields_for_with_custom_builder
|
||||
klass = nil
|
||||
|
||||
form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
|
||||
f.fields_for(:comments, Comment.new, :builder => LabelledFormBuilderSubclass) do |nested_fields|
|
||||
klass = nested_fields.class
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal LabelledFormBuilderSubclass, klass
|
||||
end
|
||||
|
||||
def test_form_for_with_html_options_adds_options_to_form_tag
|
||||
form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
|
||||
expected = "<form action=\"http://www.example.com\" class=\"some_class\" id=\"some_form\" method=\"post\"></form>"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
*Edge*
|
||||
*2.3.1 [RC2] (February 27th, 2009)*
|
||||
|
||||
* Added ActiveRecord::Base.each and ActiveRecord::Base.find_in_batches for batch processing [DHH/Jamis Buck]
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ spec = Gem::Specification.new do |s|
|
|||
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
end
|
||||
|
||||
s.add_dependency('activesupport', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
|
||||
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
|
||||
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
|
||||
|
|
|
@ -22,7 +22,7 @@ module ActiveRecord
|
|||
through_reflection = reflection.through_reflection
|
||||
source_reflection_names = reflection.source_reflection_names
|
||||
source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
|
||||
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '}?")
|
||||
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -786,11 +786,7 @@ module ActiveRecord
|
|||
# 'ORDER BY p.first_name'
|
||||
def has_many(association_id, options = {}, &extension)
|
||||
reflection = create_has_many_reflection(association_id, options, &extension)
|
||||
|
||||
configure_dependency_for_has_many(reflection)
|
||||
|
||||
add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
|
||||
add_multiple_associated_save_callbacks(reflection.name)
|
||||
add_association_callbacks(reflection.name, reflection.options)
|
||||
|
||||
if options[:through]
|
||||
|
@ -872,10 +868,10 @@ module ActiveRecord
|
|||
# [:source]
|
||||
# Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
|
||||
# inferred from the association. <tt>has_one :favorite, :through => :favorites</tt> will look for a
|
||||
# <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
|
||||
# <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
|
||||
# [:source_type]
|
||||
# Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
|
||||
# association is a polymorphic +belongs_to+.
|
||||
# association is a polymorphic +belongs_to+.
|
||||
# [:readonly]
|
||||
# If true, the associated object is readonly through the association.
|
||||
# [:validate]
|
||||
|
@ -898,22 +894,9 @@ module ActiveRecord
|
|||
association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation)
|
||||
else
|
||||
reflection = create_has_one_reflection(association_id, options)
|
||||
|
||||
method_name = "has_one_after_save_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = association_instance_get(reflection.name)
|
||||
if association && (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
|
||||
association[reflection.primary_key_name] = id
|
||||
association.save(true)
|
||||
end
|
||||
end
|
||||
after_save method_name
|
||||
|
||||
add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
|
||||
association_accessor_methods(reflection, HasOneAssociation)
|
||||
association_constructor_method(:build, reflection, HasOneAssociation)
|
||||
association_constructor_method(:create, reflection, HasOneAssociation)
|
||||
|
||||
configure_dependency_for_has_one(reflection)
|
||||
end
|
||||
end
|
||||
|
@ -1006,40 +989,10 @@ module ActiveRecord
|
|||
|
||||
if reflection.options[:polymorphic]
|
||||
association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
|
||||
|
||||
method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = association_instance_get(reflection.name)
|
||||
if association && association.target
|
||||
if association.new_record?
|
||||
association.save(true)
|
||||
end
|
||||
|
||||
if association.updated?
|
||||
self[reflection.primary_key_name] = association.id
|
||||
self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
before_save method_name
|
||||
else
|
||||
association_accessor_methods(reflection, BelongsToAssociation)
|
||||
association_constructor_method(:build, reflection, BelongsToAssociation)
|
||||
association_constructor_method(:create, reflection, BelongsToAssociation)
|
||||
|
||||
method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
if association = association_instance_get(reflection.name)
|
||||
if association.new_record?
|
||||
association.save(true)
|
||||
end
|
||||
|
||||
if association.updated?
|
||||
self[reflection.primary_key_name] = association.id
|
||||
end
|
||||
end
|
||||
end
|
||||
before_save method_name
|
||||
end
|
||||
|
||||
# Create the callbacks to update counter cache
|
||||
|
@ -1067,8 +1020,6 @@ module ActiveRecord
|
|||
)
|
||||
end
|
||||
|
||||
add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
|
||||
|
||||
configure_dependency_for_belongs_to(reflection)
|
||||
end
|
||||
|
||||
|
@ -1234,9 +1185,6 @@ module ActiveRecord
|
|||
# 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
|
||||
def has_and_belongs_to_many(association_id, options = {}, &extension)
|
||||
reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
|
||||
|
||||
add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
|
||||
add_multiple_associated_save_callbacks(reflection.name)
|
||||
collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
|
||||
|
||||
# Don't use a before_destroy callback since users' before_destroy
|
||||
|
@ -1358,70 +1306,6 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def add_single_associated_validation_callbacks(association_name)
|
||||
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
||||
define_method(method_name) do
|
||||
if association = association_instance_get(association_name)
|
||||
errors.add association_name unless association.target.nil? || association.valid?
|
||||
end
|
||||
end
|
||||
|
||||
validate method_name
|
||||
end
|
||||
|
||||
def add_multiple_associated_validation_callbacks(association_name)
|
||||
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = association_instance_get(association_name)
|
||||
|
||||
if association
|
||||
if new_record?
|
||||
association
|
||||
elsif association.loaded?
|
||||
association.select { |record| record.new_record? }
|
||||
else
|
||||
association.target.select { |record| record.new_record? }
|
||||
end.each do |record|
|
||||
errors.add association_name unless record.valid?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
validate method_name
|
||||
end
|
||||
|
||||
def add_multiple_associated_save_callbacks(association_name)
|
||||
method_name = "before_save_associated_records_for_#{association_name}".to_sym
|
||||
define_method(method_name) do
|
||||
@new_record_before_save = new_record?
|
||||
true
|
||||
end
|
||||
before_save method_name
|
||||
|
||||
method_name = "after_create_or_update_associated_records_for_#{association_name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = association_instance_get(association_name)
|
||||
|
||||
records_to_save = if @new_record_before_save
|
||||
association
|
||||
elsif association && association.loaded?
|
||||
association.select { |record| record.new_record? }
|
||||
elsif association && !association.loaded?
|
||||
association.target.select { |record| record.new_record? }
|
||||
else
|
||||
[]
|
||||
end
|
||||
records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank?
|
||||
|
||||
# reconstruct the SQL queries now that we know the owner's id
|
||||
association.send(:construct_sql) if association.respond_to?(:construct_sql)
|
||||
end
|
||||
|
||||
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
||||
after_create method_name
|
||||
after_update method_name
|
||||
end
|
||||
|
||||
def association_constructor_method(constructor, reflection, association_proxy_class)
|
||||
define_method("#{constructor}_#{reflection.name}") do |*params|
|
||||
attributees = params.first unless params.empty?
|
||||
|
|
|
@ -28,12 +28,12 @@ module ActiveRecord
|
|||
load_target.size
|
||||
end
|
||||
|
||||
def insert_record(record, force=true)
|
||||
def insert_record(record, force = true, validate = true)
|
||||
if record.new_record?
|
||||
if force
|
||||
record.save!
|
||||
else
|
||||
return false unless record.save
|
||||
return false unless record.save(validate)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -56,9 +56,9 @@ module ActiveRecord
|
|||
"#{@reflection.name}_count"
|
||||
end
|
||||
|
||||
def insert_record(record)
|
||||
def insert_record(record, force = false, validate = true)
|
||||
set_belongs_to_association_for(record)
|
||||
record.save
|
||||
force ? record.save! : record.save(validate)
|
||||
end
|
||||
|
||||
# Deletes the records according to the <tt>:dependent</tt> option.
|
||||
|
|
|
@ -47,12 +47,12 @@ module ActiveRecord
|
|||
options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil?
|
||||
end
|
||||
|
||||
def insert_record(record, force=true)
|
||||
def insert_record(record, force = true, validate = true)
|
||||
if record.new_record?
|
||||
if force
|
||||
record.save!
|
||||
else
|
||||
return false unless record.save
|
||||
return false unless record.save(validate)
|
||||
end
|
||||
end
|
||||
through_reflection = @reflection.through_reflection
|
||||
|
|
|
@ -125,79 +125,63 @@ module ActiveRecord
|
|||
# post.author.name = ''
|
||||
# post.save(false) # => true
|
||||
module AutosaveAssociation
|
||||
ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many }
|
||||
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
base.extend(ClassMethods)
|
||||
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|
|
||||
ASSOCIATION_TYPES.each do |type|
|
||||
base.send("valid_keys_for_#{type}_association") << :autosave
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Saves the parent, <tt>self</tt>, and any loaded autosave associations.
|
||||
# In addition, it destroys all children that were marked for destruction
|
||||
# with mark_for_destruction.
|
||||
#
|
||||
# This all happens inside a transaction, _if_ the Transactions module is included into
|
||||
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
||||
def save_with_autosave_associations(perform_validation = true)
|
||||
returning(save_without_autosave_associations(perform_validation)) do |valid|
|
||||
if valid
|
||||
self.class.reflect_on_all_autosave_associations.each do |reflection|
|
||||
if (association = association_instance_get(reflection.name)) && association.loaded?
|
||||
if association.is_a?(Array)
|
||||
association.proxy_target.each do |child|
|
||||
child.marked_for_destruction? ? child.destroy : child.save(perform_validation)
|
||||
end
|
||||
else
|
||||
association.marked_for_destruction? ? association.destroy : association.save(perform_validation)
|
||||
end
|
||||
end
|
||||
module ClassMethods
|
||||
private
|
||||
|
||||
# def belongs_to(name, options = {})
|
||||
# super
|
||||
# add_autosave_association_callbacks(reflect_on_association(name))
|
||||
# end
|
||||
ASSOCIATION_TYPES.each do |type|
|
||||
module_eval %{
|
||||
def #{type}(name, options = {})
|
||||
super
|
||||
add_autosave_association_callbacks(reflect_on_association(name))
|
||||
end
|
||||
end
|
||||
}
|
||||
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
|
||||
# Adds a validate and save callback for the association as specified by
|
||||
# the +reflection+.
|
||||
def add_autosave_association_callbacks(reflection)
|
||||
save_method = "autosave_associated_records_for_#{reflection.name}"
|
||||
validation_method = "validate_associated_records_for_#{reflection.name}"
|
||||
validate validation_method
|
||||
|
||||
# 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?
|
||||
self.class.reflect_on_all_autosave_associations.all? do |reflection|
|
||||
if (association = association_instance_get(reflection.name)) && association.loaded?
|
||||
if association.is_a?(Array)
|
||||
association.proxy_target.all? { |child| autosave_association_valid?(reflection, child) }
|
||||
else
|
||||
autosave_association_valid?(reflection, association)
|
||||
end
|
||||
else
|
||||
true # association not loaded yet, so it should be valid
|
||||
case reflection.macro
|
||||
when :has_many, :has_and_belongs_to_many
|
||||
before_save :before_save_collection_association
|
||||
|
||||
define_method(save_method) { save_collection_association(reflection) }
|
||||
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
||||
after_create save_method
|
||||
after_update save_method
|
||||
|
||||
define_method(validation_method) { validate_collection_association(reflection) }
|
||||
else
|
||||
case reflection.macro
|
||||
when :has_one
|
||||
define_method(save_method) { save_has_one_association(reflection) }
|
||||
after_save save_method
|
||||
when :belongs_to
|
||||
define_method(save_method) { save_belongs_to_association(reflection) }
|
||||
before_save save_method
|
||||
end
|
||||
define_method(validation_method) { validate_single_association(reflection) }
|
||||
end
|
||||
else
|
||||
false # self was not valid
|
||||
end
|
||||
end
|
||||
|
||||
# Returns whether or not the association is valid and applies any errors to the parent, <tt>self</tt>, if it wasn't.
|
||||
def autosave_association_valid?(reflection, association)
|
||||
returning(association.valid?) do |valid|
|
||||
association.errors.each do |attribute, message|
|
||||
errors.add "#{reflection.name}_#{attribute}", message
|
||||
end unless valid
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -221,5 +205,145 @@ module ActiveRecord
|
|||
def marked_for_destruction?
|
||||
@marked_for_destruction
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns the record for an association collection that should be validated
|
||||
# or saved. If +autosave+ is +false+ only new records will be returned,
|
||||
# unless the parent is/was a new record itself.
|
||||
def associated_records_to_validate_or_save(association, new_record, autosave)
|
||||
if new_record
|
||||
association
|
||||
elsif association.loaded?
|
||||
autosave ? association : association.select { |record| record.new_record? }
|
||||
else
|
||||
autosave ? association.target : association.target.select { |record| record.new_record? }
|
||||
end
|
||||
end
|
||||
|
||||
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
|
||||
# turned on for the association specified by +reflection+.
|
||||
def validate_single_association(reflection)
|
||||
if reflection.options[:validate] == true || reflection.options[:autosave] == true
|
||||
if (association = association_instance_get(reflection.name)) && !association.target.nil?
|
||||
association_valid?(reflection, association)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Validate the associated records if <tt>:validate</tt> or
|
||||
# <tt>:autosave</tt> is turned on for the association specified by
|
||||
# +reflection+.
|
||||
def validate_collection_association(reflection)
|
||||
if reflection.options[:validate] != false && association = association_instance_get(reflection.name)
|
||||
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
|
||||
records.each { |record| association_valid?(reflection, record) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns whether or not the association is valid and applies any errors to
|
||||
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
|
||||
# enabled records if they're marked_for_destruction?.
|
||||
def association_valid?(reflection, association)
|
||||
unless valid = association.valid?
|
||||
if reflection.options[:autosave]
|
||||
unless association.marked_for_destruction?
|
||||
association.errors.each do |attribute, message|
|
||||
attribute = "#{reflection.name}_#{attribute}"
|
||||
errors.add(attribute, message) unless errors.on(attribute)
|
||||
end
|
||||
end
|
||||
else
|
||||
errors.add(reflection.name)
|
||||
end
|
||||
end
|
||||
valid
|
||||
end
|
||||
|
||||
# Is used as a before_save callback to check while saving a collection
|
||||
# association whether or not the parent was a new record before saving.
|
||||
def before_save_collection_association
|
||||
@new_record_before_save = new_record?
|
||||
true
|
||||
end
|
||||
|
||||
# Saves any new associated records, or all loaded autosave associations if
|
||||
# <tt>:autosave</tt> is enabled on the association.
|
||||
#
|
||||
# In addition, it destroys all children that were marked for destruction
|
||||
# with mark_for_destruction.
|
||||
#
|
||||
# This all happens inside a transaction, _if_ the Transactions module is included into
|
||||
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
||||
def save_collection_association(reflection)
|
||||
if association = association_instance_get(reflection.name)
|
||||
autosave = reflection.options[:autosave]
|
||||
|
||||
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
|
||||
records.each do |record|
|
||||
if autosave && record.marked_for_destruction?
|
||||
record.destroy
|
||||
elsif @new_record_before_save || record.new_record?
|
||||
if autosave
|
||||
association.send(:insert_record, record, false, false)
|
||||
else
|
||||
association.send(:insert_record, record)
|
||||
end
|
||||
elsif autosave
|
||||
record.save(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# reconstruct the SQL queries now that we know the owner's id
|
||||
association.send(:construct_sql) if association.respond_to?(:construct_sql)
|
||||
end
|
||||
end
|
||||
|
||||
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled
|
||||
# on the association.
|
||||
#
|
||||
# In addition, it will destroy the association if it was marked for
|
||||
# destruction with mark_for_destruction.
|
||||
#
|
||||
# This all happens inside a transaction, _if_ the Transactions module is included into
|
||||
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
||||
def save_has_one_association(reflection)
|
||||
if association = association_instance_get(reflection.name)
|
||||
if reflection.options[:autosave] && association.marked_for_destruction?
|
||||
association.destroy
|
||||
elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave]
|
||||
association[reflection.primary_key_name] = id
|
||||
association.save(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled
|
||||
# on the association.
|
||||
#
|
||||
# In addition, it will destroy the association if it was marked for
|
||||
# destruction with mark_for_destruction.
|
||||
#
|
||||
# This all happens inside a transaction, _if_ the Transactions module is included into
|
||||
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
||||
def save_belongs_to_association(reflection)
|
||||
if association = association_instance_get(reflection.name)
|
||||
if reflection.options[:autosave] && association.marked_for_destruction?
|
||||
association.destroy
|
||||
else
|
||||
association.save(false) if association.new_record? || reflection.options[:autosave]
|
||||
|
||||
if association.updated?
|
||||
self[reflection.primary_key_name] = association.id
|
||||
# TODO: Removing this code doesn't seem to matter…
|
||||
if reflection.options[:polymorphic]
|
||||
self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,11 +1,12 @@
|
|||
module ActiveRecord
|
||||
module NamedScope
|
||||
# All subclasses of ActiveRecord::Base have two named \scopes:
|
||||
# * <tt>all</tt> - which is similar to a <tt>find(:all)</tt> query, and
|
||||
# All subclasses of ActiveRecord::Base have one named scope:
|
||||
# * <tt>scoped</tt> - which allows for the creation of anonymous \scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
|
||||
#
|
||||
# These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
|
||||
# intermediate values (scopes) around as first-class objects is convenient.
|
||||
#
|
||||
# You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
extend ClassMethods
|
||||
|
|
|
@ -2,7 +2,7 @@ module ActiveRecord
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 0
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
|
@ -190,19 +190,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
|
||||
end
|
||||
|
||||
def test_assignment_before_parent_saved
|
||||
client = Client.find(:first)
|
||||
apple = Firm.new("name" => "Apple")
|
||||
client.firm = apple
|
||||
assert_equal apple, client.firm
|
||||
assert apple.new_record?
|
||||
assert client.save
|
||||
assert apple.save
|
||||
assert !apple.new_record?
|
||||
assert_equal apple, client.firm
|
||||
assert_equal apple, client.firm(true)
|
||||
end
|
||||
|
||||
def test_assignment_before_child_saved
|
||||
final_cut = Client.new("name" => "Final Cut")
|
||||
firm = Firm.find(1)
|
||||
|
@ -215,19 +202,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal firm, final_cut.firm(true)
|
||||
end
|
||||
|
||||
def test_assignment_before_either_saved
|
||||
final_cut = Client.new("name" => "Final Cut")
|
||||
apple = Firm.new("name" => "Apple")
|
||||
final_cut.firm = apple
|
||||
assert final_cut.new_record?
|
||||
assert apple.new_record?
|
||||
assert final_cut.save
|
||||
assert !final_cut.new_record?
|
||||
assert !apple.new_record?
|
||||
assert_equal apple, final_cut.firm
|
||||
assert_equal apple, final_cut.firm(true)
|
||||
end
|
||||
|
||||
def test_new_record_with_foreign_key_but_no_object
|
||||
c = Client.new("firm_id" => 1)
|
||||
assert_equal Firm.find(:first), c.firm_with_basic_id
|
||||
|
@ -274,90 +248,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 17, reply.replies.size
|
||||
end
|
||||
|
||||
def test_store_two_association_with_one_save
|
||||
num_orders = Order.count
|
||||
num_customers = Customer.count
|
||||
order = Order.new
|
||||
|
||||
customer1 = order.billing = Customer.new
|
||||
customer2 = order.shipping = Customer.new
|
||||
assert order.save
|
||||
assert_equal customer1, order.billing
|
||||
assert_equal customer2, order.shipping
|
||||
|
||||
order.reload
|
||||
|
||||
assert_equal customer1, order.billing
|
||||
assert_equal customer2, order.shipping
|
||||
|
||||
assert_equal num_orders +1, Order.count
|
||||
assert_equal num_customers +2, Customer.count
|
||||
end
|
||||
|
||||
|
||||
def test_store_association_in_two_relations_with_one_save
|
||||
num_orders = Order.count
|
||||
num_customers = Customer.count
|
||||
order = Order.new
|
||||
|
||||
customer = order.billing = order.shipping = Customer.new
|
||||
assert order.save
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
order.reload
|
||||
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
assert_equal num_orders +1, Order.count
|
||||
assert_equal num_customers +1, Customer.count
|
||||
end
|
||||
|
||||
def test_store_association_in_two_relations_with_one_save_in_existing_object
|
||||
num_orders = Order.count
|
||||
num_customers = Customer.count
|
||||
order = Order.create
|
||||
|
||||
customer = order.billing = order.shipping = Customer.new
|
||||
assert order.save
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
order.reload
|
||||
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
assert_equal num_orders +1, Order.count
|
||||
assert_equal num_customers +1, Customer.count
|
||||
end
|
||||
|
||||
def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
|
||||
num_orders = Order.count
|
||||
num_customers = Customer.count
|
||||
order = Order.create
|
||||
|
||||
customer = order.billing = order.shipping = Customer.new
|
||||
assert order.save
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
order.reload
|
||||
|
||||
customer = order.billing = order.shipping = Customer.new
|
||||
|
||||
assert order.save
|
||||
order.reload
|
||||
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
assert_equal num_orders +1, Order.count
|
||||
assert_equal num_customers +2, Customer.count
|
||||
end
|
||||
|
||||
|
||||
def test_association_assignment_sticks
|
||||
post = Post.find(:first)
|
||||
|
||||
|
@ -410,25 +300,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal nil, sponsor.sponsorable_id
|
||||
end
|
||||
|
||||
def test_save_fails_for_invalid_belongs_to
|
||||
assert log = AuditLog.create(:developer_id=>0,:message=>"")
|
||||
|
||||
log.developer = Developer.new
|
||||
assert !log.developer.valid?
|
||||
assert !log.valid?
|
||||
assert !log.save
|
||||
assert_equal "is invalid", log.errors.on("developer")
|
||||
end
|
||||
|
||||
def test_save_succeeds_for_invalid_belongs_to_with_validate_false
|
||||
assert log = AuditLog.create(:developer_id=>0,:message=>"")
|
||||
|
||||
log.unvalidated_developer = Developer.new
|
||||
assert !log.unvalidated_developer.valid?
|
||||
assert log.valid?
|
||||
assert log.save
|
||||
end
|
||||
|
||||
def test_belongs_to_proxy_should_not_respond_to_private_methods
|
||||
assert_raises(NoMethodError) { companies(:first_firm).private_method }
|
||||
assert_raises(NoMethodError) { companies(:second_client).firm.private_method }
|
||||
|
|
|
@ -317,81 +317,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 3, companies(:first_firm).clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_adding_before_save
|
||||
no_of_firms = Firm.count
|
||||
no_of_clients = Client.count
|
||||
|
||||
new_firm = Firm.new("name" => "A New Firm, Inc")
|
||||
c = Client.new("name" => "Apple")
|
||||
|
||||
new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
|
||||
assert_equal 1, new_firm.clients_of_firm.size
|
||||
new_firm.clients_of_firm << c
|
||||
assert_equal 2, new_firm.clients_of_firm.size
|
||||
|
||||
assert_equal no_of_firms, Firm.count # Firm was not saved to database.
|
||||
assert_equal no_of_clients, Client.count # Clients were not saved to database.
|
||||
assert new_firm.save
|
||||
assert !new_firm.new_record?
|
||||
assert !c.new_record?
|
||||
assert_equal new_firm, c.firm
|
||||
assert_equal no_of_firms+1, Firm.count # Firm was saved to database.
|
||||
assert_equal no_of_clients+2, Client.count # Clients were saved to database.
|
||||
|
||||
assert_equal 2, new_firm.clients_of_firm.size
|
||||
assert_equal 2, new_firm.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_invalid_adding
|
||||
firm = Firm.find(1)
|
||||
assert !(firm.clients_of_firm << c = Client.new)
|
||||
assert c.new_record?
|
||||
assert !firm.valid?
|
||||
assert !firm.save
|
||||
assert c.new_record?
|
||||
end
|
||||
|
||||
def test_invalid_adding_before_save
|
||||
no_of_firms = Firm.count
|
||||
no_of_clients = Client.count
|
||||
new_firm = Firm.new("name" => "A New Firm, Inc")
|
||||
new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
|
||||
assert c.new_record?
|
||||
assert !c.valid?
|
||||
assert !new_firm.valid?
|
||||
assert !new_firm.save
|
||||
assert c.new_record?
|
||||
assert new_firm.new_record?
|
||||
end
|
||||
|
||||
def test_invalid_adding_with_validate_false
|
||||
firm = Firm.find(:first)
|
||||
client = Client.new
|
||||
firm.unvalidated_clients_of_firm << client
|
||||
|
||||
assert firm.valid?
|
||||
assert !client.valid?
|
||||
assert firm.save
|
||||
assert client.new_record?
|
||||
end
|
||||
|
||||
def test_valid_adding_with_validate_false
|
||||
no_of_clients = Client.count
|
||||
|
||||
firm = Firm.find(:first)
|
||||
client = Client.new("name" => "Apple")
|
||||
|
||||
assert firm.valid?
|
||||
assert client.valid?
|
||||
assert client.new_record?
|
||||
|
||||
firm.unvalidated_clients_of_firm << client
|
||||
|
||||
assert firm.save
|
||||
assert !client.new_record?
|
||||
assert_equal no_of_clients+1, Client.count
|
||||
end
|
||||
|
||||
def test_build
|
||||
company = companies(:first_firm)
|
||||
new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
|
||||
|
@ -400,10 +325,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal "Another Client", new_client.name
|
||||
assert new_client.new_record?
|
||||
assert_equal new_client, company.clients_of_firm.last
|
||||
company.name += '-changed'
|
||||
assert_queries(2) { assert company.save }
|
||||
assert !new_client.new_record?
|
||||
assert_equal 2, company.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_collection_size_after_building
|
||||
|
@ -428,11 +349,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
def test_build_many
|
||||
company = companies(:first_firm)
|
||||
new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
|
||||
|
||||
assert_equal 2, new_clients.size
|
||||
company.name += '-changed'
|
||||
assert_queries(3) { assert company.save }
|
||||
assert_equal 3, company.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_build_followed_by_save_does_not_load_target
|
||||
|
@ -463,10 +380,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal "Another Client", new_client.name
|
||||
assert new_client.new_record?
|
||||
assert_equal new_client, company.clients_of_firm.last
|
||||
company.name += '-changed'
|
||||
assert_queries(2) { assert company.save }
|
||||
assert !new_client.new_record?
|
||||
assert_equal 2, company.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_build_many_via_block
|
||||
|
@ -480,10 +393,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 2, new_clients.size
|
||||
assert_equal "changed", new_clients.first.name
|
||||
assert_equal "changed", new_clients.last.name
|
||||
|
||||
company.name += '-changed'
|
||||
assert_queries(3) { assert company.save }
|
||||
assert_equal 3, company.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_create_without_loading_association
|
||||
|
@ -501,16 +410,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal 2, first_firm.clients_of_firm.size
|
||||
end
|
||||
|
||||
def test_invalid_build
|
||||
new_client = companies(:first_firm).clients_of_firm.build
|
||||
assert new_client.new_record?
|
||||
assert !new_client.valid?
|
||||
assert_equal new_client, companies(:first_firm).clients_of_firm.last
|
||||
assert !companies(:first_firm).save
|
||||
assert new_client.new_record?
|
||||
assert_equal 1, companies(:first_firm).clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_create
|
||||
force_signal37_to_load_all_clients_of_firm
|
||||
new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
|
||||
|
@ -843,15 +742,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert !firm.clients.include?(:first_client)
|
||||
end
|
||||
|
||||
def test_replace_on_new_object
|
||||
firm = Firm.new("name" => "New Firm")
|
||||
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
|
||||
assert firm.save
|
||||
firm.reload
|
||||
assert_equal 2, firm.clients.length
|
||||
assert firm.clients.include?(Client.find_by_name("New Client"))
|
||||
end
|
||||
|
||||
def test_get_ids
|
||||
assert_equal [companies(:first_client).id, companies(:second_client).id], companies(:first_firm).client_ids
|
||||
end
|
||||
|
@ -879,15 +769,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
assert company.clients_using_sql.loaded?
|
||||
end
|
||||
|
||||
def test_assign_ids
|
||||
firm = Firm.new("name" => "Apple")
|
||||
firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
|
||||
firm.save
|
||||
firm.reload
|
||||
assert_equal 2, firm.clients.length
|
||||
assert firm.clients.include?(companies(:second_client))
|
||||
end
|
||||
|
||||
def test_assign_ids_ignoring_blanks
|
||||
firm = Firm.create!(:name => 'Apple')
|
||||
firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
|
||||
|
@ -910,16 +791,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection, &block) }
|
||||
end
|
||||
|
||||
|
||||
def test_assign_ids_for_through_a_belongs_to
|
||||
post = Post.new(:title => "Assigning IDs works!", :body => "You heared it here first, folks!")
|
||||
post.person_ids = [people(:david).id, people(:michael).id]
|
||||
post.save
|
||||
post.reload
|
||||
assert_equal 2, post.people.length
|
||||
assert post.people.include?(people(:david))
|
||||
end
|
||||
|
||||
def test_dynamic_find_should_respect_association_order_for_through
|
||||
assert_equal Comment.find(10), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'")
|
||||
assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
|
||||
|
|
|
@ -193,28 +193,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal account, firm.account
|
||||
end
|
||||
|
||||
def test_build_before_child_saved
|
||||
firm = Firm.find(1)
|
||||
|
||||
account = firm.account.build("credit_limit" => 1000)
|
||||
assert_equal account, firm.account
|
||||
assert account.new_record?
|
||||
assert firm.save
|
||||
assert_equal account, firm.account
|
||||
assert !account.new_record?
|
||||
end
|
||||
|
||||
def test_build_before_either_saved
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
|
||||
firm.account = account = Account.new("credit_limit" => 1000)
|
||||
assert_equal account, firm.account
|
||||
assert account.new_record?
|
||||
assert firm.save
|
||||
assert_equal account, firm.account
|
||||
assert !account.new_record?
|
||||
end
|
||||
|
||||
def test_failing_build_association
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.save
|
||||
|
@ -253,16 +231,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|||
firm.destroy
|
||||
end
|
||||
|
||||
def test_assignment_before_parent_saved
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.account = a = Account.find(1)
|
||||
assert firm.new_record?
|
||||
assert_equal a, firm.account
|
||||
assert firm.save
|
||||
assert_equal a, firm.account
|
||||
assert_equal a, firm.account(true)
|
||||
end
|
||||
|
||||
def test_finding_with_interpolated_condition
|
||||
firm = Firm.find(:first)
|
||||
superior = firm.clients.create(:name => 'SuperiorCo')
|
||||
|
@ -279,61 +247,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|||
assert_equal a, firm.account
|
||||
assert_equal a, firm.account(true)
|
||||
end
|
||||
|
||||
def test_save_fails_for_invalid_has_one
|
||||
firm = Firm.find(:first)
|
||||
assert firm.valid?
|
||||
|
||||
firm.account = Account.new
|
||||
|
||||
assert !firm.account.valid?
|
||||
assert !firm.valid?
|
||||
assert !firm.save
|
||||
assert_equal "is invalid", firm.errors.on("account")
|
||||
end
|
||||
|
||||
|
||||
def test_save_succeeds_for_invalid_has_one_with_validate_false
|
||||
firm = Firm.find(:first)
|
||||
assert firm.valid?
|
||||
|
||||
firm.unvalidated_account = Account.new
|
||||
|
||||
assert !firm.unvalidated_account.valid?
|
||||
assert firm.valid?
|
||||
assert firm.save
|
||||
end
|
||||
|
||||
def test_assignment_before_either_saved
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.account = a = Account.new("credit_limit" => 1000)
|
||||
assert firm.new_record?
|
||||
assert a.new_record?
|
||||
assert_equal a, firm.account
|
||||
assert firm.save
|
||||
assert !firm.new_record?
|
||||
assert !a.new_record?
|
||||
assert_equal a, firm.account
|
||||
assert_equal a, firm.account(true)
|
||||
end
|
||||
|
||||
def test_not_resaved_when_unchanged
|
||||
firm = Firm.find(:first, :include => :account)
|
||||
firm.name += '-changed'
|
||||
assert_queries(1) { firm.save! }
|
||||
|
||||
firm = Firm.find(:first)
|
||||
firm.account = Account.find(:first)
|
||||
assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
|
||||
|
||||
firm = Firm.find(:first).clone
|
||||
firm.account = Account.find(:first)
|
||||
assert_queries(2) { firm.save! }
|
||||
|
||||
firm = Firm.find(:first).clone
|
||||
firm.account = Account.find(:first).clone
|
||||
assert_queries(2) { firm.save! }
|
||||
end
|
||||
|
||||
def test_save_still_works_after_accessing_nil_has_one
|
||||
jp = Company.new :name => 'Jaded Pixel'
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
require "cases/helper"
|
||||
require "models/pirate"
|
||||
require "models/ship"
|
||||
require "models/ship_part"
|
||||
require "models/bird"
|
||||
require "models/parrot"
|
||||
require "models/treasure"
|
||||
require 'cases/helper'
|
||||
require 'models/bird'
|
||||
require 'models/company'
|
||||
require 'models/customer'
|
||||
require 'models/developer'
|
||||
require 'models/order'
|
||||
require 'models/parrot'
|
||||
require 'models/person'
|
||||
require 'models/pirate'
|
||||
require 'models/post'
|
||||
require 'models/reader'
|
||||
require 'models/ship'
|
||||
require 'models/ship_part'
|
||||
require 'models/treasure'
|
||||
|
||||
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
|
||||
def test_autosave_should_be_a_valid_option_for_has_one
|
||||
|
@ -30,6 +37,383 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
|
||||
def test_save_fails_for_invalid_has_one
|
||||
firm = Firm.find(:first)
|
||||
assert firm.valid?
|
||||
|
||||
firm.account = Account.new
|
||||
|
||||
assert !firm.account.valid?
|
||||
assert !firm.valid?
|
||||
assert !firm.save
|
||||
assert_equal "is invalid", firm.errors.on("account")
|
||||
end
|
||||
|
||||
def test_save_succeeds_for_invalid_has_one_with_validate_false
|
||||
firm = Firm.find(:first)
|
||||
assert firm.valid?
|
||||
|
||||
firm.unvalidated_account = Account.new
|
||||
|
||||
assert !firm.unvalidated_account.valid?
|
||||
assert firm.valid?
|
||||
assert firm.save
|
||||
end
|
||||
|
||||
def test_build_before_child_saved
|
||||
firm = Firm.find(1)
|
||||
|
||||
account = firm.account.build("credit_limit" => 1000)
|
||||
assert_equal account, firm.account
|
||||
assert account.new_record?
|
||||
assert firm.save
|
||||
assert_equal account, firm.account
|
||||
assert !account.new_record?
|
||||
end
|
||||
|
||||
def test_build_before_either_saved
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
|
||||
firm.account = account = Account.new("credit_limit" => 1000)
|
||||
assert_equal account, firm.account
|
||||
assert account.new_record?
|
||||
assert firm.save
|
||||
assert_equal account, firm.account
|
||||
assert !account.new_record?
|
||||
end
|
||||
|
||||
def test_assignment_before_parent_saved
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.account = a = Account.find(1)
|
||||
assert firm.new_record?
|
||||
assert_equal a, firm.account
|
||||
assert firm.save
|
||||
assert_equal a, firm.account
|
||||
assert_equal a, firm.account(true)
|
||||
end
|
||||
|
||||
def test_assignment_before_either_saved
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.account = a = Account.new("credit_limit" => 1000)
|
||||
assert firm.new_record?
|
||||
assert a.new_record?
|
||||
assert_equal a, firm.account
|
||||
assert firm.save
|
||||
assert !firm.new_record?
|
||||
assert !a.new_record?
|
||||
assert_equal a, firm.account
|
||||
assert_equal a, firm.account(true)
|
||||
end
|
||||
|
||||
def test_not_resaved_when_unchanged
|
||||
firm = Firm.find(:first, :include => :account)
|
||||
firm.name += '-changed'
|
||||
assert_queries(1) { firm.save! }
|
||||
|
||||
firm = Firm.find(:first)
|
||||
firm.account = Account.find(:first)
|
||||
assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
|
||||
|
||||
firm = Firm.find(:first).clone
|
||||
firm.account = Account.find(:first)
|
||||
assert_queries(2) { firm.save! }
|
||||
|
||||
firm = Firm.find(:first).clone
|
||||
firm.account = Account.find(:first).clone
|
||||
assert_queries(2) { firm.save! }
|
||||
end
|
||||
end
|
||||
|
||||
class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
|
||||
def test_save_fails_for_invalid_belongs_to
|
||||
assert log = AuditLog.create(:developer_id => 0, :message => "")
|
||||
|
||||
log.developer = Developer.new
|
||||
assert !log.developer.valid?
|
||||
assert !log.valid?
|
||||
assert !log.save
|
||||
assert_equal "is invalid", log.errors.on("developer")
|
||||
end
|
||||
|
||||
def test_save_succeeds_for_invalid_belongs_to_with_validate_false
|
||||
assert log = AuditLog.create(:developer_id => 0, :message=> "")
|
||||
|
||||
log.unvalidated_developer = Developer.new
|
||||
assert !log.unvalidated_developer.valid?
|
||||
assert log.valid?
|
||||
assert log.save
|
||||
end
|
||||
|
||||
def test_assignment_before_parent_saved
|
||||
client = Client.find(:first)
|
||||
apple = Firm.new("name" => "Apple")
|
||||
client.firm = apple
|
||||
assert_equal apple, client.firm
|
||||
assert apple.new_record?
|
||||
assert client.save
|
||||
assert apple.save
|
||||
assert !apple.new_record?
|
||||
assert_equal apple, client.firm
|
||||
assert_equal apple, client.firm(true)
|
||||
end
|
||||
|
||||
def test_assignment_before_either_saved
|
||||
final_cut = Client.new("name" => "Final Cut")
|
||||
apple = Firm.new("name" => "Apple")
|
||||
final_cut.firm = apple
|
||||
assert final_cut.new_record?
|
||||
assert apple.new_record?
|
||||
assert final_cut.save
|
||||
assert !final_cut.new_record?
|
||||
assert !apple.new_record?
|
||||
assert_equal apple, final_cut.firm
|
||||
assert_equal apple, final_cut.firm(true)
|
||||
end
|
||||
|
||||
def test_store_two_association_with_one_save
|
||||
num_orders = Order.count
|
||||
num_customers = Customer.count
|
||||
order = Order.new
|
||||
|
||||
customer1 = order.billing = Customer.new
|
||||
customer2 = order.shipping = Customer.new
|
||||
assert order.save
|
||||
assert_equal customer1, order.billing
|
||||
assert_equal customer2, order.shipping
|
||||
|
||||
order.reload
|
||||
|
||||
assert_equal customer1, order.billing
|
||||
assert_equal customer2, order.shipping
|
||||
|
||||
assert_equal num_orders +1, Order.count
|
||||
assert_equal num_customers +2, Customer.count
|
||||
end
|
||||
|
||||
def test_store_association_in_two_relations_with_one_save
|
||||
num_orders = Order.count
|
||||
num_customers = Customer.count
|
||||
order = Order.new
|
||||
|
||||
customer = order.billing = order.shipping = Customer.new
|
||||
assert order.save
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
order.reload
|
||||
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
assert_equal num_orders +1, Order.count
|
||||
assert_equal num_customers +1, Customer.count
|
||||
end
|
||||
|
||||
def test_store_association_in_two_relations_with_one_save_in_existing_object
|
||||
num_orders = Order.count
|
||||
num_customers = Customer.count
|
||||
order = Order.create
|
||||
|
||||
customer = order.billing = order.shipping = Customer.new
|
||||
assert order.save
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
order.reload
|
||||
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
assert_equal num_orders +1, Order.count
|
||||
assert_equal num_customers +1, Customer.count
|
||||
end
|
||||
|
||||
def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
|
||||
num_orders = Order.count
|
||||
num_customers = Customer.count
|
||||
order = Order.create
|
||||
|
||||
customer = order.billing = order.shipping = Customer.new
|
||||
assert order.save
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
order.reload
|
||||
|
||||
customer = order.billing = order.shipping = Customer.new
|
||||
|
||||
assert order.save
|
||||
order.reload
|
||||
|
||||
assert_equal customer, order.billing
|
||||
assert_equal customer, order.shipping
|
||||
|
||||
assert_equal num_orders +1, Order.count
|
||||
assert_equal num_customers +2, Customer.count
|
||||
end
|
||||
end
|
||||
|
||||
class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
|
||||
fixtures :companies, :people
|
||||
|
||||
def test_invalid_adding
|
||||
firm = Firm.find(1)
|
||||
assert !(firm.clients_of_firm << c = Client.new)
|
||||
assert c.new_record?
|
||||
assert !firm.valid?
|
||||
assert !firm.save
|
||||
assert c.new_record?
|
||||
end
|
||||
|
||||
def test_invalid_adding_before_save
|
||||
no_of_firms = Firm.count
|
||||
no_of_clients = Client.count
|
||||
new_firm = Firm.new("name" => "A New Firm, Inc")
|
||||
new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
|
||||
assert c.new_record?
|
||||
assert !c.valid?
|
||||
assert !new_firm.valid?
|
||||
assert !new_firm.save
|
||||
assert c.new_record?
|
||||
assert new_firm.new_record?
|
||||
end
|
||||
|
||||
def test_invalid_adding_with_validate_false
|
||||
firm = Firm.find(:first)
|
||||
client = Client.new
|
||||
firm.unvalidated_clients_of_firm << client
|
||||
|
||||
assert firm.valid?
|
||||
assert !client.valid?
|
||||
assert firm.save
|
||||
assert client.new_record?
|
||||
end
|
||||
|
||||
def test_valid_adding_with_validate_false
|
||||
no_of_clients = Client.count
|
||||
|
||||
firm = Firm.find(:first)
|
||||
client = Client.new("name" => "Apple")
|
||||
|
||||
assert firm.valid?
|
||||
assert client.valid?
|
||||
assert client.new_record?
|
||||
|
||||
firm.unvalidated_clients_of_firm << client
|
||||
|
||||
assert firm.save
|
||||
assert !client.new_record?
|
||||
assert_equal no_of_clients+1, Client.count
|
||||
end
|
||||
|
||||
def test_invalid_build
|
||||
new_client = companies(:first_firm).clients_of_firm.build
|
||||
assert new_client.new_record?
|
||||
assert !new_client.valid?
|
||||
assert_equal new_client, companies(:first_firm).clients_of_firm.last
|
||||
assert !companies(:first_firm).save
|
||||
assert new_client.new_record?
|
||||
assert_equal 1, companies(:first_firm).clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_adding_before_save
|
||||
no_of_firms = Firm.count
|
||||
no_of_clients = Client.count
|
||||
|
||||
new_firm = Firm.new("name" => "A New Firm, Inc")
|
||||
c = Client.new("name" => "Apple")
|
||||
|
||||
new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
|
||||
assert_equal 1, new_firm.clients_of_firm.size
|
||||
new_firm.clients_of_firm << c
|
||||
assert_equal 2, new_firm.clients_of_firm.size
|
||||
|
||||
assert_equal no_of_firms, Firm.count # Firm was not saved to database.
|
||||
assert_equal no_of_clients, Client.count # Clients were not saved to database.
|
||||
assert new_firm.save
|
||||
assert !new_firm.new_record?
|
||||
assert !c.new_record?
|
||||
assert_equal new_firm, c.firm
|
||||
assert_equal no_of_firms+1, Firm.count # Firm was saved to database.
|
||||
assert_equal no_of_clients+2, Client.count # Clients were saved to database.
|
||||
|
||||
assert_equal 2, new_firm.clients_of_firm.size
|
||||
assert_equal 2, new_firm.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_assign_ids
|
||||
firm = Firm.new("name" => "Apple")
|
||||
firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
|
||||
firm.save
|
||||
firm.reload
|
||||
assert_equal 2, firm.clients.length
|
||||
assert firm.clients.include?(companies(:second_client))
|
||||
end
|
||||
|
||||
def test_assign_ids_for_through_a_belongs_to
|
||||
post = Post.new(:title => "Assigning IDs works!", :body => "You heared it here first, folks!")
|
||||
post.person_ids = [people(:david).id, people(:michael).id]
|
||||
post.save
|
||||
post.reload
|
||||
assert_equal 2, post.people.length
|
||||
assert post.people.include?(people(:david))
|
||||
end
|
||||
|
||||
def test_build_before_save
|
||||
company = companies(:first_firm)
|
||||
new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
|
||||
assert !company.clients_of_firm.loaded?
|
||||
|
||||
company.name += '-changed'
|
||||
assert_queries(2) { assert company.save }
|
||||
assert !new_client.new_record?
|
||||
assert_equal 2, company.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_build_many_before_save
|
||||
company = companies(:first_firm)
|
||||
new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
|
||||
|
||||
company.name += '-changed'
|
||||
assert_queries(3) { assert company.save }
|
||||
assert_equal 3, company.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_build_via_block_before_save
|
||||
company = companies(:first_firm)
|
||||
new_client = assert_no_queries { company.clients_of_firm.build {|client| client.name = "Another Client" } }
|
||||
assert !company.clients_of_firm.loaded?
|
||||
|
||||
company.name += '-changed'
|
||||
assert_queries(2) { assert company.save }
|
||||
assert !new_client.new_record?
|
||||
assert_equal 2, company.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_build_many_via_block_before_save
|
||||
company = companies(:first_firm)
|
||||
new_clients = assert_no_queries do
|
||||
company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
|
||||
client.name = "changed"
|
||||
end
|
||||
end
|
||||
|
||||
company.name += '-changed'
|
||||
assert_queries(3) { assert company.save }
|
||||
assert_equal 3, company.clients_of_firm(true).size
|
||||
end
|
||||
|
||||
def test_replace_on_new_object
|
||||
firm = Firm.new("name" => "New Firm")
|
||||
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
|
||||
assert firm.save
|
||||
firm.reload
|
||||
assert_equal 2, firm.clients.length
|
||||
assert firm.clients.include?(Client.find_by_name("New Client"))
|
||||
end
|
||||
end
|
||||
|
||||
class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
|
@ -62,6 +446,14 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
|
|||
assert_nil Ship.find_by_id(id)
|
||||
end
|
||||
|
||||
def test_should_skip_validation_on_a_child_association_if_marked_for_destruction
|
||||
@pirate.ship.name = ''
|
||||
assert !@pirate.valid?
|
||||
|
||||
@pirate.ship.mark_for_destruction
|
||||
assert_difference('Ship.count', -1) { @pirate.save! }
|
||||
end
|
||||
|
||||
def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child
|
||||
# Stub the save method of the @pirate.ship instance to destroy and then raise an exception
|
||||
class << @pirate.ship
|
||||
|
@ -91,6 +483,14 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
|
|||
assert_nil Pirate.find_by_id(id)
|
||||
end
|
||||
|
||||
def test_should_skip_validation_on_a_parent_association_if_marked_for_destruction
|
||||
@ship.pirate.catchphrase = ''
|
||||
assert !@ship.valid?
|
||||
|
||||
@ship.pirate.mark_for_destruction
|
||||
assert_difference('Pirate.count', -1) { @ship.save! }
|
||||
end
|
||||
|
||||
def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent
|
||||
# Stub the save method of the @ship.pirate instance to destroy and then raise an exception
|
||||
class << @ship.pirate
|
||||
|
@ -124,6 +524,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
|
|||
ids.each { |id| assert_nil klass.find_by_id(id) }
|
||||
end
|
||||
|
||||
define_method("test_should_skip_validation_on_the_#{association_name}_association_if_marked_for_destruction") do
|
||||
2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
|
||||
children = @pirate.send(association_name)
|
||||
|
||||
children.each { |child| child.name = '' }
|
||||
assert !@pirate.valid?
|
||||
|
||||
children.each { |child| child.mark_for_destruction }
|
||||
assert_difference("#{association_name.classify}.count", -2) { @pirate.save! }
|
||||
end
|
||||
|
||||
define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do
|
||||
2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
|
||||
before = @pirate.send(association_name).map { |c| c }
|
||||
|
@ -181,6 +592,14 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
|
|||
assert !@pirate.errors.on(:ship_name).blank?
|
||||
end
|
||||
|
||||
def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
|
||||
@pirate.ship.name = nil
|
||||
@pirate.catchphrase = nil
|
||||
assert !@pirate.valid?
|
||||
assert !@pirate.errors.on(:ship_name).blank?
|
||||
assert !@pirate.errors.on(:catchphrase).blank?
|
||||
end
|
||||
|
||||
def test_should_still_allow_to_bypass_validations_on_the_associated_model
|
||||
@pirate.catchphrase = ''
|
||||
@pirate.ship.name = ''
|
||||
|
@ -263,6 +682,14 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
|
|||
assert !@ship.errors.on(:pirate_catchphrase).blank?
|
||||
end
|
||||
|
||||
def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
|
||||
@ship.name = nil
|
||||
@ship.pirate.catchphrase = nil
|
||||
assert !@ship.valid?
|
||||
assert !@ship.errors.on(:name).blank?
|
||||
assert !@ship.errors.on(:pirate_catchphrase).blank?
|
||||
end
|
||||
|
||||
def test_should_still_allow_to_bypass_validations_on_the_associated_model
|
||||
@ship.pirate.catchphrase = ''
|
||||
@ship.name = ''
|
||||
|
@ -326,7 +753,24 @@ module AutosaveAssociationOnACollectionAssociationTests
|
|||
assert @pirate.errors.on(@association_name).blank?
|
||||
end
|
||||
|
||||
def test_should_still_allow_to_bypass_validations_on_the_associated_models
|
||||
def test_should_not_use_default_invalid_error_on_associated_models
|
||||
@pirate.send(@association_name).build(:name => '')
|
||||
|
||||
assert !@pirate.valid?
|
||||
assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name")
|
||||
assert @pirate.errors.on(@association_name).blank?
|
||||
end
|
||||
|
||||
def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
|
||||
@pirate.send(@association_name).each { |child| child.name = '' }
|
||||
@pirate.catchphrase = nil
|
||||
|
||||
assert !@pirate.valid?
|
||||
assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name")
|
||||
assert !@pirate.errors.on(:catchphrase).blank?
|
||||
end
|
||||
|
||||
def test_should_allow_to_bypass_validations_on_the_associated_models_on_update
|
||||
@pirate.catchphrase = ''
|
||||
@pirate.send(@association_name).each { |child| child.name = '' }
|
||||
|
||||
|
@ -338,6 +782,20 @@ module AutosaveAssociationOnACollectionAssociationTests
|
|||
]
|
||||
end
|
||||
|
||||
def test_should_validation_the_associated_models_on_create
|
||||
assert_no_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count") do
|
||||
2.times { @pirate.send(@association_name).build }
|
||||
@pirate.save(true)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_to_bypass_validations_on_the_associated_models_on_create
|
||||
assert_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count", +2) do
|
||||
2.times { @pirate.send(@association_name).build }
|
||||
@pirate.save(false)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
|
||||
before = [@pirate.catchphrase, *@pirate.send(@association_name).map(&:name)]
|
||||
new_names = ['Grace OMalley', 'Privateers Greed']
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
*2.3.1 [RC2] (February 27th, 2009)*
|
||||
|
||||
* Nothing new, just included in 2.3.1
|
||||
|
||||
|
||||
*2.3.0 [RC1] (February 1st, 2009)*
|
||||
|
||||
* Nothing new, just included in 2.3.0
|
||||
|
|
|
@ -67,7 +67,7 @@ spec = Gem::Specification.new do |s|
|
|||
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
end
|
||||
|
||||
s.add_dependency('activesupport', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
|
||||
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'active_resource'
|
||||
|
|
|
@ -2,7 +2,7 @@ module ActiveResource
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 0
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
*Edge*
|
||||
*2.3.1 [RC2] (February 27th, 2009)*
|
||||
|
||||
* Vendorize i18n 0.1.3 gem (fixes issues with incompatible character encodings in Ruby 1.9) #2038 [Akira Matsuda]
|
||||
|
||||
* Update bundled memcache-client from 1.5.0.5 to 1.6.4.99. See http://www.mikeperham.com/2009/02/15/memcache-client-performance/ [Mike Perham]
|
||||
|
||||
|
|
|
@ -7,10 +7,9 @@ module ActiveSupport #:nodoc:
|
|||
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ")
|
||||
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ")
|
||||
def to_sentence(options = {})
|
||||
|
||||
default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale])
|
||||
default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale])
|
||||
default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale])
|
||||
default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
|
||||
default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
|
||||
|
||||
# Try to emulate to_senteces previous to 2.3
|
||||
if options.has_key?(:connector) || options.has_key?(:skip_last_comma)
|
||||
|
|
|
@ -70,7 +70,7 @@ module ActiveSupport
|
|||
[:years, :months, :days, :minutes, :seconds].map do |length|
|
||||
n = consolidated[length]
|
||||
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
|
||||
end.compact.to_sentence
|
||||
end.compact.to_sentence(:locale => :en)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -22,8 +22,8 @@ end
|
|||
|
||||
# TODO I18n gem has not been released yet
|
||||
# begin
|
||||
# gem 'i18n', '~> 0.1.1'
|
||||
# gem 'i18n', '~> 0.1.3'
|
||||
# rescue Gem::LoadError
|
||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.1/lib"
|
||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.3/lib"
|
||||
require 'i18n'
|
||||
# end
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.DS_Store
|
||||
test/rails/fixtures
|
||||
doc
|
|
@ -1,7 +1,7 @@
|
|||
Gem::Specification.new do |s|
|
||||
s.name = "i18n"
|
||||
s.version = "0.1.1"
|
||||
s.date = "2008-10-26"
|
||||
s.version = "0.1.3"
|
||||
s.date = "2009-01-09"
|
||||
s.summary = "Internationalization support for Ruby"
|
||||
s.email = "rails-i18n@googlegroups.com"
|
||||
s.homepage = "http://rails-i18n.org"
|
|
@ -151,12 +151,7 @@ module I18n
|
|||
def interpolate(locale, string, values = {})
|
||||
return string unless string.is_a?(String)
|
||||
|
||||
if string.respond_to?(:force_encoding)
|
||||
original_encoding = string.encoding
|
||||
string.force_encoding(Encoding::BINARY)
|
||||
end
|
||||
|
||||
result = string.gsub(MATCH) do
|
||||
string.gsub(MATCH) do
|
||||
escaped, pattern, key = $1, $2, $2.to_sym
|
||||
|
||||
if escaped
|
||||
|
@ -169,9 +164,6 @@ module I18n
|
|||
values[key].to_s
|
||||
end
|
||||
end
|
||||
|
||||
result.force_encoding(original_encoding) if original_encoding
|
||||
result
|
||||
end
|
||||
|
||||
# Loads a single translations file by delegating to #load_rb or
|
|
@ -253,6 +253,32 @@ class I18nSimpleBackendInterpolateTest < Test::Unit::TestCase
|
|||
assert_equal 'Häi David!', @backend.send(:interpolate, nil, 'Häi {{name}}!', :name => 'David')
|
||||
end
|
||||
|
||||
def test_interpolate_given_an_unicode_value_hash_interpolates_to_the_string
|
||||
assert_equal 'Hi ゆきひろ!', @backend.send(:interpolate, nil, 'Hi {{name}}!', :name => 'ゆきひろ')
|
||||
end
|
||||
|
||||
def test_interpolate_given_an_unicode_value_hash_interpolates_into_unicode_string
|
||||
assert_equal 'こんにちは、ゆきひろさん!', @backend.send(:interpolate, nil, 'こんにちは、{{name}}さん!', :name => 'ゆきひろ')
|
||||
end
|
||||
|
||||
if Kernel.const_defined?(:Encoding)
|
||||
def test_interpolate_given_a_non_unicode_multibyte_value_hash_interpolates_into_a_string_with_the_same_encoding
|
||||
assert_equal euc_jp('Hi ゆきひろ!'), @backend.send(:interpolate, nil, 'Hi {{name}}!', :name => euc_jp('ゆきひろ'))
|
||||
end
|
||||
|
||||
def test_interpolate_given_an_unicode_value_hash_into_a_non_unicode_multibyte_string_raises_encoding_compatibility_error
|
||||
assert_raises(Encoding::CompatibilityError) do
|
||||
@backend.send(:interpolate, nil, euc_jp('こんにちは、{{name}}さん!'), :name => 'ゆきひろ')
|
||||
end
|
||||
end
|
||||
|
||||
def test_interpolate_given_a_non_unicode_multibyte_value_hash_into_an_unicode_string_raises_encoding_compatibility_error
|
||||
assert_raises(Encoding::CompatibilityError) do
|
||||
@backend.send(:interpolate, nil, 'こんにちは、{{name}}さん!', :name => euc_jp('ゆきひろ'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_interpolate_given_nil_as_a_string_returns_nil
|
||||
assert_nil @backend.send(:interpolate, nil, nil, :name => 'David')
|
||||
end
|
||||
|
@ -272,6 +298,12 @@ class I18nSimpleBackendInterpolateTest < Test::Unit::TestCase
|
|||
def test_interpolate_given_a_string_containing_a_reserved_key_raises_reserved_interpolation_key
|
||||
assert_raises(I18n::ReservedInterpolationKey) { @backend.send(:interpolate, nil, '{{default}}', {:default => nil}) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def euc_jp(string)
|
||||
string.encode!(Encoding::EUC_JP)
|
||||
end
|
||||
end
|
||||
|
||||
class I18nSimpleBackendLocalizeDateTest < Test::Unit::TestCase
|
||||
|
@ -485,6 +517,10 @@ end
|
|||
class I18nSimpleBackendLoadPathTest < Test::Unit::TestCase
|
||||
include I18nSimpleBackendTestSetup
|
||||
|
||||
def teardown
|
||||
I18n.load_path = []
|
||||
end
|
||||
|
||||
def test_nested_load_paths_do_not_break_locale_loading
|
||||
@backend = I18n::Backend::Simple.new
|
||||
I18n.load_path = [[File.dirname(__FILE__) + '/locale/en.yml']]
|
||||
|
@ -492,6 +528,14 @@ class I18nSimpleBackendLoadPathTest < Test::Unit::TestCase
|
|||
assert_nothing_raised { @backend.send :init_translations }
|
||||
assert_not_nil backend_get_translations
|
||||
end
|
||||
|
||||
def test_adding_arrays_of_filenames_to_load_path_do_not_break_locale_loading
|
||||
@backend = I18n::Backend::Simple.new
|
||||
I18n.load_path << Dir[File.dirname(__FILE__) + '/locale/*.{rb,yml}']
|
||||
assert_nil backend_get_translations
|
||||
assert_nothing_raised { @backend.send :init_translations }
|
||||
assert_not_nil backend_get_translations
|
||||
end
|
||||
end
|
||||
|
||||
class I18nSimpleBackendReloadTranslationsTest < Test::Unit::TestCase
|
||||
|
@ -521,4 +565,4 @@ class I18nSimpleBackendReloadTranslationsTest < Test::Unit::TestCase
|
|||
@backend.reload!
|
||||
assert_equal @backend.initialized?, false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@ module ActiveSupport
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 0
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
*2.3.1 [RC2] (February 27th, 2009)*
|
||||
|
||||
* Allow metal to live in plugins #2045 [Matthew Rudy]
|
||||
|
||||
|
||||
*2.3.0 [RC1] (February 1st, 2009)*
|
||||
|
||||
* Added metal [Josh Peek]
|
||||
|
||||
* Remove script/performance/request in favour of the performance integration tests. [Pratik Naik]
|
||||
|
||||
To continue using script/performance/request, install the request_profiler plugin :
|
||||
|
|
|
@ -311,11 +311,11 @@ spec = Gem::Specification.new do |s|
|
|||
EOF
|
||||
|
||||
s.add_dependency('rake', '>= 0.8.3')
|
||||
s.add_dependency('activesupport', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('actionmailer', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('activeresource', '= 2.3.0' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 2.3.1' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 2.3.1' + PKG_BUILD)
|
||||
s.add_dependency('actionmailer', '= 2.3.1' + PKG_BUILD)
|
||||
s.add_dependency('activeresource', '= 2.3.1' + PKG_BUILD)
|
||||
|
||||
s.rdoc_options << '--exclude' << '.'
|
||||
s.has_rdoc = false
|
||||
|
|
|
@ -559,6 +559,8 @@ Run `rake gems:install` to install the missing gems.
|
|||
end
|
||||
|
||||
def initialize_metal
|
||||
Rails::Rack::Metal.metal_paths += plugin_loader.engine_metal_paths
|
||||
|
||||
configuration.middleware.insert_before(
|
||||
:"ActionController::RewindableInput",
|
||||
Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?)
|
||||
|
|
|
@ -80,6 +80,10 @@ module Rails
|
|||
File.join(directory, 'app', 'controllers')
|
||||
end
|
||||
|
||||
def metal_path
|
||||
File.join(directory, 'app', 'metal')
|
||||
end
|
||||
|
||||
def routing_file
|
||||
File.join(directory, 'config', 'routes.rb')
|
||||
end
|
||||
|
@ -100,7 +104,7 @@ module Rails
|
|||
|
||||
|
||||
def app_paths
|
||||
[ File.join(directory, 'app', 'models'), File.join(directory, 'app', 'helpers'), controller_path ]
|
||||
[ File.join(directory, 'app', 'models'), File.join(directory, 'app', 'helpers'), controller_path, metal_path ]
|
||||
end
|
||||
|
||||
def lib_path
|
||||
|
@ -160,4 +164,4 @@ module Rails
|
|||
File.join(directory, 'rails', 'init.rb')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,6 +65,9 @@ module Rails
|
|||
$LOAD_PATH.uniq!
|
||||
end
|
||||
|
||||
def engine_metal_paths
|
||||
engines.collect(&:metal_path)
|
||||
end
|
||||
|
||||
protected
|
||||
def configure_engines
|
||||
|
@ -178,11 +181,11 @@ module Rails
|
|||
if explicit_plugin_loading_order?
|
||||
if configuration.plugins.detect {|plugin| plugin != :all && !loaded?(plugin) }
|
||||
missing_plugins = configuration.plugins - (plugins.map{|p| p.name.to_sym} + [:all])
|
||||
raise LoadError, "Could not locate the following plugins: #{missing_plugins.to_sentence}"
|
||||
raise LoadError, "Could not locate the following plugins: #{missing_plugins.to_sentence(:locale => :en)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,14 +6,17 @@ module Rails
|
|||
NotFoundResponse = [404, {}, []].freeze
|
||||
NotFound = lambda { NotFoundResponse }
|
||||
|
||||
def self.metals
|
||||
base = "#{Rails.root}/app/metal"
|
||||
matcher = /\A#{Regexp.escape(base)}\/(.*)\.rb\Z/
|
||||
cattr_accessor :metal_paths
|
||||
self.metal_paths = ["#{Rails.root}/app/metal"]
|
||||
|
||||
Dir["#{base}/**/*.rb"].sort.map do |file|
|
||||
file.sub!(matcher, '\1')
|
||||
require file
|
||||
file.classify.constantize
|
||||
def self.metals
|
||||
matcher = /#{Regexp.escape('/app/metal/')}(.*)\.rb\Z/
|
||||
metal_glob = metal_paths.map{ |base| "#{base}/**/*.rb" }
|
||||
|
||||
Dir[*metal_glob].sort.map do |file|
|
||||
path = file.match(matcher)[1]
|
||||
require path
|
||||
path.classify.constantize
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ module Rails
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 0
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
|
@ -48,7 +48,7 @@ task :test do
|
|||
task
|
||||
end
|
||||
end.compact
|
||||
abort "Errors running #{errors.to_sentence}!" if errors.any?
|
||||
abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any?
|
||||
end
|
||||
|
||||
namespace :test do
|
||||
|
|
10
railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb
vendored
Normal file
10
railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
class EngineMetal
|
||||
def self.call(env)
|
||||
if env["PATH_INFO"] =~ /^\/metal/
|
||||
[200, {"Content-Type" => "text/html"}, ["Engine metal"]]
|
||||
else
|
||||
[404, {"Content-Type" => "text/html"}, ["Not Found"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -120,7 +120,7 @@ class TestPluginLoader < Test::Unit::TestCase
|
|||
|
||||
@loader.add_plugin_load_paths
|
||||
|
||||
%w( models controllers helpers ).each do |app_part|
|
||||
%w( models controllers metal helpers ).each do |app_part|
|
||||
assert ActiveSupport::Dependencies.load_paths.include?(
|
||||
File.join(plugin_fixture_path('engines/engine'), 'app', app_part)
|
||||
), "Couldn't find #{app_part} in load path"
|
||||
|
@ -161,4 +161,4 @@ class TestPluginLoader < Test::Unit::TestCase
|
|||
$LOAD_PATH.clear
|
||||
ORIGINAL_LOAD_PATH.each { |path| $LOAD_PATH << path }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue