Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge remote branch 'rails/master' into pg_schema_fu

  • Loading branch information...
commit 2b3d67fdaf2c78a5447316a90c54c7c9c520c71a 2 parents b0d5990 + 6c705e3
@tardate authored
Showing with 1,294 additions and 542 deletions.
  1. +5 −2 Gemfile
  2. +3 −3 README.rdoc
  3. +21 −2 Rakefile
  4. +5 −5 actionmailer/lib/action_mailer/base.rb
  5. +28 −21 actionpack/CHANGELOG
  6. +1 −0  actionpack/lib/action_controller/metal/redirecting.rb
  7. +3 −3 actionpack/lib/action_dispatch/http/headers.rb
  8. +11 −1 actionpack/lib/action_dispatch/http/upload.rb
  9. +1 −1  actionpack/lib/action_dispatch/routing.rb
  10. +4 −4 actionpack/lib/action_dispatch/routing/mapper.rb
  11. +1 −1  actionpack/lib/action_view/helpers/controller_helper.rb
  12. +6 −7 actionpack/lib/action_view/helpers/date_helper.rb
  13. +15 −11 actionpack/lib/action_view/helpers/form_helper.rb
  14. +42 −26 actionpack/lib/action_view/helpers/form_options_helper.rb
  15. +7 −4 actionpack/lib/action_view/helpers/form_tag_helper.rb
  16. +3 −3 actionpack/lib/action_view/helpers/text_helper.rb
  17. +1 −1  actionpack/lib/action_view/helpers/translation_helper.rb
  18. +12 −3 actionpack/lib/sprockets/railtie.rb
  19. +1 −1  actionpack/test/abstract/abstract_controller_test.rb
  20. +0 −1  actionpack/test/controller/test_test.rb
  21. +2 −2 actionpack/test/controller/url_for_test.rb
  22. +2 −2 actionpack/test/controller/webservice_test.rb
  23. +7 −0 actionpack/test/dispatch/uploaded_file_test.rb
  24. +2 −2 actionpack/test/template/form_helper_test.rb
  25. +18 −2 actionpack/test/template/form_options_helper_test.rb
  26. +2 −2 actionpack/test/template/form_tag_helper_test.rb
  27. +8 −0 actionpack/test/template/translation_helper_test.rb
  28. +8 −0 actionpack/test/template/url_helper_test.rb
  29. +3 −0  activemodel/CHANGELOG
  30. +1 −1  activemodel/lib/active_model/mass_assignment_security/sanitizer.rb
  31. +3 −4 activemodel/lib/active_model/naming.rb
  32. +2 −3 activemodel/lib/active_model/translation.rb
  33. +0 −1  activemodel/test/cases/attribute_methods_test.rb
  34. +0 −10 activemodel/test/cases/translation_test.rb
  35. +5 −0 activerecord/RUNNING_UNIT_TESTS
  36. +43 −24 activerecord/lib/active_record/associations/collection_association.rb
  37. +10 −11 activerecord/lib/active_record/base.rb
  38. +1 −0  activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
  39. +5 −5 activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
  40. +4 −4 activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
  41. +1 −1  activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
  42. +2 −2 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
  43. +4 −2 activerecord/lib/active_record/migration.rb
  44. +19 −13 activerecord/lib/active_record/nested_attributes.rb
  45. +10 −7 activerecord/lib/active_record/test_case.rb
  46. +63 −0 activerecord/test/cases/associations/has_many_associations_test.rb
  47. +1 −1  activerecord/test/cases/autosave_association_test.rb
  48. +7 −6 activerecord/test/cases/helper.rb
  49. +275 −30 activerecord/test/cases/mass_assignment_security_test.rb
  50. +12 −0 activerecord/test/models/company.rb
  51. +6 −2 activerecord/test/models/person.rb
  52. +8 −4 activerecord/test/support/config.rb
  53. +7 −1 activesupport/CHANGELOG
  54. +1 −1  activesupport/lib/active_support/core_ext/array/wrap.rb
  55. +3 −2 activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
  56. +1 −1  activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
  57. +37 −12 activesupport/lib/active_support/core_ext/string/output_safety.rb
  58. +26 −1 activesupport/lib/active_support/core_ext/time/calculations.rb
  59. +0 −11 activesupport/lib/active_support/dependencies.rb
  60. +12 −1 activesupport/lib/active_support/memoizable.rb
  61. +9 −2 activesupport/test/core_ext/class/attribute_accessor_test.rb
  62. +7 −1 activesupport/test/core_ext/string_ext_test.rb
  63. +20 −0 activesupport/test/core_ext/time_ext_test.rb
  64. +3 −1 activesupport/test/flush_cache_on_private_memoization_test.rb
  65. +32 −6 activesupport/test/memoizable_test.rb
  66. +37 −5 activesupport/test/safe_buffer_test.rb
  67. +2 −0  railties/CHANGELOG
  68. +3 −3 railties/guides/source/action_view_overview.textile
  69. +5 −5 railties/guides/source/active_record_validations_callbacks.textile
  70. +60 −10 railties/guides/source/active_support_core_extensions.textile
  71. +72 −20 railties/guides/source/asset_pipeline.textile
  72. +1 −0  railties/guides/source/caching_with_rails.textile
  73. +74 −116 railties/guides/source/command_line.textile
  74. +30 −15 railties/guides/source/configuring.textile
  75. +2 −2 railties/guides/source/contributing_to_ruby_on_rails.textile
  76. +40 −5 railties/guides/source/getting_started.textile
  77. +6 −6 railties/guides/source/nested_model_forms.textile
  78. +3 −3 railties/guides/source/performance_testing.textile
  79. +4 −6 railties/guides/source/rails_application_templates.textile
  80. +0 −6 railties/lib/rails/application.rb
  81. +1 −1  railties/lib/rails/application/railties.rb
  82. +2 −2 railties/lib/rails/commands/plugin.rb
  83. +10 −2 railties/lib/rails/engine.rb
  84. +12 −6 railties/lib/rails/generators/app_base.rb
  85. +1 −1  railties/lib/rails/generators/generated_attribute.rb
  86. +0 −3  railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
  87. +1 −1  railties/lib/rails/generators/rails/app/templates/config/routes.rb
  88. +7 −7 railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
  89. +14 −0 railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/application.html.erb.tt
  90. +4 −4 railties/lib/rails/railtie.rb
  91. +3 −3 railties/test/application/initializers/i18n_test.rb
  92. +3 −10 railties/test/generators/generated_attribute_test.rb
  93. +9 −3 railties/test/generators/model_generator_test.rb
  94. +17 −13 railties/test/generators/plugin_new_generator_test.rb
  95. +9 −3 railties/test/generators/scaffold_generator_test.rb
View
7 Gemfile
@@ -26,6 +26,9 @@ gem "memcache-client", ">= 1.8.5"
platforms :mri_18 do
gem "system_timer"
+ # ruby-debug requires linecache which depends on require_relative but doesn't explicitly
+ # declare this in its gemspec
+ gem "require_relative"
gem "ruby-debug", ">= 0.10.3"
gem "json"
end
@@ -41,7 +44,7 @@ platforms :ruby do
end
gem "json"
gem "yajl-ruby"
- gem "nokogiri", ">= 1.4.4"
+ gem "nokogiri", ">= 1.4.5"
group :test do
gem "ruby-prof" if RUBY_VERSION < "1.9.3"
@@ -53,7 +56,7 @@ platforms :ruby do
group :db do
gem "pg", ">= 0.11.0"
gem "mysql", ">= 2.8.1"
- gem "mysql2", ">= 0.3.0"
+ gem "mysql2", ">= 0.3.6"
end
end
View
6 README.rdoc
@@ -1,7 +1,7 @@
== Welcome to Rails
Rails is a web-application framework that includes everything needed to create
-database-backed web applications according to the Model-View-Controller pattern.
+database-backed web applications according to the {Model-View-Controller (MVC)}[http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller] pattern.
Understanding the MVC pattern is key to understanding Rails. MVC divides your application
into three layers, each with a specific responsibility.
@@ -18,7 +18,7 @@ you to present the data from database rows as objects and embellish these data o
with business logic methods. Although most Rails models are backed by a database, models
can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as
provided by the ActiveModel module. You can read more about Active Record in its
-{README}[link:files/activerecord/README_rdoc.html].
+{README}[link:blob/master/activerecord/README.rdoc].
The Controller layer is responsible for handling incoming HTTP requests and providing a
suitable response. Usually this means returning HTML, but Rails controllers can also
@@ -29,7 +29,7 @@ In Rails, the Controller and View layers are handled together by Action Pack.
These two layers are bundled in a single package due to their heavy interdependence.
This is unlike the relationship between the Active Record and Action Pack which are
independent. Each of these packages can be used independently outside of Rails. You
-can read more about Action Pack in its {README}[link:files/actionpack/README_rdoc.html].
+can read more about Action Pack in its {README}[link:blob/master/actionpack/README.rdoc].
== Getting Started
View
23 Rakefile
@@ -51,9 +51,28 @@ desc "Generate documentation for the Rails framework"
RDoc::Task.new do |rdoc|
RDOC_MAIN = 'RDOC_MAIN.rdoc'
+ # This is a hack.
+ #
+ # Backslashes are needed to prevent RDoc from autolinking "Rails" to the
+ # documentation of the Rails module. On the other hand, as of this
+ # writing README.rdoc is displayed in the front page of the project in
+ # GitHub, where backslashes are shown and look weird.
+ #
+ # The temporary solution is to have a README.rdoc without backslashes for
+ # GitHub, and gsub it to generate the main page of the API.
+ #
+ # The idea for the future is to have totally different files, since the
+ # API is no longer a generic entry point to Rails and deserves a
+ # dedicated main page specifically thought as an API entry point.
rdoc.before_running_rdoc do
rdoc_main = File.read('README.rdoc')
- rdoc_main.gsub!(/\b(?=Rails)\b/) { '\\' }
+
+ # The ^(?=\S) assertion prevents code blocks from being processed,
+ # since no autolinking happens there and RDoc displays the backslash
+ # otherwise.
+ rdoc_main.gsub!(/^(?=\S).*?\b(?=Rails)\b/) { "#$&\\" }
+ rdoc_main.gsub!(/link:blob\/master\/(\w+)\/README.rdoc/, "link:files/\\1/README_rdoc.html")
+
File.open(RDOC_MAIN, 'w') do |f|
f.write(rdoc_main)
end
@@ -72,7 +91,7 @@ RDoc::Task.new do |rdoc|
rdoc.rdoc_files.include('railties/MIT-LICENSE')
rdoc.rdoc_files.include('railties/README.rdoc')
rdoc.rdoc_files.include('railties/lib/**/*.rb')
- rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/*')
+ rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/**/*.rb')
rdoc.rdoc_files.include('activerecord/README.rdoc')
rdoc.rdoc_files.include('activerecord/CHANGELOG')
View
10 actionmailer/lib/action_mailer/base.rb
@@ -57,7 +57,7 @@ module ActionMailer #:nodoc:
# will accept (any valid Email header including optional fields).
#
# The mail method, if not passed a block, will inspect your views and send all the views with
- # the same name as the method, so the above action would send the +welcome.text.plain.erb+ view
+ # the same name as the method, so the above action would send the +welcome.text.erb+ view
# file as well as the +welcome.text.html.erb+ view file in a +multipart/alternative+ email.
#
# If you want to explicitly render only certain templates, pass a block:
@@ -88,7 +88,7 @@ module ActionMailer #:nodoc:
#
# To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same
# name as the method in your mailer model. For example, in the mailer defined above, the template at
- # <tt>app/views/notifier/signup_notification.text.plain.erb</tt> would be used to generate the email.
+ # <tt>app/views/notifier/welcome.text.erb</tt> would be used to generate the email.
#
# Variables defined in the model are accessible as instance variables in the view.
#
@@ -153,7 +153,7 @@ module ActionMailer #:nodoc:
# by the content type. Each such detected template will be added as separate part to the message.
#
# For example, if the following templates exist:
- # * signup_notification.text.plain.erb
+ # * signup_notification.text.erb
# * signup_notification.text.html.erb
# * signup_notification.text.xml.builder
# * signup_notification.text.yaml.erb
@@ -178,7 +178,7 @@ module ActionMailer #:nodoc:
# end
# end
#
- # Which will (if it had both a <tt>welcome.text.plain.erb</tt> and <tt>welcome.text.html.erb</tt>
+ # Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.text.html.erb</tt>
# template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
# the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
# and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
@@ -365,7 +365,7 @@ def register_observer(observer)
Mail.register_observer(delivery_observer)
end
- # Register an Inteceptor which will be called before mail is sent.
+ # Register an Interceptor which will be called before mail is sent.
# Either a class or a string can be passed in as the Interceptor. If a string is passed in
# it will be <tt>constantize</tt>d.
def register_interceptor(interceptor)
View
49 actionpack/CHANGELOG
@@ -1,5 +1,33 @@
*Rails 3.2.0 (unreleased)*
+* Generate hidden input before select with :multiple option set to true.
+ This is useful when you rely on the fact that when no options is set,
+ the state of select will be sent to rails application. Without hidden field
+ nothing is sent according to HTML spec [Bogdan Gusiev]
+
+* Refactor ActionController::TestCase cookies [Andrew White]
+
+ Assigning cookies for test cases should now use cookies[], e.g:
+
+ cookies[:email] = 'user@example.com'
+ get :index
+ assert_equal 'user@example.com', cookies[:email]
+
+ To clear the cookies, use clear, e.g:
+
+ cookies.clear
+ get :index
+ assert_nil cookies[:email]
+
+ We now no longer write out HTTP_COOKIE and the cookie jar is
+ persistent between requests so if you need to manipulate the environment
+ for your test you need to do it before the cookie jar is created.
+
+
+*Rails 3.1.0 (unreleased)*
+
+* json_escape will now return a SafeBuffer string if it receives SafeBuffer string [tenderlove]
+
* Make sure escape_js returns SafeBuffer string if it receives SafeBuffer string [Prem Sichanugrist]
* Fix escape_js to work correctly with the new SafeBuffer restriction [Paul Gallagher]
@@ -31,27 +59,6 @@
You can read more about this change in http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2e516e7acc96c4fb
-* Refactor ActionController::TestCase cookies [Andrew White]
-
- Assigning cookies for test cases should now use cookies[], e.g:
-
- cookies[:email] = 'user@example.com'
- get :index
- assert_equal 'user@example.com', cookies[:email]
-
- To clear the cookies, use clear, e.g:
-
- cookies.clear
- get :index
- assert_nil cookies[:email]
-
- We now no longer write out HTTP_COOKIE and the cookie jar is
- persistent between requests so if you need to manipulate the environment
- for your test you need to do it before the cookie jar is created.
-
-
-*Rails 3.1.0 (unreleased)*
-
* Added 'ActionView::Helpers::FormHelper.fields_for_with_index', similar to fields_for but allows to have access to the current iteration index [Jorge Bejar]
* Warn if we cannot verify CSRF token authenticity [José Valim]
View
1  actionpack/lib/action_controller/metal/redirecting.rb
@@ -43,6 +43,7 @@ module Redirecting
#
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
# integer, or a symbol representing the downcased, underscored and symbolized description.
+ # Note that the status code must be a 3xx HTTP code, or redirection will not occur.
#
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
View
6 actionpack/lib/action_dispatch/http/headers.rb
@@ -3,9 +3,10 @@
module ActionDispatch
module Http
class Headers < ::Hash
- extend ActiveSupport::Memoizable
+ @@env_cache = Hash.new { |h,k| h[k] = "HTTP_#{k.upcase.gsub(/-/, '_')}" }
def initialize(*args)
+
if args.size == 1 && args[0].is_a?(Hash)
super()
update(args[0])
@@ -25,9 +26,8 @@ def [](header_name)
private
# Converts a HTTP header name to an environment variable name.
def env_name(header_name)
- "HTTP_#{header_name.upcase.gsub(/-/, '_')}"
+ @@env_cache[header_name]
end
- memoize :env_name
end
end
end
View
12 actionpack/lib/action_dispatch/http/upload.rb
@@ -4,7 +4,7 @@ class UploadedFile
attr_accessor :original_filename, :content_type, :tempfile, :headers
def initialize(hash)
- @original_filename = hash[:filename]
+ @original_filename = encode_filename(hash[:filename])
@content_type = hash[:type]
@headers = hash[:head]
@tempfile = hash[:tempfile]
@@ -30,6 +30,16 @@ def rewind
def size
@tempfile.size
end
+
+ private
+ def encode_filename(filename)
+ # Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
+ if "ruby".encoding_aware? && filename
+ filename.force_encoding("UTF-8").encode!
+ else
+ filename
+ end
+ end
end
module Upload
View
2  actionpack/lib/action_dispatch/routing.rb
@@ -161,7 +161,7 @@ module ActionDispatch
# Consider the following route, which you will find commented out at the
# bottom of your generated <tt>config/routes.rb</tt>:
#
- # match ':controller(/:action(/:id(.:format)))'
+ # match ':controller(/:action(/:id))(.:format)'
#
# This route states that it expects requests to consist of a
# <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
View
8 actionpack/lib/action_dispatch/routing/mapper.rb
@@ -87,7 +87,7 @@ def normalize_options!
raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
end
end
- end
+ end
# match "account/overview"
def using_match_shorthand?(path, options)
@@ -1105,9 +1105,9 @@ def resource(*resources, &block)
#
# The +comments+ resource here will have the following routes generated for it:
#
- # post_comments GET /sekret/posts/:post_id/comments(.:format)
- # post_comments POST /sekret/posts/:post_id/comments(.:format)
- # new_post_comment GET /sekret/posts/:post_id/comments/new(.:format)
+ # post_comments GET /posts/:post_id/comments(.:format)
+ # post_comments POST /posts/:post_id/comments(.:format)
+ # new_post_comment GET /posts/:post_id/comments/new(.:format)
# edit_comment GET /sekret/comments/:id/edit(.:format)
# comment GET /sekret/comments/:id(.:format)
# comment PUT /sekret/comments/:id(.:format)
View
2  actionpack/lib/action_view/helpers/controller_helper.rb
@@ -20,4 +20,4 @@ def assign_controller(controller)
end
end
end
-end
+end
View
13 actionpack/lib/action_view/helpers/date_helper.rb
@@ -621,7 +621,6 @@ def time_tag(date_or_time, *args)
end
class DateTimeSelector #:nodoc:
- extend ActiveSupport::Memoizable
include ActionView::Helpers::TagHelper
DEFAULT_PREFIX = 'date'.freeze
@@ -786,11 +785,12 @@ def select_year
# Returns translated month names, but also ensures that a custom month
# name array has a leading nil element.
def month_names
- month_names = @options[:use_month_names] || translated_month_names
- month_names.unshift(nil) if month_names.size < 13
- month_names
+ @month_names ||= begin
+ month_names = @options[:use_month_names] || translated_month_names
+ month_names.unshift(nil) if month_names.size < 13
+ month_names
+ end
end
- memoize :month_names
# Returns translated month names.
# => [nil, "January", "February", "March",
@@ -825,9 +825,8 @@ def month_name(number)
end
def date_order
- @options[:order] || translated_date_order
+ @date_order ||= @options[:order] || translated_date_order
end
- memoize :date_order
def translated_date_order
I18n.translate(:'date.order', :locale => @options[:locale]) || []
View
26 actionpack/lib/action_view/helpers/form_helper.rb
@@ -219,9 +219,9 @@ def convert_to_model(object)
# <% end %>
#
# If you have an object that needs to be represented as a different
- # parameter, like a Client that acts as a Person:
+ # parameter, like a Person that acts as a Client:
#
- # <%= form_for(@post, :as => :client) do |f| %>
+ # <%= form_for(@person, :as => :client) do |f| %>
# ...
# <% end %>
#
@@ -290,7 +290,7 @@ def convert_to_model(object)
#
# Example:
#
- # <%= form(@post) do |f| %>
+ # <%= form_for(@post) do |f| %>
# <% f.fields_for(:comments, :include_id => false) do |cf| %>
# ...
# <% end %>
@@ -517,6 +517,18 @@ def apply_form_for_options!(object_or_array, options) #:nodoc:
# end
# end
#
+ # Note that the <tt>projects_attributes=</tt> writer method is in fact
+ # required for fields_for to correctly identify <tt>:projects</tt> as a
+ # collection, and the correct indices to be set in the form markup.
+ #
+ # When projects is already an association on Person you can use
+ # +accepts_nested_attributes_for+ to define the writer method for you:
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :projects
+ # accepts_nested_attributes_for :projects
+ # end
+ #
# This model can now be used with a nested fields_for. The block given to
# the nested fields_for call will be repeated for each instance in the
# collection:
@@ -568,14 +580,6 @@ def apply_form_for_options!(object_or_array, options) #:nodoc:
# ...
# <% end %>
#
- # When projects is already an association on Person you can use
- # +accepts_nested_attributes_for+ to define the writer method for you:
- #
- # class Person < ActiveRecord::Base
- # has_many :projects
- # accepts_nested_attributes_for :projects
- # end
- #
# If you want to destroy any of the associated models through the
# form, you have to enable it first using the <tt>:allow_destroy</tt>
# option for +accepts_nested_attributes_for+:
View
68 actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -128,6 +128,28 @@ module FormOptionsHelper
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection
# or <tt>:selected => nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option
# tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled.
+ #
+ # ==== Gotcha
+ #
+ # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
+ # web browsers do not send any value to server. Unfortunately this introduces a gotcha:
+ # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
+ # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
+ # any mass-assignment idiom like
+ #
+ # @user.update_attributes(params[:user])
+ #
+ # wouldn't update roles.
+ #
+ # To prevent this the helper generates an auxiliary hidden field before
+ # every multiple select. The hidden field has the same name as multiple select and blank value.
+ #
+ # This way, the client either sends only the hidden field (representing
+ # the deselected multiple select box), or both fields. Since the HTML specification
+ # says key/value pairs have to be sent in the same order they appear in the
+ # form, and parameters extraction gets the last occurrence of any repeated
+ # key in the query string, that works for ordinary forms.
+ #
def select(object, method, choices, options = {}, html_options = {})
InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)
end
@@ -552,43 +574,26 @@ class InstanceTag #:nodoc:
include FormOptionsHelper
def to_select_tag(choices, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- selected_value = options.has_key?(:selected) ? options[:selected] : value
- disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil
- content_tag("select", add_options(options_for_select(choices, :selected => selected_value, :disabled => disabled_value), options, selected_value), html_options)
+ selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
+ select_content_tag(options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]), options, html_options)
end
def to_collection_select_tag(collection, value_method, text_method, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil
- selected_value = options.has_key?(:selected) ? options[:selected] : value
- content_tag(
- "select", add_options(options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => disabled_value), options, value), html_options
+ selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
+ select_content_tag(
+ options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options
)
end
def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- content_tag(
- "select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options
+ select_content_tag(
+ option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, html_options
)
end
def to_time_zone_select_tag(priority_zones, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- content_tag("select",
- add_options(
- time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone),
- options, value
- ), html_options
+ select_content_tag(
+ time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options
)
end
@@ -603,6 +608,17 @@ def add_options(option_tags, options, value = nil)
end
option_tags.html_safe
end
+
+ def select_content_tag(option_tags, options, html_options)
+ html_options = html_options.stringify_keys
+ add_default_name_and_id(html_options)
+ select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
+ if html_options["multiple"]
+ tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
+ else
+ select
+ end
+ end
end
class FormBuilder
View
11 actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -597,6 +597,12 @@ def range_field_tag(name, value = nil, options = {})
number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
end
+ # Creates the hidden UTF8 enforcer tag. Override this method in a helper
+ # to customize the tag.
+ def utf8_enforcer_tag
+ tag(:input, :type => "hidden", :name => "utf8", :value => "&#x2713;".html_safe)
+ end
+
private
def html_options_for_form(url_for_options, options, *parameters_for_url)
options.stringify_keys.tap do |html_options|
@@ -611,9 +617,6 @@ def html_options_for_form(url_for_options, options, *parameters_for_url)
end
def extra_tags_for_form(html_options)
- snowman_tag = tag(:input, :type => "hidden",
- :name => "utf8", :value => "&#x2713;".html_safe)
-
authenticity_token = html_options.delete("authenticity_token")
method = html_options.delete("method").to_s
@@ -629,7 +632,7 @@ def extra_tags_for_form(html_options)
tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
end
- tags = snowman_tag << method_tag
+ tags = utf8_enforcer_tag << method_tag
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
end
View
6 actionpack/lib/action_view/helpers/text_helper.rb
@@ -255,7 +255,7 @@ def word_wrap(text, *args)
# simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false)
# # => "<p><span>I'm allowed!</span> It's true.</p>"
def simple_format(text, html_options={}, options={})
- text = ''.html_safe if text.nil?
+ text = '' if text.nil?
start_tag = tag('p', html_options, true)
text = sanitize(text) unless options[:sanitize] == false
text = text.to_str
@@ -263,7 +263,7 @@ def simple_format(text, html_options={}, options={})
text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
text.insert 0, start_tag
- text = ActiveSupport::SafeBuffer.new(text).safe_concat("</p>")
+ text.html_safe.safe_concat("</p>")
end
# Creates a Cycle object whose _to_s_ method cycles through elements of an
@@ -280,7 +280,7 @@ def simple_format(text, html_options={}, options={})
# @items = [1,2,3,4]
# <table>
# <% @items.each do |item| %>
- # <tr class="<%= cycle("even", "odd") -%>">
+ # <tr class="<%= cycle("odd", "even") -%>">
# <td>item</td>
# </tr>
# <% end %>
View
2  actionpack/lib/action_view/helpers/translation_helper.rb
@@ -5,7 +5,7 @@ module I18n
class ExceptionHandler
include Module.new {
def call(exception, locale, key, options)
- exception.is_a?(MissingTranslation) ? super.html_safe : super
+ exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super
end
}
end
View
15 actionpack/lib/sprockets/railtie.rb
@@ -54,12 +54,21 @@ def asset_environment(app)
env = Sprockets::Environment.new(app.root.to_s)
env.static_root = File.join(app.root.join("public"), assets.prefix)
- env.paths.concat assets.paths
+
+ if env.respond_to?(:append_path)
+ assets.paths.each { |path| env.append_path(path) }
+ else
+ env.paths.concat assets.paths
+ end
env.logger = Rails.logger
- env.js_compressor = expand_js_compressor(assets.js_compressor)
- env.css_compressor = expand_css_compressor(assets.css_compressor)
+ if assets.compress
+ # temporarily hardcode default JS compressor to uglify. Soon, it will work
+ # the same as SCSS, where a default plugin sets the default.
+ env.js_compressor = expand_js_compressor(assets.js_compressor || :uglifier)
+ env.css_compressor = expand_css_compressor(assets.css_compressor)
+ end
env
end
View
2  actionpack/test/abstract/abstract_controller_test.rb
@@ -181,7 +181,7 @@ def index
class TestLayouts < ActiveSupport::TestCase
test "layouts are included" do
controller = Me4.new
- result = controller.process(:index)
+ controller.process(:index)
assert_equal "Me4 Enter : Hello from me4/index.erb : Exit", controller.response_body
end
end
View
1  actionpack/test/controller/test_test.rb
@@ -612,7 +612,6 @@ def test_should_detect_if_cookie_is_deleted
send(method, :test_remote_addr)
assert false, "expected RuntimeError, got nothing"
rescue RuntimeError => error
- assert true
assert_match(%r{@#{variable} is nil}, error.message)
rescue => error
assert false, "expected RuntimeError, got #{error.class}"
View
4 actionpack/test/controller/url_for_test.rb
@@ -293,8 +293,8 @@ def test_multiple_includes_maintain_distinct_options
first_class.default_url_options[:host] = first_host
second_class.default_url_options[:host] = second_host
- assert_equal first_class.default_url_options[:host], first_host
- assert_equal second_class.default_url_options[:host], second_host
+ assert_equal first_host, first_class.default_url_options[:host]
+ assert_equal second_host, second_class.default_url_options[:host]
end
def test_with_stringified_keys
View
4 actionpack/test/controller/webservice_test.rb
@@ -30,7 +30,7 @@ def setup
def test_check_parameters
with_test_route_set do
get "/"
- assert_blank @controller.response.body
+ assert_equal '', @controller.response.body
end
end
@@ -162,7 +162,7 @@ def test_register_and_use_xml_simple
def test_use_xml_ximple_with_empty_request
with_test_route_set do
assert_nothing_raised { post "/", "", {'CONTENT_TYPE' => 'application/xml'} }
- assert_blank @controller.response.body
+ assert_equal '', @controller.response.body
end
end
View
7 actionpack/test/dispatch/uploaded_file_test.rb
@@ -12,6 +12,13 @@ def test_original_filename
uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
assert_equal 'foo', uf.original_filename
end
+
+ if "ruby".encoding_aware?
+ def test_filename_should_be_in_utf_8
+ uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
+ assert_equal "UTF-8", uf.original_filename.encoding.to_s
+ end
+ end
def test_content_type
uf = Http::UploadedFile.new(:type => 'foo', :tempfile => Object.new)
View
4 actionpack/test/template/form_helper_test.rb
@@ -1890,7 +1890,7 @@ def test_form_for_with_labelled_builder
assert_dom_equal expected, output_buffer
end
- def snowman(method = nil)
+ def hidden_fields(method = nil)
txt = %{<div style="margin:0;padding:0;display:inline">}
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
if method && !method.to_s.in?(['get', 'post'])
@@ -1918,7 +1918,7 @@ def whole_form(action = "/", id = nil, html_class = nil, options = nil)
method = options
end
- form_text(action, id, html_class, remote, multipart, method) + snowman(method) + contents + "</form>"
+ form_text(action, id, html_class, remote, multipart, method) + hidden_fields(method) + contents + "</form>"
end
def test_default_form_builder
View
20 actionpack/test/template/form_options_helper_test.rb
@@ -457,6 +457,22 @@ def test_select_under_fields_for_with_string_and_given_prompt
)
end
+ def test_select_with_multiple_to_add_hidden_input
+ output_buffer = select(:post, :category, "", {}, :multiple => true)
+ assert_dom_equal(
+ "<input type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>",
+ output_buffer
+ )
+ end
+
+ def test_select_with_multiple_and_disabled_to_add_disabled_hidden_input
+ output_buffer = select(:post, :category, "", {}, :multiple => true, :disabled => true)
+ assert_dom_equal(
+ "<input disabled=\"disabled\"type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" disabled=\"disabled\" id=\"post_category\" name=\"post[category][]\"></select>",
+ output_buffer
+ )
+ end
+
def test_select_with_blank
@post = Post.new
@post.category = "<mus>"
@@ -649,11 +665,11 @@ def test_collection_select_with_blank_as_string_and_style
)
end
- def test_collection_select_with_multiple_option_appends_array_brackets
+ def test_collection_select_with_multiple_option_appends_array_brackets_and_hidden_input
@post = Post.new
@post.author_name = "Babe"
- expected = "<select id=\"post_author_name\" name=\"post[author_name][]\" multiple=\"multiple\"><option value=\"\"></option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>"
+ expected = "<input type=\"hidden\" name=\"post[author_name][]\" value=\"\"/><select id=\"post_author_name\" name=\"post[author_name][]\" multiple=\"multiple\"><option value=\"\"></option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>"
# Should suffix default name with [].
assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { :include_blank => true }, :multiple => true)
View
4 actionpack/test/template/form_tag_helper_test.rb
@@ -9,7 +9,7 @@ def setup
@controller = BasicController.new
end
- def snowman(options = {})
+ def hidden_fields(options = {})
method = options[:method]
txt = %{<div style="margin:0;padding:0;display:inline">}
@@ -34,7 +34,7 @@ def form_text(action = "http://www.example.com", options = {})
end
def whole_form(action = "http://www.example.com", options = {})
- out = form_text(action, options) + snowman(options)
+ out = form_text(action, options) + hidden_fields(options)
if block_given?
out << yield << "</form>"
View
8 actionpack/test/template/translation_helper_test.rb
@@ -38,11 +38,19 @@ def test_delegates_localize_to_i18n
def test_returns_missing_translation_message_wrapped_into_span
expected = '<span class="translation_missing" title="translation missing: en.translations.missing">Missing</span>'
assert_equal expected, translate(:"translations.missing")
+ assert_equal true, translate(:"translations.missing").html_safe?
end
def test_returns_missing_translation_message_using_nil_as_rescue_format
expected = 'translation missing: en.translations.missing'
assert_equal expected, translate(:"translations.missing", :rescue_format => nil)
+ assert_equal false, translate(:"translations.missing", :rescue_format => nil).html_safe?
+ end
+
+ def test_i18n_translate_defaults_to_nil_rescue_format
+ expected = 'translation missing: en.translations.missing'
+ assert_equal expected, I18n.translate(:"translations.missing")
+ assert_equal false, I18n.translate(:"translations.missing").html_safe?
end
def test_translation_returning_an_array
View
8 actionpack/test/template/url_helper_test.rb
@@ -15,6 +15,7 @@ class UrlHelperTest < ActiveSupport::TestCase
routes.draw do
match "/" => "foo#bar"
match "/other" => "foo#other"
+ match "/article/:id" => "foo#article", :as => :article
end
include routes.url_helpers
@@ -264,6 +265,13 @@ def test_link_tag_using_block_in_erb
assert_equal '<a href="/">Example site</a>', out
end
+ def test_link_tag_with_html_safe_string
+ assert_dom_equal(
+ "<a href=\"/article/Gerd_M%C3%BCller\">Gerd Müller</a>",
+ link_to("Gerd Müller", article_path("Gerd_Müller".html_safe))
+ )
+ end
+
def test_link_to_unless
assert_equal "Showing", link_to_unless(true, "Showing", url_hash)
View
3  activemodel/CHANGELOG
@@ -2,6 +2,9 @@
*Rails 3.1.0 (unreleased)*
+* Alternate I18n namespace lookup is no longer supported.
+ Instead of "activerecord.models.admins.post", do "activerecord.models.admins/post" instead [José Valim]
+
* attr_accessible and friends now accepts :as as option to specify a role [Josh Kalderimis]
* Add support for proc or lambda as an option for InclusionValidator,
View
2  activemodel/lib/active_model/mass_assignment_security/sanitizer.rb
@@ -19,7 +19,7 @@ def debug_protected_attribute_removal(attributes, sanitized_attributes)
removed_keys = attributes.keys - sanitized_attributes.keys
process_removed_attributes(removed_keys) if removed_keys.any?
end
-
+
def process_removed_attributes(attrs)
raise NotImplementedError, "#process_removed_attributes(attrs) suppose to be overwritten"
end
View
7 activemodel/lib/active_model/naming.rb
@@ -21,7 +21,7 @@ def initialize(klass, namespace = nil, name = nil)
@partial_path = "#{@collection}/#{@element}".freeze
@param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze
@route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze
- @i18n_key = self.underscore.tr('/', '.').to_sym
+ @i18n_key = self.underscore.to_sym
end
# Transform the model name into a more humane format, using I18n. By default,
@@ -35,9 +35,8 @@ def human(options={})
@klass.respond_to?(:i18n_scope)
defaults = @klass.lookup_ancestors.map do |klass|
- [klass.model_name.i18n_key,
- klass.model_name.i18n_key.to_s.tr('.', '/').to_sym]
- end.flatten
+ klass.model_name.i18n_key
+ end
defaults << options[:default] if options[:default]
defaults << @human
View
5 activemodel/lib/active_model/translation.rb
@@ -44,9 +44,8 @@ def lookup_ancestors
# Specify +options+ with additional translating options.
def human_attribute_name(attribute, options = {})
defaults = lookup_ancestors.map do |klass|
- [:"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}",
- :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key.to_s.tr('.', '/')}.#{attribute}"]
- end.flatten
+ :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
+ end
defaults << :"attributes.#{attribute}"
defaults << options.delete(:default) if options[:default]
View
1  activemodel/test/cases/attribute_methods_test.rb
@@ -78,7 +78,6 @@ class AttributeMethodsTest < ActiveModel::TestCase
test '#define_attribute_method generates attribute method with invalid identifier characters' do
ModelWithWeirdNamesAttributes.define_attribute_method(:'a?b')
- ModelWithWeirdNamesAttributes.define_attribute_method(:'a?b')
assert_respond_to ModelWithWeirdNamesAttributes.new, :'a?b'
assert_equal "value of a?b", ModelWithWeirdNamesAttributes.new.send('a?b')
View
10 activemodel/test/cases/translation_test.rb
@@ -76,15 +76,5 @@ def test_human_does_not_modify_options
Person.model_name.human(options)
assert_equal({:default => 'person model'}, options)
end
-
- def test_alternate_namespaced_model_attribute_translation
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:gender => {:attribute => 'person gender attribute'}}}}
- assert_equal 'person gender attribute', Person::Gender.human_attribute_name('attribute')
- end
-
- def test_alternate_namespaced_model_translation
- I18n.backend.store_translations 'en', :activemodel => {:models => {:person => {:gender => 'person gender model'}}}
- assert_equal 'person gender model', Person::Gender.model_name.human
- end
end
View
5 activerecord/RUNNING_UNIT_TESTS
@@ -32,3 +32,8 @@ By default the tests run with the Identity Map turned off. But all tests should
not the identity map is on or off. You can turn it on using the IM env variable:
$ IM=true ruby -Itest test/case/base_test.rb
+
+== Config file
+
+By default, the config file is expected to be at the path test/config.yml. You can specify a
+custom location with the ARCONFIG environment variable.
View
67 activerecord/lib/active_record/associations/collection_association.rb
@@ -114,19 +114,13 @@ def create!(attributes = {}, options = {}, &block)
# Add +records+ to this association. Returns +self+ so method calls may be chained.
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
def concat(*records)
- result = true
load_target if owner.new_record?
- transaction do
- records.flatten.each do |record|
- raise_on_type_mismatch(record)
- add_to_target(record) do |r|
- result &&= insert_record(record) unless owner.new_record?
- end
- end
+ if owner.new_record?
+ concat_records(records)
+ else
+ transaction { concat_records(records) }
end
-
- result && records
end
# Starts a transaction in the association class's database connection.
@@ -295,14 +289,10 @@ def replace(other_array)
other_array.each { |val| raise_on_type_mismatch(val) }
original_target = load_target.dup
- transaction do
- delete(target - other_array)
-
- unless concat(other_array - target)
- @target = original_target
- raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
- "new records could not be saved."
- end
+ if owner.new_record?
+ replace_records(other_array, original_target)
+ else
+ transaction { replace_records(other_array, original_target) }
end
end
@@ -444,14 +434,20 @@ def delete_or_destroy(records, method)
records.each { |record| raise_on_type_mismatch(record) }
existing_records = records.reject { |r| r.new_record? }
- transaction do
- records.each { |record| callback(:before_remove, record) }
+ if existing_records.empty?
+ remove_records(existing_records, records, method)
+ else
+ transaction { remove_records(existing_records, records, method) }
+ end
+ end
- delete_records(existing_records, method) if existing_records.any?
- records.each { |record| target.delete(record) }
+ def remove_records(existing_records, records, method)
+ records.each { |record| callback(:before_remove, record) }
- records.each { |record| callback(:after_remove, record) }
- end
+ delete_records(existing_records, method) if existing_records.any?
+ records.each { |record| target.delete(record) }
+
+ records.each { |record| callback(:after_remove, record) }
end
# Delete the given records from the association, using one of the methods :destroy,
@@ -460,6 +456,29 @@ def delete_records(records, method)
raise NotImplementedError
end
+ def replace_records(new_target, original_target)
+ delete(target - new_target)
+
+ unless concat(new_target - target)
+ @target = original_target
+ raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
+ "new records could not be saved."
+ end
+ end
+
+ def concat_records(records)
+ result = true
+
+ records.flatten.each do |record|
+ raise_on_type_mismatch(record)
+ add_to_target(record) do |r|
+ result &&= insert_record(record) unless owner.new_record?
+ end
+ end
+
+ result && records
+ end
+
def callback(method, record)
callbacks_for(method).each do |callback|
case callback
View
21 activerecord/lib/active_record/base.rb
@@ -428,10 +428,6 @@ class Base
class_attribute :default_scopes, :instance_writer => false
self.default_scopes = []
- # Boolean flag to prevent infinite recursion when evaluating default scopes
- class_attribute :apply_default_scope, :instance_writer => false
- self.apply_default_scope = true
-
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
class_attribute :serialized_attributes
@@ -1265,11 +1261,11 @@ def default_scope(scope = {})
self.default_scopes = default_scopes + [scope]
end
- # The apply_default_scope flag is used to prevent an infinite recursion situation where
+ # The @ignore_default_scope flag is used to prevent an infinite recursion situation where
# a default scope references a scope which has a default scope which references a scope...
def build_default_scope #:nodoc:
- return unless apply_default_scope
- self.apply_default_scope = false
+ return if defined?(@ignore_default_scope) && @ignore_default_scope
+ @ignore_default_scope = true
if method(:default_scope).owner != Base.singleton_class
default_scope
@@ -1285,7 +1281,7 @@ def build_default_scope #:nodoc:
end
end
ensure
- self.apply_default_scope = true
+ @ignore_default_scope = false
end
# Returns the class type of the record using the current module as a prefix. So descendants of
@@ -1717,10 +1713,13 @@ def assign_attributes(new_attributes, options = {})
attributes.each do |k, v|
if k.include?("(")
multi_parameter_attributes << [ k, v ]
- elsif respond_to?("#{k}=")
- send("#{k}=", v)
else
- raise(UnknownAttributeError, "unknown attribute: #{k}")
+ method_name = "#{k}="
+ if respond_to?(method_name)
+ method(method_name).arity == -2 ? send(method_name, v, options) : send(method_name, v)
+ else
+ raise(UnknownAttributeError, "unknown attribute: #{k}")
+ end
end
end
View
1  activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -119,6 +119,7 @@ def table_exists?(name)
with_connection do |conn|
conn.tables.each { |table| @tables[table] = true }
+ @tables[name] = true if !@tables.key?(name) && conn.table_exists?(name)
end
@tables.key? name
View
10 activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -386,13 +386,13 @@ def remove(*column_names)
# Removes the given index from the table.
#
# ===== Examples
- # ====== Remove the suppliers_name_index in the suppliers table
- # t.remove_index :name
- # ====== Remove the index named accounts_branch_id_index in the accounts table
+ # ====== Remove the index_table_name_on_column in the table_name table
+ # t.remove_index :column
+ # ====== Remove the index named index_table_name_on_branch_id in the table_name table
# t.remove_index :column => :branch_id
- # ====== Remove the index named accounts_branch_id_party_id_index in the accounts table
+ # ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
# t.remove_index :column => [:branch_id, :party_id]
- # ====== Remove the index named by_branch_party in the accounts table
+ # ====== Remove the index named by_branch_party in the table_name table
# t.remove_index :name => :by_branch_party
def remove_index(options = {})
@base.remove_index(@table_name, options)
View
8 activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -346,11 +346,11 @@ def add_index(table_name, column_name, options = {})
# Remove the given index from the table.
#
- # Remove the suppliers_name_index in the suppliers table.
- # remove_index :suppliers, :name
- # Remove the index named accounts_branch_id_index in the accounts table.
+ # Remove the index_accounts_on_column in the accounts table.
+ # remove_index :accounts, :column
+ # Remove the index named index_accounts_on_branch_id in the accounts table.
# remove_index :accounts, :column => :branch_id
- # Remove the index named accounts_branch_id_party_id_index in the accounts table.
+ # Remove the index named index_accounts_on_branch_id_and_party_id in the accounts table.
# remove_index :accounts, :column => [:branch_id, :party_id]
# Remove the index named by_branch_party in the accounts table.
# remove_index :accounts, :name => :by_branch_party
View
2  activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -1,6 +1,6 @@
# encoding: utf-8
-gem 'mysql2', '~> 0.3.0'
+gem 'mysql2', '~> 0.3.6'
require 'mysql2'
module ActiveRecord
View
4 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -811,7 +811,7 @@ def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
if pk && sequence
quoted_sequence = quote_table_name(sequence)
-
+
select_value <<-end_sql, 'Reset sequence'
SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
end_sql
@@ -840,7 +840,7 @@ def pk_and_sequence_for(table) #:nodoc:
else
sequence = result.second+'.'+result.last
end
-
+
[result.first, sequence]
rescue
nil
View
6 activerecord/lib/active_record/migration.rb
@@ -116,8 +116,10 @@ def initialize(name)
# with the name of the column. Other options include
# <tt>:name</tt> and <tt>:unique</tt> (e.g.
# <tt>{ :name => "users_name_index", :unique => true }</tt>).
- # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified
- # by +index_name+.
+ # * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
+ # specified by +column_name+.
+ # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
+ # specified by +index_name+.
#
# == Irreversible transformations
#
View
32 activerecord/lib/active_record/nested_attributes.rb
@@ -276,15 +276,15 @@ def accepts_nested_attributes_for(*attr_names)
type = (reflection.collection? ? :collection : :one_to_one)
- # def pirate_attributes=(attributes)
- # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
+ # def pirate_attributes=(attributes, assignment_opts = {})
+ # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, assignment_opts)
# end
class_eval <<-eoruby, __FILE__, __LINE__ + 1
if method_defined?(:#{association_name}_attributes=)
remove_method(:#{association_name}_attributes=)
end
- def #{association_name}_attributes=(attributes)
- assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
+ def #{association_name}_attributes=(attributes, assignment_opts = {})
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, assignment_opts)
end
eoruby
else
@@ -319,21 +319,21 @@ def _destroy
# If the given attributes include a matching <tt>:id</tt> attribute, or
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
# then the existing record will be marked for destruction.
- def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
+ def assign_nested_attributes_for_one_to_one_association(association_name, attributes, assignment_opts = {})
options = self.nested_attributes_options[association_name]
attributes = attributes.with_indifferent_access
if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) &&
(options[:update_only] || record.id.to_s == attributes['id'].to_s)
- assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
+ assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy], assignment_opts) unless call_reject_if(association_name, attributes)
- elsif attributes['id'].present?
+ elsif attributes['id'].present? && !assignment_opts[:without_protection]
raise_nested_attributes_record_not_found(association_name, attributes['id'])
elsif !reject_new_record?(association_name, attributes)
method = "build_#{association_name}"
if respond_to?(method)
- send(method, attributes.except(*UNASSIGNABLE_KEYS))
+ send(method, attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
else
raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
end
@@ -367,7 +367,7 @@ def assign_nested_attributes_for_one_to_one_association(association_name, attrib
# { :name => 'John' },
# { :id => '2', :_destroy => true }
# ])
- def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {})
options = self.nested_attributes_options[association_name]
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
@@ -401,7 +401,7 @@ def assign_nested_attributes_for_collection_association(association_name, attrib
if attributes['id'].blank?
unless reject_new_record?(association_name, attributes)
- association.build(attributes.except(*UNASSIGNABLE_KEYS))
+ association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
end
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
unless association.loaded? || call_reject_if(association_name, attributes)
@@ -418,8 +418,10 @@ def assign_nested_attributes_for_collection_association(association_name, attrib
end
if !call_reject_if(association_name, attributes)
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy], assignment_opts)
end
+ elsif assignment_opts[:without_protection]
+ association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
else
raise_nested_attributes_record_not_found(association_name, attributes['id'])
end
@@ -428,8 +430,8 @@ def assign_nested_attributes_for_collection_association(association_name, attrib
# Updates a record with the +attributes+ or marks it for destruction if
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
- def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
- record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
+ def assign_to_or_mark_for_destruction(record, attributes, allow_destroy, assignment_opts)
+ record.assign_attributes(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
end
@@ -458,5 +460,9 @@ def call_reject_if(association_name, attributes)
def raise_nested_attributes_record_not_found(association_name, record_id)
raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
end
+
+ def unassignable_keys(assignment_opts)
+ assignment_opts[:without_protection] ? UNASSIGNABLE_KEYS - %w[id] : UNASSIGNABLE_KEYS
+ end
end
end
View
17 activerecord/lib/active_record/test_case.rb
@@ -31,27 +31,30 @@ def assert_date_from_db(expected, actual, message = nil)
end
def assert_sql(*patterns_to_match)
- $queries_executed = []
+ ActiveRecord::SQLCounter.log = []
yield
- $queries_executed
+ ActiveRecord::SQLCounter.log
ensure
failed_patterns = []
patterns_to_match.each do |pattern|
- failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
+ failed_patterns << pattern unless ActiveRecord::SQLCounter.log.any?{ |sql| pattern === sql }
end
- assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
end
def assert_queries(num = 1)
- $queries_executed = []
+ ActiveRecord::SQLCounter.log = []
yield
ensure
- %w{ BEGIN COMMIT }.each { |x| $queries_executed.delete(x) }
- assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
+ assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
end
def assert_no_queries(&block)
+ prev_ignored_sql = ActiveRecord::SQLCounter.ignored_sql
+ ActiveRecord::SQLCounter.ignored_sql = []
assert_queries(0, &block)
+ ensure
+ ActiveRecord::SQLCounter.ignored_sql = prev_ignored_sql
end
def with_kcode(kcode)
View
63 activerecord/test/cases/associations/has_many_associations_test.rb
@@ -537,6 +537,25 @@ def test_adding_a_collection
assert_equal 3, companies(:first_firm).clients_of_firm(true).size
end
+ def test_transactions_when_adding_to_persisted
+ good = Client.new(:name => "Good")
+ bad = Client.new(:name => "Bad", :raise_on_save => true)
+
+ begin
+ companies(:first_firm).clients_of_firm.concat(good, bad)
+ rescue Client::RaisedOnSave
+ end
+
+ assert !companies(:first_firm).clients_of_firm(true).include?(good)
+ end
+
+ def test_transactions_when_adding_to_new_record
+ assert_no_queries do
+ firm = Firm.new
+ firm.clients_of_firm.concat(Client.new("name" => "Natural Company"))
+ end
+ end
+
def test_new_aliased_to_build
company = companies(:first_firm)
new_client = assert_no_queries { company.clients_of_firm.new("name" => "Another Client") }
@@ -778,6 +797,29 @@ def test_delete_all_with_not_yet_loaded_association_collection
assert_equal 0, companies(:first_firm).clients_of_firm(true).size
end
+ def test_transaction_when_deleting_persisted
+ good = Client.new(:name => "Good")
+ bad = Client.new(:name => "Bad", :raise_on_destroy => true)
+
+ companies(:first_firm).clients_of_firm = [good, bad]
+
+ begin
+ companies(:first_firm).clients_of_firm.destroy(good, bad)
+ rescue Client::RaisedOnDestroy
+ end
+
+ assert_equal [good, bad], companies(:first_firm).clients_of_firm(true)
+ end
+
+ def test_transaction_when_deleting_new_record
+ assert_no_queries do
+ firm = Firm.new
+ client = Client.new("name" => "New Client")
+ firm.clients_of_firm << client
+ firm.clients_of_firm.destroy(client)
+ end
+ end
+
def test_clearing_an_association_collection
firm = companies(:first_firm)
client_id = firm.clients_of_firm.first.id
@@ -1111,6 +1153,27 @@ def test_replace_failure
assert_equal orig_accounts, firm.accounts
end
+ def test_transactions_when_replacing_on_persisted
+ good = Client.new(:name => "Good")
+ bad = Client.new(:name => "Bad", :raise_on_save => true)
+
+ companies(:first_firm).clients_of_firm = [good]
+
+ begin
+ companies(:first_firm).clients_of_firm = [bad]
+ rescue Client::RaisedOnSave
+ end
+
+ assert_equal [good], companies(:first_firm).clients_of_firm(true)
+ end
+
+ def test_transactions_when_replacing_on_new_record
+ assert_no_queries do
+ firm = Firm.new
+ firm.clients_of_firm = [Client.new("name" => "New Client")]
+ end
+ end
+
def test_get_ids
assert_equal [companies(:first_client).id, companies(:second_client).id], companies(:first_firm).client_ids
end
View
2  activerecord/test/cases/autosave_association_test.rb
@@ -837,7 +837,7 @@ def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_savi
@pirate.parrots.each { |parrot| parrot.mark_for_destruction }
assert @pirate.save
- assert_no_queries do
+ assert_queries(0) do
assert @pirate.save
end
end
View
13 activerecord/test/cases/helper.rb
@@ -58,15 +58,15 @@ def with_active_record_default_timezone(zone)
module ActiveRecord
class SQLCounter
- IGNORED_SQL = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/]
+ cattr_accessor :ignored_sql
+ self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
# FIXME: this needs to be refactored so specific database can add their own
# ignored SQL. This ignored SQL is for Oracle.
- IGNORED_SQL.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
+ ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
- def initialize
- $queries_executed = []
- end
+ cattr_accessor :log
+ self.log = []
def call(name, start, finish, message_id, values)
sql = values[:sql]
@@ -74,10 +74,11 @@ def call(name, start, finish, message_id, values)
# FIXME: this seems bad. we should probably have a better way to indicate
# the query was cached
unless 'CACHE' == values[:name]
- $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
+ self.class.log << sql unless self.class.ignored_sql.any? { |r| sql =~ r }
end
end
end
+
ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
end
View
305 activerecord/test/cases/mass_assignment_security_test.rb
@@ -384,81 +384,81 @@ class MassAssignmentSecurityBelongsToRelationsTest < ActiveRecord::TestCase
# build
- def test_has_one_build_with_attr_protected_attributes
+ def test_belongs_to_build_with_attr_protected_attributes
best_friend = @person.build_best_friend_of(attributes_hash)
assert_default_attributes(best_friend)
end
- def test_has_one_build_with_attr_accessible_attributes
+ def test_belongs_to_build_with_attr_accessible_attributes
best_friend = @person.build_best_friend_of(attributes_hash)
assert_default_attributes(best_friend)
end
- def test_has_one_build_with_admin_role_with_attr_protected_attributes
+ def test_belongs_to_build_with_admin_role_with_attr_protected_attributes
best_friend = @person.build_best_friend_of(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
- def test_has_one_build_with_admin_role_with_attr_accessible_attributes
+ def test_belongs_to_build_with_admin_role_with_attr_accessible_attributes
best_friend = @person.build_best_friend_of(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
- def test_has_one_build_without_protection
+ def test_belongs_to_build_without_protection
best_friend = @person.build_best_friend_of(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create
- def test_has_one_create_with_attr_protected_attributes
+ def test_belongs_to_create_with_attr_protected_attributes
best_friend = @person.create_best_friend_of(attributes_hash)
assert_default_attributes(best_friend, true)
end
- def test_has_one_create_with_attr_accessible_attributes
+ def test_belongs_to_create_with_attr_accessible_attributes
best_friend = @person.create_best_friend_of(attributes_hash)
assert_default_attributes(best_friend, true)
end
- def test_has_one_create_with_admin_role_with_attr_protected_attributes
+ def test_belongs_to_create_with_admin_role_with_attr_protected_attributes
best_friend = @person.create_best_friend_of(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
- def test_has_one_create_with_admin_role_with_attr_accessible_attributes
+ def test_belongs_to_create_with_admin_role_with_attr_accessible_attributes
best_friend = @person.create_best_friend_of(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
- def test_has_one_create_without_protection
+ def test_belongs_to_create_without_protection
best_friend = @person.create_best_friend_of(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create!
- def test_has_one_create_with_bang_with_attr_protected_attributes
+ def test_belongs_to_create_with_bang_with_attr_protected_attributes
best_friend = @person.create_best_friend!(attributes_hash)
assert_default_attributes(best_friend, true)
end
- def test_has_one_create_with_bang_with_attr_accessible_attributes
+ def test_belongs_to_create_with_bang_with_attr_accessible_attributes
best_friend = @person.create_best_friend!(attributes_hash)
assert_default_attributes(best_friend, true)
end
- def test_has_one_create_with_bang_with_admin_role_with_attr_protected_attributes
+ def test_belongs_to_create_with_bang_with_admin_role_with_attr_protected_attributes
best_friend = @person.create_best_friend!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
- def test_has_one_create_with_bang_with_admin_role_with_attr_accessible_attributes
+ def test_belongs_to_create_with_bang_with_admin_role_with_attr_accessible_attributes
best_friend = @person.create_best_friend!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
- def test_has_one_create_with_bang_without_protection
+ def test_belongs_to_create_with_bang_without_protection
best_friend = @person.create_best_friend!(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
@@ -472,83 +472,328 @@ class MassAssignmentSecurityHasManyRelationsTest < ActiveRecord::TestCase
# build
- def test_has_one_build_with_attr_protected_attributes
+ def test_has_many_build_with_attr_protected_attributes
best_friend = @person.best_friends.build(attributes_hash)
assert_default_attributes(best_friend)
end
- def test_has_one_build_with_attr_accessible_attributes
+ def test_has_many_build_with_attr_accessible_attributes
best_friend = @person.best_friends.build(attributes_hash)
assert_default_attributes(best_friend)
end
- def test_has_one_build_with_admin_role_with_attr_protected_attributes
+ def test_has_many_build_with_admin_role_with_attr_protected_attributes
best_friend = @person.best_friends.build(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
- def test_has_one_build_with_admin_role_with_attr_accessible_attributes
+ def test_has_many_build_with_admin_role_with_attr_accessible_attributes
best_friend = @person.best_friends.build(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
- def test_has_one_build_without_protection
+ def test_has_many_build_without_protection
best_friend = @person.best_friends.build(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create
- def test_has_one_create_with_attr_protected_attributes
+ def test_has_many_create_with_attr_protected_attributes
best_friend = @person.best_friends.create(attributes_hash)
assert_default_attributes(best_friend, true)
end
- def test_has_one_create_with_attr_accessible_attributes
+ def test_has_many_create_with_attr_accessible_attributes
best_friend = @person.best_friends.create(attributes_hash)
assert_default_attributes(best_friend, true)
end
- def test_has_one_create_with_admin_role_with_attr_protected_attributes
+ def test_has_many_create_with_admin_role_with_attr_protected_attributes
best_friend = @person.best_friends.create(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
- def test_has_one_create_with_admin_role_with_attr_accessible_attributes
+ def test_has_many_create_with_admin_role_with_attr_accessible_attributes
best_friend = @person.best_friends.create(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
- def test_has_one_create_without_protection
+ def test_has_many_create_without_protection
best_friend = @person.best_friends.create(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create!
- def test_has_one_create_with_bang_with_attr_protected_attributes
+ def test_has_many_create_with_bang_with_attr_protected_attributes
best_friend = @person.best_friends.create!(attributes_hash)
assert_default_attributes(best_friend, true)
end
- def test_has_one_create_with_bang_with_attr_accessible_attributes
+ def test_has_many_create_with_bang_with_attr_accessible_attributes
best_friend = @person.best_friends.create!(attributes_hash)
assert_default_attributes(best_friend, true)
end
- def test_has_one_create_with_bang_with_admin_role_with_attr_protected_attributes
+ def test_has_many_create_with_bang_with_admin_role_with_attr_protected_attributes
best_friend = @person.best_friends.create!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
- def test_has_one_create_with_bang_with_admin_role_with_attr_accessible_attributes
+ def test_has_many_create_with_bang_with_admin_role_with_attr_accessible_attributes
best_friend = @person.best_friends.create!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
- def test_has_one_create_with_bang_without_protection
+ def test_has_many_create_with_bang_without_protection
best_friend = @person.best_friends.create!(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
end
+
+
+class MassAssignmentSecurityNestedAttributesTest < ActiveRecord::TestCase
+ include MassAssignmentTestHelpers
+
+ def nested_attributes_hash(association, collection = false, except = [:id])
+ if collection
+ { :first_name => 'David' }.merge(:"#{association}_attributes" => [attributes_hash.except(*except)])
+ else
+ { :first_name => 'David' }.merge(:"#{association}_attributes" => attributes_hash.except(*except))
+ end
+ end
+
+ # build
+
+ def test_has_one_new_with_attr_protected_attributes
+ person = LoosePerson.new(nested_attributes_hash(:best_friend))
+ assert_default_attributes(person.best_friend)
+ end
+
+ def test_has_one_new_with_attr_accessible_attributes
+ person = TightPerson.new(nested_attributes_hash(:best_friend))
+ assert_default_attributes(person.best_friend)
+ end
+
+ def test_has_one_new_with_admin_role_with_attr_protected_attributes
+ person = LoosePerson.new(nested_attributes_hash(:best_friend), :as => :admin)
+ assert_admin_attributes(person.best_friend)
+ end
+
+ def test_has_one_new_with_admin_role_with_attr_accessible_attributes
+ person = TightPerson.new(nested_attributes_hash(:best_friend), :as => :admin)
+ assert_admin_attributes(person.best_friend)
+ end
+
+ def test_has_one_new_without_protection
+ person = LoosePerson.new(nested_attributes_hash(:best_friend, false, nil), :without_protection => true)
+ assert_all_attributes(person.best_friend)
+ end
+
+ def test_belongs_to_new_with_attr_protected_attributes
+ person = LoosePerson.new(nested_attributes_hash(:best_friend_of))
+ assert_default_attributes(person.best_friend_of)
+ end
+
+ def test_belongs_to_new_with_attr_accessible_attributes
+ person = TightPerson.new(nested_attributes_hash(:best_friend_of))
+ assert_default_attributes(person.best_friend_of)
+ end
+
+ def test_belongs_to_new_with_admin_role_with_attr_protected_attributes
+ person = LoosePerson.new(nested_attributes_hash(:best_friend_of), :as => :admin)
+ assert_admin_attributes(person.best_friend_of)
+ end
+
+ def test_belongs_to_new_with_admin_role_with_attr_accessible_attributes
+ person = TightPerson.new(nested_attributes_hash(:best_friend_of), :as => :admin)
+ assert_admin_attributes(person.best_friend_of)
+ end
+
+ def test_belongs_to_new_without_protection
+ person = LoosePerson.new(nested_attributes_hash(:best_friend_of, false, nil), :without_protection => true)
+ assert_all_attributes(person.best_friend_of)
+ end
+
+ def test_has_many_new_with_attr_protected_attributes
+ person = LoosePerson.new(nested_attributes_hash(:best_friends, true))
+ assert_default_attributes(person.best_friends.first)
+ end
+
+ def test_has_many_new_with_attr_accessible_attributes
+ person = TightPerson.new(nested_attributes_hash(:best_friends, true))
+ assert_default_attributes(person.best_friends.first)
+ end
+
+ def test_has_many_new_with_admin_role_with_attr_protected_attributes
+ person = LoosePerson.new(nested_attributes_hash(:best_friends, true), :as => :admin)
+ assert_admin_attributes(person.best_friends.first)
+ end
+
+ def test_has_many_new_with_admin_role_with_attr_accessible_attributes
+ person = TightPerson.new(nested_attributes_hash(:best_friends, true), :as => :admin)
+ assert_admin_attributes(person.best_friends.first)
+ end
+
+ def test_has_many_new_without_protection
+ person = LoosePerson.new(nested_attributes_hash(:best_friends, true, nil), :without_protection => true)
+ assert_all_attributes(person.best_friends.first)
+ end
+
+ # create
+
+ def test_has_one_create_with_attr_protected_attributes
+ person = LoosePerson.create(nested_attributes_hash(:best_friend))
+ assert_default_attributes(person.best_friend, true)
+ end
+
+ def test_has_one_create_with_attr_accessible_attributes
+ person = TightPerson.create(nested_attributes_hash(:best_friend))
+ assert_default_attributes(person.best_friend, true)
+ end
+
+ def test_has_one_create_with_admin_role_with_attr_protected_attributes
+ person = LoosePerson.create(nested_attributes_hash(:best_friend), :as => :admin)
+ assert_admin_attributes(person.best_friend, true)
+ end
+
+ def test_has_one_create_with_admin_role_with_attr_accessible_attributes
+ person = TightPerson.create(nested_attributes_hash(:best_friend), :as => :admin)
+ assert_admin_attributes(person.best_friend, true)
+ end
+
+ def test_has_one_create_without_protection
+ person = LoosePerson.create(nested_attributes_hash(:best_friend, false, nil), :without_protection => true)
+ assert_all_attributes(person.best_friend)
+ end
+
+ def test_belongs_to_create_with_attr_protected_attributes
+ person = LoosePerson.create(nested_attributes_hash(:best_friend_of))
+ assert_default_attributes(person.best_friend_of, true)
+ end
+
+ def test_belongs_to_create_with_attr_accessible_attributes
+ person = TightPerson.create(nested_attributes_hash(:best_friend_of))
+ assert_default_attributes(person.best_friend_of, true)
+ end
+
+ def test_belongs_to_create_with_admin_role_with_attr_protected_attributes
+ person = LoosePerson.create(nested_attributes_hash(:best_friend_of), :as => :admin)
+ assert_admin_attributes(person.best_friend_of, true)
+ end
+
+ def test_belongs_to_create_with_admin_role_with_attr_accessible_attributes
+ person = TightPerson.create(nested_attributes_hash(:best_friend_of), :as => :admin)
+ assert_admin_attributes(person.best_friend_of, true)
+ end
+
+ def test_belongs_to_create_without_protection
+ person = LoosePerson.create(nested_attributes_hash(:best_friend_of, false, nil), :without_protection => true)
+ assert_all_attributes(person.best_friend_of)
+ end
+
+ def test_has_many_create_with_attr_protected_attributes
+ person = LoosePerson.create(nested_attributes_hash(:best_friends, true))
+ assert_default_attributes(person.best_friends.first, true)
+ end
+
+ def test_has_many_create_with_attr_accessible_attributes
+ person = TightPerson.create(nested_attributes_hash(:best_friends, true))
+ assert_default_attributes(person.best_friends.first, true)
+ end
+
+ def test_has_many_create_with_admin_role_with_attr_protected_attributes
+ person = LoosePerson.create(nested_attributes_hash(:best_friends, true), :as => :admin)
+ assert_admin_attributes(person.best_friends.first, true)
+ end
+
+ def test_has_many_create_with_admin_role_with_attr_accessible_attributes
+ person = TightPerson.create(nested_attributes_hash(:best_friends, true), :as => :admin)
+ assert_admin_attributes(person.best_friends.first, true)
+ end
+
+ def test_has_many_create_without_protection
+ person = LoosePerson.create(nested_attributes_hash(:best_friends, true, nil), :without_protection => true)
+ assert_all_attributes(person.best_friends.first)
+ end
+
+ # create!
+
+ def test_has_one_create_with_bang_with_attr_protected_attributes
+ person = LoosePerson.create!(nested_attributes_hash(:best_friend))
+ assert_default_attributes(person.best_friend, true)