From 5a898e106a6f92ad2773a6525b0899cf906d2b3f Mon Sep 17 00:00:00 2001 From: Peter Baker Date: Thu, 14 Oct 2010 11:24:25 -0500 Subject: Explain actionamailer authentication types --- actionmailer/lib/action_mailer/base.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 0bf60a2c24..478ceda389 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -290,7 +290,9 @@ module ActionMailer #:nodoc: # * :password - If your mail server requires authentication, set the password in this setting. # * :authentication - If your mail server requires authentication, you need to specify the # authentication type here. - # This is a symbol and one of :plain, :login, :cram_md5. + # This is a symbol and one of :plain (will send the password in the clear), :login (will + # send password BASE64 encoded) or :cram_md5 (combines a Challenge/Response mechanism to exchange + # information and a cryptographic Message Digest 5 algorithm to hash important information) # * :enable_starttls_auto - When set to true, detects if STARTTLS is enabled in your SMTP server # and starts to use it. # -- cgit v1.2.3 From 1ef9ddde90a6a10099faecc93542335c437cfb09 Mon Sep 17 00:00:00 2001 From: Jared Crapo Date: Thu, 21 Oct 2010 11:56:36 -0700 Subject: Fixed typo in code for Session Expiry --- railties/guides/source/security.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 4656cf4e40..e2103959ac 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -166,7 +166,7 @@ end The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above: -delete_all "updated_at < '#{time.to_s(:db)}' OR +delete_all "updated_at < '#{time.ago.to_s(:db)}' OR created_at < '#{2.days.ago.to_s(:db)}'" -- cgit v1.2.3 From 5b502ed9ccc3fef7ba956588119e0e5b257dfd66 Mon Sep 17 00:00:00 2001 From: Greg Jastrab Date: Fri, 22 Oct 2010 13:30:00 -0400 Subject: fixed example code for i18n exception handling --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 8a7e9fcae6..998eac81f6 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -830,7 +830,7 @@ In other contexts you might want to change this behaviour, though. E.g. the defa module I18n - def just_raise_that_exception(*args) + def self.just_raise_that_exception(*args) raise args.first end end -- cgit v1.2.3 From 67c40160d384037d664952cee808a49104bdc627 Mon Sep 17 00:00:00 2001 From: "Ryan L. Cross" Date: Sun, 24 Oct 2010 19:17:49 -0700 Subject: Corrected typo and wording. --- activesupport/lib/active_support/cache.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 9098ffbfec..9771ca966f 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -214,7 +214,7 @@ module ActiveSupport # cache.write(key, value, :expire_in => 1.minute) # Set a lower value for one entry # # Setting :race_condition_ttl is very useful in situations where a cache entry - # is used very frequently unver heavy load. If a cache expires and due to heavy load + # is used very frequently and is under heavy load. If a cache expires and due to heavy load # seven different processes will try to read data natively and then they all will try to # write to cache. To avoid that case the first process to find an expired cache entry will # bump the cache expiration time by the value set in :race_condition_ttl. Yes -- cgit v1.2.3 From 04775a41cbbddceeffa9cf8590566b9990fd4070 Mon Sep 17 00:00:00 2001 From: eparreno Date: Mon, 15 Nov 2010 19:21:40 +0100 Subject: remove old school validations and replaced with new validates method. Pending: fix active_record guide --- .../active_record_validations_callbacks.textile | 18 ++++---- railties/guides/source/i18n.textile | 48 +++++++++++----------- railties/guides/source/migrations.textile | 2 +- railties/guides/source/security.textile | 2 +- railties/guides/source/testing.textile | 2 +- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index e9ff6197ec..374c0f84a5 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -96,7 +96,7 @@ To verify whether or not an object is valid, Rails uses the +valid?+ method. You class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true end Person.create(:name => "John Doe").valid? # => true @@ -109,7 +109,7 @@ Note that an object instantiated with +new+ will not report errors even if it's class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true end >> p = Person.new @@ -147,7 +147,7 @@ This method is only useful _after_ validations have been run, because it only in class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true end >> Person.new.errors[:name].any? # => false @@ -355,7 +355,7 @@ This helper validates that the specified attributes are not empty. It uses the + class Person < ActiveRecord::Base - validates_presence_of :name, :login, :email + validates :name, :presence => true, :login, :email end @@ -505,7 +505,7 @@ class Person < ActiveRecord::Base validates_numericality_of :age, :on => :update # the default (validates on both create and update) - validates_presence_of :name, :on => :save + validates :name, :presence => true, :on => :save end @@ -603,7 +603,7 @@ Returns an OrderedHash with all errors. Each key is the attribute name and the v class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true validates_length_of :name, :minimum => 3 end @@ -623,7 +623,7 @@ h4(#working_with_validation_errors-errors-2). +errors[]+ class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true validates_length_of :name, :minimum => 3 end @@ -699,7 +699,7 @@ The +clear+ method is used when you intentionally want to clear all the messages class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true validates_length_of :name, :minimum => 3 end @@ -723,7 +723,7 @@ The +size+ method returns the total number of error messages for the object. class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true validates_length_of :name, :minimum => 3 validates_presence_of :email end diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 8a7e9fcae6..4803264c97 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -672,11 +672,11 @@ Active Record validation error messages can also be translated easily. Active Re This gives you quite powerful means to flexibly adjust your messages to your application's needs. -Consider a User model with a +validates_presence_of+ validation for the name attribute like this: +Consider a User model with a validation for the name attribute like this: class User < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true end @@ -706,7 +706,7 @@ For example, you might have an Admin model inheriting from User: class Admin < User - validates_presence_of :name + validates :name, :presence => true end @@ -733,27 +733,27 @@ So, for example, instead of the default error message +"can not be blank"+ you c * +count+, where available, can be used for pluralization if present: |_. validation |_.with option |_.message |_.interpolation| -| validates_confirmation_of | - | :confirmation | -| -| validates_acceptance_of | - | :accepted | -| -| validates_presence_of | - | :blank | -| -| validates_length_of | :within, :in | :too_short | count| -| validates_length_of | :within, :in | :too_long | count| -| validates_length_of | :is | :wrong_length | count| -| validates_length_of | :minimum | :too_short | count| -| validates_length_of | :maximum | :too_long | count| -| validates_uniqueness_of | - | :taken | -| -| validates_format_of | - | :invalid | -| -| validates_inclusion_of | - | :inclusion | -| -| validates_exclusion_of | - | :exclusion | -| -| validates_associated | - | :invalid | -| -| validates_numericality_of | - | :not_a_number | -| -| validates_numericality_of | :greater_than | :greater_than | count| -| validates_numericality_of | :greater_than_or_equal_to | :greater_than_or_equal_to | count| -| validates_numericality_of | :equal_to | :equal_to | count| -| validates_numericality_of | :less_than | :less_than | count| -| validates_numericality_of | :less_than_or_equal_to | :less_than_or_equal_to | count| -| validates_numericality_of | :odd | :odd | -| -| validates_numericality_of | :even | :even | -| +| confirmation | - | :confirmation | -| +| acceptance | - | :accepted | -| +| presence | - | :blank | -| +| length | :within, :in | :too_short | count| +| length | :within, :in | :too_long | count| +| length | :is | :wrong_length | count| +| length | :minimum | :too_short | count| +| length | :maximum | :too_long | count| +| uniqueness | - | :taken | -| +| format | - | :invalid | -| +| inclusion | - | :inclusion | -| +| exclusion | - | :exclusion | -| +| associated | - | :invalid | -| +| numericality | - | :not_a_number | -| +| numericality | :greater_than | :greater_than | count| +| numericality | :greater_than_or_equal_to | :greater_than_or_equal_to | count| +| numericality | :equal_to | :equal_to | count| +| numericality | :less_than | :less_than | count| +| numericality | :less_than_or_equal_to | :less_than_or_equal_to | count| +| numericality | :odd | :odd | -| +| numericality | :even | :even | -| h5. Translations for the Active Record +error_messages_for+ Helper diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index 4c49a4ccbc..61d457e351 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -584,7 +584,7 @@ h3. Active Record and Referential Integrity The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used. -Validations such as +validates_uniqueness_of+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints. +Validations such as +validates :foreign_key, :uniqueness => true+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints. Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":http://github.com/harukizaemon/redhillonrails/tree/master/foreign_key_migrations/ which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+). diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 4656cf4e40..bc6e6e3390 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -550,7 +550,7 @@ Ruby uses a slightly different approach than many other languages to match the e class File < ActiveRecord::Base - validates_format_of :name, :with => /^[\w\.\-\+]+$/ + validates :name, format => /^[\w\.\-\+]+$/ end diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index 043a2462b4..aae849c369 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -315,7 +315,7 @@ Now to get this test to pass we can add a model level validation for the _title_ class Post < ActiveRecord::Base - validates_presence_of :title + validates :title, :presence => true end -- cgit v1.2.3 From 45e3827eb22b243c6bc4358fcbee7feed3de3873 Mon Sep 17 00:00:00 2001 From: zhengjia Date: Tue, 16 Nov 2010 09:26:21 -0600 Subject: Minor fix on Rails:Railtie documentation --- railties/lib/rails/railtie.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index c76bc83377..030a838dc1 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -97,7 +97,7 @@ module Rails # If your railtie has rake tasks, you can tell Rails to load them through the method # rake tasks: # - # class MyRailtie < Railtie + # class MyRailtie < Rails::Railtie # rake_tasks do # load "path/to/my_railtie.tasks" # end @@ -107,7 +107,7 @@ module Rails # your generators at a different location, you can specify in your Railtie a block which # will load them during normal generators lookup: # - # class MyRailtie < Railtie + # class MyRailtie < Rails::Railtie # generators do # require "path/to/my_railtie_generator" # end -- cgit v1.2.3 From 0545d0eb993eff74143092b7ace2f85e9e809790 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Tue, 16 Nov 2010 22:25:20 +0100 Subject: i18n guide: it's activerecord.errors.messages.record_invalid (instead of 'invalid'), and messagges typo --- railties/guides/source/i18n.textile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 4803264c97..6bf52f1ac2 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -464,24 +464,24 @@ I18n.t 'message' The +translate+ method also takes a +:scope+ option which can contain one or more additional keys that will be used to specify a “namespace” or scope for a translation key: -I18n.t :invalid, :scope => [:activerecord, :errors, :messages] +I18n.t :record_invalid, :scope => [:activerecord, :errors, :messages] -This looks up the +:invalid+ message in the Active Record error messages. +This looks up the +:record_invalid+ message in the Active Record error messages. Additionally, both the key and scopes can be specified as dot-separated keys as in: -I18n.translate :"activerecord.errors.messages.invalid" +I18n.translate "activerecord.errors.messages.record_invalid" Thus the following calls are equivalent: -I18n.t 'activerecord.errors.messages.invalid' -I18n.t 'errors.messages.invalid', :scope => :active_record -I18n.t :invalid, :scope => 'activerecord.errors.messages' -I18n.t :invalid, :scope => [:activerecord, :errors, :messages] +I18n.t 'activerecord.errors.messages.record_invalid' +I18n.t 'errors.messages.record_invalid', :scope => :active_record +I18n.t :record_invalid, :scope => 'activerecord.errors.messages' +I18n.t :record_invalid, :scope => [:activerecord, :errors, :messages] h5. Defaults @@ -697,7 +697,7 @@ activerecord.errors.models.user.attributes.name.blank activerecord.errors.models.user.blank activerecord.errors.messages.blank errors.attributes.name.blank -errors.messagges.blank +errors.messages.blank When your models are additionally using inheritance then the messages are looked up in the inheritance chain. @@ -719,7 +719,7 @@ activerecord.errors.models.user.attributes.title.blank activerecord.errors.models.user.blank activerecord.errors.messages.blank errors.attributes.title.blank -errors.messagges.blank +errors.messages.blank This way you can provide special translations for various error messages at different points in your models inheritance chain and in the attributes, models, or default scopes. -- cgit v1.2.3 From 8f8abbbf7ae21f5bb88c2d43388532bcfe33ca94 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Tue, 16 Nov 2010 22:35:11 +0100 Subject: i18n guide: remove link to external page about 'How to encode the current locale in the URL' as it no longer exists --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 6bf52f1ac2..3e344c32b3 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -253,7 +253,7 @@ match '/:locale' => 'dashboard#index' Do take special care about the *order of your routes*, so this route declaration does not "eat" other ones. (You may want to add it directly before the +root :to+ declaration.) -IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/wikipages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki. +IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. h4. Setting the Locale from the Client Supplied Information -- cgit v1.2.3 From 648568df1a61cf335d8b235487617777dcdea117 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Wed, 17 Nov 2010 09:24:47 +0100 Subject: i18n guide: this is not longer a problem --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 3e344c32b3..b42942bb63 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -253,7 +253,7 @@ match '/:locale' => 'dashboard#index' Do take special care about the *order of your routes*, so this route declaration does not "eat" other ones. (You may want to add it directly before the +root :to+ declaration.) -IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. +NOTE: Have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. h4. Setting the Locale from the Client Supplied Information -- cgit v1.2.3 From 126fbd7ed8a310bf940414c1b7ddab06b03d400e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 17 Nov 2010 21:46:55 -0500 Subject: unscoped takes care of named_scopes too --- activerecord/lib/active_record/base.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f588475bbf..d7a7101404 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -856,8 +856,7 @@ module ActiveRecord #:nodoc: # limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } # - # It is recommended to use block form of unscoped because chaining unscoped with named_scope - # does not work. Assuming that published is a named_scope following two statements are same. + # Assuming that published is a named_scope following two statements are same. # # Post.unscoped.published # Post.published -- cgit v1.2.3 From 03f141378705cb544986ad2db54169c8f1c464c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Hackin?= Date: Thu, 18 Nov 2010 08:25:38 -0200 Subject: Fix code for customize the error messages html adding a .html_safe of 8.3 section --- railties/guides/source/active_record_validations_callbacks.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 374c0f84a5..444a1d4154 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -824,10 +824,10 @@ Here is a simple example where we change the Rails behaviour to always display t ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| if instance.error_message.kind_of?(Array) %(#{html_tag}  - #{instance.error_message.join(',')}) + #{instance.error_message.join(',')}).html_safe else %(#{html_tag}  - #{instance.error_message}) + #{instance.error_message}).html_safe end end -- cgit v1.2.3 From bf78ceb7722c30f94a7fc915d53d123502ae7c1d Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Fri, 19 Nov 2010 17:25:37 +0100 Subject: i18n guide: fix external link to rack locale --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index b42942bb63..3179748c56 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -278,7 +278,7 @@ def extract_locale_from_accept_language_header end -Of course, in a production environment you would need much more robust code, and could use a plugin such as Iain Hecker's "http_accept_language":http://github.com/iain/http_accept_language/tree/master or even Rack middleware such as Ryan Tomayko's "locale":http://github.com/rtomayko/rack-contrib/blob/master/lib/rack/locale.rb. +Of course, in a production environment you would need much more robust code, and could use a plugin such as Iain Hecker's "http_accept_language":http://github.com/iain/http_accept_language/tree/master or even Rack middleware such as Ryan Tomayko's "locale":http://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb. h5. Using GeoIP (or Similar) Database -- cgit v1.2.3 From 938243feb9ea177b84276821818c9eed66064340 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 19 Nov 2010 16:26:09 -0800 Subject: do not require ruby-debug automatically. please require it if you have declared it as a dependency --- actionpack/test/abstract_unit.rb | 8 -------- activemodel/test/cases/helper.rb | 6 ------ activerecord/test/cases/helper.rb | 5 ----- railties/lib/rails/test_help.rb | 11 ----------- 4 files changed, 30 deletions(-) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 92597e40ff..60534a9746 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -36,14 +36,6 @@ require 'active_record' require 'action_controller/caching' require 'action_controller/caching/sweeping' -begin - require 'ruby-debug' - Debugger.settings[:autoeval] = true - Debugger.start -rescue LoadError - # Debugging disabled. `gem install ruby-debug` to enable. -end - require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late module Rails diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index a81584bbad..01f0158678 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -12,9 +12,3 @@ ActiveSupport::Deprecation.debug = true require 'rubygems' require 'test/unit' - -begin - require 'ruby-debug' - Debugger.start -rescue LoadError -end diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 52f26b71f5..f9bbc5299b 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -13,11 +13,6 @@ require 'active_record' require 'active_support/dependencies' require 'connection' -begin - require 'ruby-debug' -rescue LoadError -end - # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index 38f2f651f4..f81002328f 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -39,14 +39,3 @@ class ActionDispatch::IntegrationTest @routes = Rails.application.routes end end - -begin - require_library_or_gem 'ruby-debug' - Debugger.start - if Debugger.respond_to?(:settings) - Debugger.settings[:autoeval] = true - Debugger.settings[:autolist] = 1 - end -rescue LoadError - # ruby-debug wasn't available so neither can the debugging be -end -- cgit v1.2.3 From d3f8765482133df14d355f1763d3df1c883d2689 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 20 Nov 2010 02:28:58 -0200 Subject: Avoid range object creation --- actionpack/lib/abstract_controller/rendering.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index ba31e2b1ea..91b75273fa 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -128,7 +128,7 @@ module AbstractController hash = {} variables = instance_variable_names variables -= protected_instance_variables if respond_to?(:protected_instance_variables) - variables.each { |name| hash[name.to_s[1..-1]] = instance_variable_get(name) } + variables.each { |name| hash[name.to_s[1, name.length]] = instance_variable_get(name) } hash end -- cgit v1.2.3 From 4f3b5b8ec1443f0a6150178ce06b1dc43377540e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 20 Nov 2010 02:46:55 -0200 Subject: Fix indentation --- .../lib/action_controller/metal/rendering.rb | 47 +++++++++++----------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index e524e546ad..14cc547dd0 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -20,36 +20,35 @@ module ActionController private - # Normalize arguments by catching blocks and setting them on :update. - def _normalize_args(action=nil, options={}, &blk) #:nodoc: - options = super - options[:update] = blk if block_given? - options - end - - # Normalize both text and status options. - def _normalize_options(options) #:nodoc: - if options.key?(:text) && options[:text].respond_to?(:to_text) - options[:text] = options[:text].to_text - end + # Normalize arguments by catching blocks and setting them on :update. + def _normalize_args(action=nil, options={}, &blk) #:nodoc: + options = super + options[:update] = blk if block_given? + options + end - if options[:status] - options[:status] = Rack::Utils.status_code(options[:status]) - end + # Normalize both text and status options. + def _normalize_options(options) #:nodoc: + if options.key?(:text) && options[:text].respond_to?(:to_text) + options[:text] = options[:text].to_text + end - super + if options[:status] + options[:status] = Rack::Utils.status_code(options[:status]) end - # Process controller specific options, as status, content-type and location. - def _process_options(options) #:nodoc: - status, content_type, location = options.values_at(:status, :content_type, :location) + super + end - self.status = status if status - self.content_type = content_type if content_type - self.headers["Location"] = url_for(location) if location + # Process controller specific options, as status, content-type and location. + def _process_options(options) #:nodoc: + status, content_type, location = options.values_at(:status, :content_type, :location) - super - end + self.status = status if status + self.content_type = content_type if content_type + self.headers["Location"] = url_for(location) if location + super + end end end -- cgit v1.2.3 From 5b86e8f5e9fbc55a04d72113035054c35a400507 Mon Sep 17 00:00:00 2001 From: nosolopau Date: Sat, 20 Nov 2010 04:23:31 -0800 Subject: Spelling mistake: "Projecto" instead of "projeto" --- railties/guides/source/3_0_release_notes.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile index 9c08c9fa0a..2d9655fa7a 100644 --- a/railties/guides/source/3_0_release_notes.textile +++ b/railties/guides/source/3_0_release_notes.textile @@ -271,10 +271,10 @@ end scope 'es' do - resources :projects, :path_names => { :edit => 'cambiar' }, :path => 'projeto' + resources :projects, :path_names => { :edit => 'cambiar' }, :path => 'projecto' end -# Gives you the edit action with /es/projeto/1/cambiar +# Gives you the edit action with /es/projecto/1/cambiar * Added +root+ method to the router as a short cut for match '/', :to => path. -- cgit v1.2.3 From 206e48e8b943981b5b9ffc1fe7a2150e2ac21427 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 20 Nov 2010 16:20:42 +0100 Subject: applies API conventions to the RDoc of json_encode * Examples running with the text are preferred over separate Example sections. * No need to call puts, in # => we show the return value, not STDOUT. * Say explicitly that double quotes are removed. * Specify that we are talking \uXXX, rather than, say, HTML entities. --- .../core_ext/string/output_safety.rb | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 37c206ea3c..bb0f747960 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -33,21 +33,23 @@ class ERB singleton_class.send(:remove_method, :html_escape) module_function :html_escape - # A utility method for escaping HTML entities in JSON strings. - # This method is also aliased as j. + # A utility method for escaping HTML entities in JSON strings + # using \uXXXX JavaScript escape sequences for string literals: # - # Note that after this operation is performed the output is not - # a valid JSON. + # json_escape("is a > 0 & a < 10?") + # # => is a \u003E 0 \u0026 a \u003C 10? # - # In your ERb templates, use this method to escape any HTML entities: - # <%=j @person.to_json %> + # Note that after this operation is performed the output is not + # valid JSON. In particular double quotes are removed: # - # ==== Example: - # puts json_escape("{\"name\":\"john\",\"created_at\":\"2010-04-28T01:39:31Z\",\"id\":1}") + # json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}') # # => {name:john,created_at:2010-04-28T01:39:31Z,id:1} # - # puts json_escape("is a > 0 & a < 10?") - # # => is a \u003E 0 \u0026 a \u003C 10? + # This method is also aliased as +j+, and available as a helper + # in Rails templates: + # + # <%=j @person.to_json %> + # def json_escape(s) s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] } end -- cgit v1.2.3 From d9ac62c91466e40b557d27c4d712f86759d63a4b Mon Sep 17 00:00:00 2001 From: Matthew Rudy Jacobs Date: Sun, 21 Nov 2010 00:18:33 +0800 Subject: Fix Bug: disable_referential_integrity doesn't work for postgres 9.0.1 --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index ccc5085b84..7dd81abc7e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -404,10 +404,7 @@ module ActiveRecord # REFERENTIAL INTEGRITY ==================================== def supports_disable_referential_integrity?() #:nodoc: - version = query("SHOW server_version")[0][0].split('.') - version[0].to_i >= 8 && version[1].to_i >= 1 - rescue - return false + postgresql_version >= 80100 end def disable_referential_integrity #:nodoc: -- cgit v1.2.3 From 3ddbaf955e45eed15662e2f141b75dc0df63c1b0 Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Sun, 21 Nov 2010 01:36:27 +0700 Subject: Mention the case that `render` will return nil if given `:collection` is nil or empty when rendering partial. --- actionpack/lib/action_view/partials.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 844c3e9572..cd3f130dc4 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -75,6 +75,11 @@ module ActionView # # <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %> # + # If the given :collection is nil or empty, render will return nil. This will allow you + # to specify a text which will displayed instead by using this form: + # + # <%= render(:partial => "ad", :collection => @advertisements) || "There's no ad to be displayed" %> + # # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also # just keep domain objects, like Active Records, in there. # -- cgit v1.2.3 From 8d8062190ddd0452088f6ea917267ed993a70990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 20 Nov 2010 19:39:30 +0100 Subject: I18n is always loaded on boot by Active Model or Action Pack, so no need for supporting lazy hooks. --- activesupport/lib/active_support/i18n.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 4d33f597d9..00ea8813dd 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -7,4 +7,3 @@ rescue LoadError => e end I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" -ActiveSupport.run_load_hooks(:i18n) -- cgit v1.2.3 From d7db6a88734c3b666f4b85f266d223eff408b294 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Fri, 19 Nov 2010 18:29:33 +0100 Subject: class inheritable attributes is used no more! all internal use of class inheritable has been changed to class_attribute. class inheritable attributes has been deprecated. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionmailer/lib/action_mailer/test_case.rb | 7 +++++-- actionpack/lib/action_controller/test_case.rb | 6 ++++-- .../vendor/html-scanner/html/sanitizer.rb | 4 ++-- actionpack/lib/action_view/helpers/form_helper.rb | 4 ++-- activerecord/lib/active_record/associations.rb | 13 +++++++------ .../associations/association_collection.rb | 2 +- .../lib/active_record/attribute_methods/dirty.rb | 3 ++- .../attribute_methods/time_zone_conversion.rb | 6 ++++-- activerecord/lib/active_record/base.rb | 20 ++++++++++++-------- activerecord/lib/active_record/named_scope.rb | 10 ++++++---- activerecord/lib/active_record/nested_attributes.rb | 13 +++++++++---- activerecord/lib/active_record/reflection.rb | 20 +++++++++----------- activerecord/lib/active_record/timestamp.rb | 8 +++++--- activerecord/test/cases/associations_test.rb | 8 ++++---- activeresource/lib/active_resource/base.rb | 8 +++++--- .../core_ext/class/inheritable_attributes.rb | 9 +++++++++ .../class/class_inheritable_attributes_test.rb | 5 +++++ 17 files changed, 91 insertions(+), 55 deletions(-) diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb index f4d1bb59f1..63e18147f6 100644 --- a/actionmailer/lib/action_mailer/test_case.rb +++ b/actionmailer/lib/action_mailer/test_case.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/class/attribute' + module ActionMailer class NonInferrableMailerError < ::StandardError def initialize(name) @@ -15,11 +17,11 @@ module ActionMailer module ClassMethods def tests(mailer) - write_inheritable_attribute(:mailer_class, mailer) + self._mailer_class = mailer end def mailer_class - if mailer = read_inheritable_attribute(:mailer_class) + if mailer = self._mailer_class mailer else tests determine_default_mailer(name) @@ -65,6 +67,7 @@ module ActionMailer end included do + class_attribute :_mailer_class setup :initialize_test_deliveries setup :set_expected_mail end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 2b2f647d32..0f43527a56 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,6 +1,7 @@ require 'rack/session/abstract/id' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/to_query' +require 'active_support/core_ext/class/attribute' module ActionController module TemplateAssertions @@ -325,11 +326,11 @@ module ActionController def controller_class=(new_class) prepare_controller_class(new_class) if new_class - write_inheritable_attribute(:controller_class, new_class) + self._controller_class = new_class end def controller_class - if current_controller_class = read_inheritable_attribute(:controller_class) + if current_controller_class = self._controller_class current_controller_class else self.controller_class = determine_default_controller_class(name) @@ -442,6 +443,7 @@ module ActionController included do include ActionController::TemplateAssertions include ActionDispatch::Assertions + class_attribute :_controller_class setup :setup_controller_request_and_response end diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb index dceddb9b80..3e5d23b5c1 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb @@ -1,5 +1,5 @@ require 'set' -require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/attribute' module HTML class Sanitizer @@ -60,7 +60,7 @@ module HTML class WhiteListSanitizer < Sanitizer [:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags, :allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr| - class_inheritable_accessor attr, :instance_writer => false + class_attribute attr, :instance_writer => false end # A regular expression of the valid characters used to separate protocols like diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 8c300ec745..ef5bbd8ae3 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -2,7 +2,7 @@ require 'cgi' require 'action_view/helpers/date_helper' require 'action_view/helpers/tag_helper' require 'action_view/helpers/form_tag_helper' -require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' @@ -1117,7 +1117,7 @@ module ActionView class FormBuilder #:nodoc: # The methods which wrap a form helper call. - class_inheritable_accessor :field_helpers + class_attribute :field_helpers self.field_helpers = (FormHelper.instance_method_names - ['form_for']) attr_accessor :object_name, :object, :options diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 08a4ebfd7e..7da38cd03f 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/module/remove_method' +require 'active_support/core_ext/class/attribute' module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: @@ -1810,12 +1811,12 @@ module ActiveRecord callbacks.each do |callback_name| full_callback_name = "#{callback_name}_for_#{association_name}" defined_callbacks = options[callback_name.to_sym] - if options.has_key?(callback_name.to_sym) - class_inheritable_reader full_callback_name.to_sym - write_inheritable_attribute(full_callback_name.to_sym, [defined_callbacks].flatten) - else - write_inheritable_attribute(full_callback_name.to_sym, []) - end + + full_callback_value = options.has_key?(callback_name.to_sym) ? [defined_callbacks].flatten : [] + + # TODO : why do i need method_defined? I think its because of the inheritance chain + class_attribute full_callback_name.to_sym unless method_defined?(full_callback_name) + self.send("#{full_callback_name}=", full_callback_value) end end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 6090376bb8..774103342a 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -530,7 +530,7 @@ module ActiveRecord def callbacks_for(callback_name) full_callback_name = "#{callback_name}_for_#{@reflection.name}" - @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || [] + @owner.class.send(full_callback_name.to_sym) || [] end def ensure_owner_is_not_new diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 439880c1fa..c19a33faa8 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/object/blank' module ActiveRecord @@ -88,7 +89,7 @@ module ActiveRecord end def clone_with_time_zone_conversion_attribute?(attr, old) - old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym) + old.class.name == "Time" && time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(attr.to_sym) end end end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index d640b26b74..dc2785b6bf 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/class/attribute' + module ActiveRecord module AttributeMethods module TimeZoneConversion @@ -7,7 +9,7 @@ module ActiveRecord cattr_accessor :time_zone_aware_attributes, :instance_writer => false self.time_zone_aware_attributes = false - class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false + class_attribute :skip_time_zone_conversion_for_attributes, :instance_writer => false self.skip_time_zone_conversion_for_attributes = [] end @@ -54,7 +56,7 @@ module ActiveRecord private def create_time_zone_conversion_attribute?(name, column) - time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type) + time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type) end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 314f676711..8bde1869c7 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -7,7 +7,7 @@ require 'active_support/time' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/class/delegating_attributes' -require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/hash/indifferent_access' @@ -412,7 +412,7 @@ module ActiveRecord #:nodoc: self.store_full_sti_class = true # Stores the default scope for the class - class_inheritable_accessor :default_scoping, :instance_writer => false + class_attribute :default_scoping, :instance_writer => false self.default_scoping = [] # Returns a hash of all the attributes that have been specified for serialization as @@ -420,6 +420,9 @@ module ActiveRecord #:nodoc: class_attribute :serialized_attributes self.serialized_attributes = {} + class_attribute :_attr_readonly, :instance_writer => false + self._attr_readonly = [] + class << self # Class methods delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped delegate :find_each, :find_in_batches, :to => :scoped @@ -504,12 +507,12 @@ module ActiveRecord #:nodoc: # Attributes listed as readonly will be used to create a new record but update operations will # ignore these fields. def attr_readonly(*attributes) - write_inheritable_attribute(:attr_readonly, Set.new(attributes.map { |a| a.to_s }) + (readonly_attributes || [])) + self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || []) end # Returns an array of all the attributes that have been specified as readonly. def readonly_attributes - read_inheritable_attribute(:attr_readonly) || [] + self._attr_readonly end # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object, @@ -724,8 +727,8 @@ module ActiveRecord #:nodoc: @arel_engine = @relation = @arel_table = nil end - def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: - descendants.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information } + def reset_column_information_for_all_subclasses#:nodoc: + descendants.each { |klass| klass.reset_column_information } end def attribute_method?(attribute) @@ -1126,7 +1129,8 @@ MSG # Article.create.published # => true def default_scope(options = {}) reset_scoped_methods - self.default_scoping << construct_finder_arel(options, default_scoping.pop) + default_scoping = self.default_scoping.dup + self.default_scoping = default_scoping << construct_finder_arel(options, default_scoping.pop) end def current_scoped_methods #:nodoc: @@ -1579,7 +1583,7 @@ MSG self.class.columns_hash[name.to_s] end - # Returns true if +comparison_object+ is the same exact object, or +comparison_object+ + # Returns true if +comparison_object+ is the same exact object, or +comparison_object+ # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+. # # Note that new records are different from any other record by definition, unless the diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 0b92ba5caa..0f421560f0 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -2,12 +2,18 @@ require 'active_support/core_ext/array' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/class/attribute' module ActiveRecord # = Active Record Named \Scopes module NamedScope extend ActiveSupport::Concern + included do + class_attribute :scopes + self.scopes = {} + end + module ClassMethods # Returns an anonymous \scope. # @@ -33,10 +39,6 @@ module ActiveRecord end end - def scopes - read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {}) - end - # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query, # such as where(:color => :red).select('shirts.*').includes(:washing_instructions). # diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 0c3392263a..f1d3eaed38 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/class/attribute' module ActiveRecord module NestedAttributes #:nodoc: @@ -11,7 +12,7 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_inheritable_accessor :nested_attributes_options, :instance_writer => false + class_attribute :nested_attributes_options, :instance_writer => false self.nested_attributes_options = {} end @@ -268,7 +269,11 @@ module ActiveRecord if reflection = reflect_on_association(association_name) reflection.options[:autosave] = true add_autosave_association_callbacks(reflection) + + nested_attributes_options = self.nested_attributes_options.dup nested_attributes_options[association_name.to_sym] = options + self.nested_attributes_options = nested_attributes_options + type = (reflection.collection? ? :collection : :one_to_one) # def pirate_attributes=(attributes) @@ -315,7 +320,7 @@ module ActiveRecord # update_only is true, and a :_destroy 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) - options = nested_attributes_options[association_name] + options = self.nested_attributes_options[association_name] attributes = attributes.with_indifferent_access check_existing_record = (options[:update_only] || !attributes['id'].blank?) @@ -364,7 +369,7 @@ module ActiveRecord # { :id => '2', :_destroy => true } # ]) def assign_nested_attributes_for_collection_association(association_name, attributes_collection) - options = nested_attributes_options[association_name] + options = self.nested_attributes_options[association_name] unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" @@ -433,7 +438,7 @@ module ActiveRecord end def call_reject_if(association_name, attributes) - case callback = nested_attributes_options[association_name][:reject_if] + case callback = self.nested_attributes_options[association_name][:reject_if] when Symbol method(callback).arity == 0 ? send(callback) : send(callback, attributes) when Proc diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index a2260e9a19..a07c321960 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1,8 +1,15 @@ +require 'active_support/core_ext/class/attribute' + module ActiveRecord # = Active Record Reflection module Reflection # :nodoc: extend ActiveSupport::Concern + included do + class_attribute :reflections + self.reflections = {} + end + # Reflection enables to interrogate Active Record classes and objects # about their associations and aggregations. This information can, # for example, be used in a form builder that takes an Active Record object @@ -20,18 +27,9 @@ module ActiveRecord when :composed_of reflection = AggregateReflection.new(macro, name, options, active_record) end - write_inheritable_hash :reflections, name => reflection - reflection - end - # Returns a hash containing all AssociationReflection objects for the current class. - # Example: - # - # Invoice.reflections - # Account.reflections - # - def reflections - read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {}) + self.reflections = self.reflections.merge(name => reflection) + reflection end # Returns an array of AggregateReflection objects for all the aggregations in the class. diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 230adf6b2b..2ecbd906bd 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/class/attribute' + module ActiveRecord # = Active Record Timestamp # @@ -29,14 +31,14 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_inheritable_accessor :record_timestamps, :instance_writer => false + class_attribute :record_timestamps, :instance_writer => false self.record_timestamps = true end private def create #:nodoc: - if record_timestamps + if self.record_timestamps current_time = current_time_from_proper_timezone all_timestamp_attributes.each do |column| @@ -61,7 +63,7 @@ module ActiveRecord end def should_record_timestamps? - record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?) + self.record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?) end def timestamp_attributes_for_update_in_model diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 93a51d3606..83c605d2bb 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -270,17 +270,17 @@ class OverridingAssociationsTest < ActiveRecord::TestCase def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited # redeclared association on AR descendant should not inherit callbacks from superclass - callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many) + callbacks = PeopleList.before_add_for_has_and_belongs_to_many assert_equal([:enlist], callbacks) - callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many) + callbacks = DifferentPeopleList.before_add_for_has_and_belongs_to_many assert_equal([], callbacks) end def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited # redeclared association on AR descendant should not inherit callbacks from superclass - callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_many) + callbacks = PeopleList.before_add_for_has_many assert_equal([:enlist], callbacks) - callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_many) + callbacks = DifferentPeopleList.before_add_for_has_many assert_equal([], callbacks) end diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index b5b46d7431..d959fd103a 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -1,6 +1,6 @@ require 'active_support' require 'active_support/core_ext/class/attribute_accessors' -require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/module/attr_accessor_with_default' @@ -263,6 +263,8 @@ module ActiveResource # The logger for diagnosing and tracing Active Resource calls. cattr_accessor :logger + class_attribute :_format + class << self # Creates a schema for this resource - setting the attributes that are # known prior to fetching an instance from the remote system. @@ -492,13 +494,13 @@ module ActiveResource format = mime_type_reference_or_format.is_a?(Symbol) ? ActiveResource::Formats[mime_type_reference_or_format] : mime_type_reference_or_format - write_inheritable_attribute(:format, format) + self._format = format connection.format = format if site end # Returns the current format, default is ActiveResource::Formats::XmlFormat. def format - read_inheritable_attribute(:format) || ActiveResource::Formats::XmlFormat + self._format || ActiveResource::Formats::XmlFormat end # Sets the number of seconds after which requests to the REST API should time out. diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index 043a650ea0..ca3db2349e 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -1,8 +1,10 @@ require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/array/extract_options' +require 'active_support/deprecation' # Retained for backward compatibility. Methods are now included in Class. module ClassInheritableAttributes # :nodoc: + DEPRECATION_WARNING_MESSAGE = "class_inheritable_attribute is deprecated, please use class_attribute method instead. Notice their behavior are slightly different, so refer to class_attribute documentation first" end # It is recommended to use class_attribute over methods defined in this file. Please @@ -36,6 +38,7 @@ end # Person.new.hair_colors # => NoMethodError class Class # :nodoc: def class_inheritable_reader(*syms) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE options = syms.extract_options! syms.each do |sym| next if sym.is_a?(Hash) @@ -54,6 +57,7 @@ class Class # :nodoc: end def class_inheritable_writer(*syms) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@ -71,6 +75,7 @@ class Class # :nodoc: end def class_inheritable_array_writer(*syms) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@ -88,6 +93,7 @@ class Class # :nodoc: end def class_inheritable_hash_writer(*syms) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@ -124,6 +130,7 @@ class Class # :nodoc: end def write_inheritable_attribute(key, value) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES) @inheritable_attributes = {} end @@ -141,10 +148,12 @@ class Class # :nodoc: end def read_inheritable_attribute(key) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE inheritable_attributes[key] end def reset_inheritable_attributes + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES end diff --git a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb index b284e5ee1c..020dfce56a 100644 --- a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb +++ b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb @@ -3,9 +3,14 @@ require 'active_support/core_ext/class/inheritable_attributes' class ClassInheritableAttributesTest < Test::Unit::TestCase def setup + ActiveSupport::Deprecation.silenced = true @klass = Class.new end + def teardown + ActiveSupport::Deprecation.silenced = false + end + def test_reader_declaration assert_nothing_raised do @klass.class_inheritable_reader :a -- cgit v1.2.3 From 8796f9a31a77bb2ab97ea6c2cf2036fb6e38497e Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Fri, 19 Nov 2010 18:30:48 +0100 Subject: removed an AR method which wasn't used internally, had no tests, and had no docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8bde1869c7..aee6f3a7eb 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -727,10 +727,6 @@ module ActiveRecord #:nodoc: @arel_engine = @relation = @arel_table = nil end - def reset_column_information_for_all_subclasses#:nodoc: - descendants.each { |klass| klass.reset_column_information } - end - def attribute_method?(attribute) super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, ''))) end -- cgit v1.2.3 From d5779efaf2375466ab557868e586b9dbd942abc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 20 Nov 2010 21:56:08 +0100 Subject: Ensure calling reset session twice does not raise errors. --- actionpack/test/activerecord/active_record_store_test.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index 7c595d1b89..f0fb113860 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -23,6 +23,7 @@ class ActiveRecordStoreTest < ActionDispatch::IntegrationTest def call_reset_session session[:foo] reset_session + reset_session if params[:twice] session[:foo] = "baz" head :ok end @@ -94,6 +95,17 @@ class ActiveRecordStoreTest < ActionDispatch::IntegrationTest end end + def test_calling_reset_session_twice_does_not_raise_errors + with_test_route_set do + get '/call_reset_session', :twice => "true" + assert_response :success + + get '/get_session_value' + assert_response :success + assert_equal 'foo: "baz"', response.body + end + end + def test_setting_session_value_after_session_reset with_test_route_set do get '/set_session_value' -- cgit v1.2.3 From d773ef8e8c819f09ad3b436b11631c26e4ca2ff9 Mon Sep 17 00:00:00 2001 From: Jamison Dance Date: Sat, 20 Nov 2010 14:55:39 -0700 Subject: fix some grammar issues with section 2.5 --- railties/guides/source/active_record_validations_callbacks.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 444a1d4154..59db20c926 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -141,7 +141,7 @@ end h4(#validations_overview-errors). +errors[]+ -To verify whether or not a particular attribute of an object is valid, you can use +errors[:attribute]+ that returns an array with all attribute errors, when there are no errors on the specified attribute, an empty array is returned. +To verify whether or not a particular attribute of an object is valid, you can use +errors[:attribute]+. It returns an array of all the errors for +:attribue+. If there are no errors on the specified attribute, an empty array is returned. This method is only useful _after_ validations have been run, because it only inspects the errors collection and does not trigger validations itself. It's different from the +ActiveRecord::Base#invalid?+ method explained above because it doesn't verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object. -- cgit v1.2.3 From 074782eda2caddb054d20a99e8d167ef8e1eecd2 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 21 Nov 2010 00:01:10 +0100 Subject: copy-edits d773ef8 --- railties/guides/source/active_record_validations_callbacks.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 59db20c926..9ddbe6fadc 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -141,7 +141,7 @@ end h4(#validations_overview-errors). +errors[]+ -To verify whether or not a particular attribute of an object is valid, you can use +errors[:attribute]+. It returns an array of all the errors for +:attribue+. If there are no errors on the specified attribute, an empty array is returned. +To verify whether or not a particular attribute of an object is valid, you can use +errors[:attribute]+. It returns an array of all the errors for +:attribute+. If there are no errors on the specified attribute, an empty array is returned. This method is only useful _after_ validations have been run, because it only inspects the errors collection and does not trigger validations itself. It's different from the +ActiveRecord::Base#invalid?+ method explained above because it doesn't verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object. -- cgit v1.2.3 From 69d47a00c7e43e2431aa1b164a2e6309da1bf118 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 20 Nov 2010 21:00:44 -0500 Subject: use_accept_header is no longer supported --- actionpack/lib/action_dispatch/http/mime_negotiation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index b959aa258e..5bb42cca3a 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -36,7 +36,7 @@ module ActionDispatch # # GET /posts/5.xml | request.format => Mime::XML # GET /posts/5.xhtml | request.format => Mime::HTML - # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of ActionController::Base.use_accept_header + # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first # def format(view_path = []) formats.first -- cgit v1.2.3 From f326221c701e4f9d991e3eadcc793a73795fb218 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 21 Nov 2010 03:22:57 +0100 Subject: Spanish for "project" is "proyecto" --- railties/guides/source/3_0_release_notes.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile index 2d9655fa7a..adb1c755df 100644 --- a/railties/guides/source/3_0_release_notes.textile +++ b/railties/guides/source/3_0_release_notes.textile @@ -271,10 +271,10 @@ end scope 'es' do - resources :projects, :path_names => { :edit => 'cambiar' }, :path => 'projecto' + resources :projects, :path_names => { :edit => 'cambiar' }, :path => 'proyecto' end -# Gives you the edit action with /es/projecto/1/cambiar +# Gives you the edit action with /es/proyecto/1/cambiar * Added +root+ method to the router as a short cut for match '/', :to => path. -- cgit v1.2.3 From 3d377454dbeb9ab9fc0a4d491498c237e33e8d4b Mon Sep 17 00:00:00 2001 From: eparreno Date: Mon, 15 Nov 2010 19:21:40 +0100 Subject: remove old school validations and replaced with new validates method. Pending: fix active_record guide --- .../active_record_validations_callbacks.textile | 18 ++++---- railties/guides/source/i18n.textile | 48 +++++++++++----------- railties/guides/source/migrations.textile | 2 +- railties/guides/source/security.textile | 2 +- railties/guides/source/testing.textile | 2 +- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index f7ef33bc1f..2a6f0715a9 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -96,7 +96,7 @@ To verify whether or not an object is valid, Rails uses the +valid?+ method. You class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true end Person.create(:name => "John Doe").valid? # => true @@ -109,7 +109,7 @@ Note that an object instantiated with +new+ will not report errors even if it's class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true end >> p = Person.new @@ -147,7 +147,7 @@ This method is only useful _after_ validations have been run, because it only in class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true end >> Person.new.errors[:name].any? # => false @@ -355,7 +355,7 @@ This helper validates that the specified attributes are not empty. It uses the + class Person < ActiveRecord::Base - validates_presence_of :name, :login, :email + validates :name, :presence => true, :login, :email end @@ -505,7 +505,7 @@ class Person < ActiveRecord::Base validates_numericality_of :age, :on => :update # the default (validates on both create and update) - validates_presence_of :name, :on => :save + validates :name, :presence => true, :on => :save end @@ -603,7 +603,7 @@ Returns an OrderedHash with all errors. Each key is the attribute name and the v class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true validates_length_of :name, :minimum => 3 end @@ -623,7 +623,7 @@ h4(#working_with_validation_errors-errors-2). +errors[]+ class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true validates_length_of :name, :minimum => 3 end @@ -699,7 +699,7 @@ The +clear+ method is used when you intentionally want to clear all the messages class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true validates_length_of :name, :minimum => 3 end @@ -723,7 +723,7 @@ The +size+ method returns the total number of error messages for the object. class Person < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true validates_length_of :name, :minimum => 3 validates_presence_of :email end diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 825185c832..12901e4dac 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -672,11 +672,11 @@ Active Record validation error messages can also be translated easily. Active Re This gives you quite powerful means to flexibly adjust your messages to your application's needs. -Consider a User model with a +validates_presence_of+ validation for the name attribute like this: +Consider a User model with a validation for the name attribute like this: class User < ActiveRecord::Base - validates_presence_of :name + validates :name, :presence => true end @@ -706,7 +706,7 @@ For example, you might have an Admin model inheriting from User: class Admin < User - validates_presence_of :name + validates :name, :presence => true end @@ -733,27 +733,27 @@ So, for example, instead of the default error message +"can not be blank"+ you c * +count+, where available, can be used for pluralization if present: |_. validation |_.with option |_.message |_.interpolation| -| validates_confirmation_of | - | :confirmation | -| -| validates_acceptance_of | - | :accepted | -| -| validates_presence_of | - | :blank | -| -| validates_length_of | :within, :in | :too_short | count| -| validates_length_of | :within, :in | :too_long | count| -| validates_length_of | :is | :wrong_length | count| -| validates_length_of | :minimum | :too_short | count| -| validates_length_of | :maximum | :too_long | count| -| validates_uniqueness_of | - | :taken | -| -| validates_format_of | - | :invalid | -| -| validates_inclusion_of | - | :inclusion | -| -| validates_exclusion_of | - | :exclusion | -| -| validates_associated | - | :invalid | -| -| validates_numericality_of | - | :not_a_number | -| -| validates_numericality_of | :greater_than | :greater_than | count| -| validates_numericality_of | :greater_than_or_equal_to | :greater_than_or_equal_to | count| -| validates_numericality_of | :equal_to | :equal_to | count| -| validates_numericality_of | :less_than | :less_than | count| -| validates_numericality_of | :less_than_or_equal_to | :less_than_or_equal_to | count| -| validates_numericality_of | :odd | :odd | -| -| validates_numericality_of | :even | :even | -| +| confirmation | - | :confirmation | -| +| acceptance | - | :accepted | -| +| presence | - | :blank | -| +| length | :within, :in | :too_short | count| +| length | :within, :in | :too_long | count| +| length | :is | :wrong_length | count| +| length | :minimum | :too_short | count| +| length | :maximum | :too_long | count| +| uniqueness | - | :taken | -| +| format | - | :invalid | -| +| inclusion | - | :inclusion | -| +| exclusion | - | :exclusion | -| +| associated | - | :invalid | -| +| numericality | - | :not_a_number | -| +| numericality | :greater_than | :greater_than | count| +| numericality | :greater_than_or_equal_to | :greater_than_or_equal_to | count| +| numericality | :equal_to | :equal_to | count| +| numericality | :less_than | :less_than | count| +| numericality | :less_than_or_equal_to | :less_than_or_equal_to | count| +| numericality | :odd | :odd | -| +| numericality | :even | :even | -| h5. Translations for the Active Record +error_messages_for+ Helper diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index a9c3d51752..0d13fbc10a 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -584,7 +584,7 @@ h3. Active Record and Referential Integrity The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used. -Validations such as +validates_uniqueness_of+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints. +Validations such as +validates :foreign_key, :uniqueness => true+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints. Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":http://github.com/harukizaemon/redhillonrails/tree/master/foreign_key_migrations/ which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+). diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 693645b202..6f766430c1 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -550,7 +550,7 @@ Ruby uses a slightly different approach than many other languages to match the e class File < ActiveRecord::Base - validates_format_of :name, :with => /^[\w\.\-\+]+$/ + validates :name, format => /^[\w\.\-\+]+$/ end diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index 3d4f31cc5d..c292a5d83b 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -315,7 +315,7 @@ Now to get this test to pass we can add a model level validation for the _title_ class Post < ActiveRecord::Base - validates_presence_of :title + validates :title, :presence => true end -- cgit v1.2.3 From 4a51328ae8d51f8d1a637780d5b82fd27261758f Mon Sep 17 00:00:00 2001 From: zhengjia Date: Tue, 16 Nov 2010 09:26:21 -0600 Subject: Minor fix on Rails:Railtie documentation --- railties/lib/rails/railtie.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index c76bc83377..030a838dc1 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -97,7 +97,7 @@ module Rails # If your railtie has rake tasks, you can tell Rails to load them through the method # rake tasks: # - # class MyRailtie < Railtie + # class MyRailtie < Rails::Railtie # rake_tasks do # load "path/to/my_railtie.tasks" # end @@ -107,7 +107,7 @@ module Rails # your generators at a different location, you can specify in your Railtie a block which # will load them during normal generators lookup: # - # class MyRailtie < Railtie + # class MyRailtie < Rails::Railtie # generators do # require "path/to/my_railtie_generator" # end -- cgit v1.2.3 From 3c645536448e1ba1fa81160162a1eefe03bb6787 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Tue, 16 Nov 2010 22:25:20 +0100 Subject: i18n guide: it's activerecord.errors.messages.record_invalid (instead of 'invalid'), and messagges typo --- railties/guides/source/i18n.textile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 12901e4dac..4f6ae5fbd2 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -464,24 +464,24 @@ I18n.t 'message' The +translate+ method also takes a +:scope+ option which can contain one or more additional keys that will be used to specify a “namespace” or scope for a translation key: -I18n.t :invalid, :scope => [:activerecord, :errors, :messages] +I18n.t :record_invalid, :scope => [:activerecord, :errors, :messages] -This looks up the +:invalid+ message in the Active Record error messages. +This looks up the +:record_invalid+ message in the Active Record error messages. Additionally, both the key and scopes can be specified as dot-separated keys as in: -I18n.translate :"activerecord.errors.messages.invalid" +I18n.translate "activerecord.errors.messages.record_invalid" Thus the following calls are equivalent: -I18n.t 'activerecord.errors.messages.invalid' -I18n.t 'errors.messages.invalid', :scope => :active_record -I18n.t :invalid, :scope => 'activerecord.errors.messages' -I18n.t :invalid, :scope => [:activerecord, :errors, :messages] +I18n.t 'activerecord.errors.messages.record_invalid' +I18n.t 'errors.messages.record_invalid', :scope => :active_record +I18n.t :record_invalid, :scope => 'activerecord.errors.messages' +I18n.t :record_invalid, :scope => [:activerecord, :errors, :messages] h5. Defaults @@ -697,7 +697,7 @@ activerecord.errors.models.user.attributes.name.blank activerecord.errors.models.user.blank activerecord.errors.messages.blank errors.attributes.name.blank -errors.messagges.blank +errors.messages.blank When your models are additionally using inheritance then the messages are looked up in the inheritance chain. @@ -719,7 +719,7 @@ activerecord.errors.models.user.attributes.title.blank activerecord.errors.models.user.blank activerecord.errors.messages.blank errors.attributes.title.blank -errors.messagges.blank +errors.messages.blank This way you can provide special translations for various error messages at different points in your models inheritance chain and in the attributes, models, or default scopes. -- cgit v1.2.3 From cbea139130f0d5794a7756fcf93aed48c033ce29 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Tue, 16 Nov 2010 22:35:11 +0100 Subject: i18n guide: remove link to external page about 'How to encode the current locale in the URL' as it no longer exists --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 4f6ae5fbd2..31b6d72041 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -253,7 +253,7 @@ match '/:locale' => 'dashboard#index' Do take special care about the *order of your routes*, so this route declaration does not "eat" other ones. (You may want to add it directly before the +root :to+ declaration.) -IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. See also the page "How to encode the current locale in the URL":http://rails-i18n.org/wiki/wikipages/how-to-encode-the-current-locale-in-the-url in the Rails i18n Wiki. +IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. h4. Setting the Locale from the Client Supplied Information -- cgit v1.2.3 From f044d38192dab10ccdf6556e2bb3b9c90a912fbe Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Wed, 17 Nov 2010 09:24:47 +0100 Subject: i18n guide: this is not longer a problem --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 31b6d72041..4ae11b1b7b 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -253,7 +253,7 @@ match '/:locale' => 'dashboard#index' Do take special care about the *order of your routes*, so this route declaration does not "eat" other ones. (You may want to add it directly before the +root :to+ declaration.) -IMPORTANT: This solution has currently one rather big *downside*. Due to the _default_url_options_ implementation, you have to pass the +:id+ option explicitly, like this: +link_to 'Show', book_url(:id => book)+ and not depend on Rails' magic in code like +link_to 'Show', book+. If this should be a problem, have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. +NOTE: Have a look at two plugins which simplify work with routes in this way: Sven Fuchs's "routing_filter":http://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's "translate_routes":http://github.com/raul/translate_routes/tree/master. h4. Setting the Locale from the Client Supplied Information -- cgit v1.2.3 From c8ab3992f85569e43055b9e57c67324f5d5088cd Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 17 Nov 2010 21:46:55 -0500 Subject: unscoped takes care of named_scopes too --- activerecord/lib/active_record/base.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index aee6f3a7eb..aa028c6f36 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -852,8 +852,7 @@ module ActiveRecord #:nodoc: # limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } # - # It is recommended to use block form of unscoped because chaining unscoped with named_scope - # does not work. Assuming that published is a named_scope following two statements are same. + # Assuming that published is a named_scope following two statements are same. # # Post.unscoped.published # Post.published -- cgit v1.2.3 From f5a51a778804f7a48b8f43e591ad3bc6b828867e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Hackin?= Date: Thu, 18 Nov 2010 08:25:38 -0200 Subject: Fix code for customize the error messages html adding a .html_safe of 8.3 section --- railties/guides/source/active_record_validations_callbacks.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 2a6f0715a9..fddd715feb 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -824,10 +824,10 @@ Here is a simple example where we change the Rails behaviour to always display t ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| if instance.error_message.kind_of?(Array) %(#{html_tag}  - #{instance.error_message.join(',')}) + #{instance.error_message.join(',')}).html_safe else %(#{html_tag}  - #{instance.error_message}) + #{instance.error_message}).html_safe end end -- cgit v1.2.3 From 925aeed4b61141cc62dec42b0b577158f077ffc1 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Fri, 19 Nov 2010 17:25:37 +0100 Subject: i18n guide: fix external link to rack locale --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 4ae11b1b7b..1a83b84004 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -278,7 +278,7 @@ def extract_locale_from_accept_language_header end -Of course, in a production environment you would need much more robust code, and could use a plugin such as Iain Hecker's "http_accept_language":http://github.com/iain/http_accept_language/tree/master or even Rack middleware such as Ryan Tomayko's "locale":http://github.com/rtomayko/rack-contrib/blob/master/lib/rack/locale.rb. +Of course, in a production environment you would need much more robust code, and could use a plugin such as Iain Hecker's "http_accept_language":http://github.com/iain/http_accept_language/tree/master or even Rack middleware such as Ryan Tomayko's "locale":http://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb. h5. Using GeoIP (or Similar) Database -- cgit v1.2.3 From f975f35147d7d6df7f6094bfd60e3b7dd45aa2b3 Mon Sep 17 00:00:00 2001 From: nosolopau Date: Sat, 20 Nov 2010 04:23:31 -0800 Subject: Spelling mistake: "Projecto" instead of "projeto" --- railties/guides/source/3_0_release_notes.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile index 9c08c9fa0a..2d9655fa7a 100644 --- a/railties/guides/source/3_0_release_notes.textile +++ b/railties/guides/source/3_0_release_notes.textile @@ -271,10 +271,10 @@ end scope 'es' do - resources :projects, :path_names => { :edit => 'cambiar' }, :path => 'projeto' + resources :projects, :path_names => { :edit => 'cambiar' }, :path => 'projecto' end -# Gives you the edit action with /es/projeto/1/cambiar +# Gives you the edit action with /es/projecto/1/cambiar * Added +root+ method to the router as a short cut for match '/', :to => path. -- cgit v1.2.3 From e65c23dc3e775be3162de25548f3192939ff3da3 Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Sun, 21 Nov 2010 01:36:27 +0700 Subject: Mention the case that `render` will return nil if given `:collection` is nil or empty when rendering partial. --- actionpack/lib/action_view/partials.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 844c3e9572..cd3f130dc4 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -75,6 +75,11 @@ module ActionView # # <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %> # + # If the given :collection is nil or empty, render will return nil. This will allow you + # to specify a text which will displayed instead by using this form: + # + # <%= render(:partial => "ad", :collection => @advertisements) || "There's no ad to be displayed" %> + # # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also # just keep domain objects, like Active Records, in there. # -- cgit v1.2.3 From ffed9db2692475a6e24354336f884991075906c5 Mon Sep 17 00:00:00 2001 From: Jamison Dance Date: Sat, 20 Nov 2010 14:55:39 -0700 Subject: fix some grammar issues with section 2.5 --- railties/guides/source/active_record_validations_callbacks.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index fddd715feb..940ee93cc1 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -141,7 +141,7 @@ end h4(#validations_overview-errors). +errors[]+ -To verify whether or not a particular attribute of an object is valid, you can use +errors[:attribute]+ that returns an array with all attribute errors, when there are no errors on the specified attribute, an empty array is returned. +To verify whether or not a particular attribute of an object is valid, you can use +errors[:attribute]+. It returns an array of all the errors for +:attribue+. If there are no errors on the specified attribute, an empty array is returned. This method is only useful _after_ validations have been run, because it only inspects the errors collection and does not trigger validations itself. It's different from the +ActiveRecord::Base#invalid?+ method explained above because it doesn't verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object. -- cgit v1.2.3 From a03a3b7c3130a93ca6385a1f89ce553d00ca3c49 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 21 Nov 2010 00:01:10 +0100 Subject: copy-edits d773ef8 --- railties/guides/source/active_record_validations_callbacks.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 940ee93cc1..9ce0423244 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -141,7 +141,7 @@ end h4(#validations_overview-errors). +errors[]+ -To verify whether or not a particular attribute of an object is valid, you can use +errors[:attribute]+. It returns an array of all the errors for +:attribue+. If there are no errors on the specified attribute, an empty array is returned. +To verify whether or not a particular attribute of an object is valid, you can use +errors[:attribute]+. It returns an array of all the errors for +:attribute+. If there are no errors on the specified attribute, an empty array is returned. This method is only useful _after_ validations have been run, because it only inspects the errors collection and does not trigger validations itself. It's different from the +ActiveRecord::Base#invalid?+ method explained above because it doesn't verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object. -- cgit v1.2.3 From 7d5b097357c3bbfb86c95271d1bdbe5148a01069 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 20 Nov 2010 21:00:44 -0500 Subject: use_accept_header is no longer supported --- actionpack/lib/action_dispatch/http/mime_negotiation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index b959aa258e..5bb42cca3a 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -36,7 +36,7 @@ module ActionDispatch # # GET /posts/5.xml | request.format => Mime::XML # GET /posts/5.xhtml | request.format => Mime::HTML - # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of ActionController::Base.use_accept_header + # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first # def format(view_path = []) formats.first -- cgit v1.2.3 From 7c51d1fcf9dc504c2dfd6e7184bbe8186f09819d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 21 Nov 2010 03:22:57 +0100 Subject: Spanish for "project" is "proyecto" --- railties/guides/source/3_0_release_notes.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile index 2d9655fa7a..adb1c755df 100644 --- a/railties/guides/source/3_0_release_notes.textile +++ b/railties/guides/source/3_0_release_notes.textile @@ -271,10 +271,10 @@ end scope 'es' do - resources :projects, :path_names => { :edit => 'cambiar' }, :path => 'projecto' + resources :projects, :path_names => { :edit => 'cambiar' }, :path => 'proyecto' end -# Gives you the edit action with /es/projecto/1/cambiar +# Gives you the edit action with /es/proyecto/1/cambiar * Added +root+ method to the router as a short cut for match '/', :to => path. -- cgit v1.2.3 From 15fd29b3eaf9e2e202542fcf9a582b0180fc2664 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 21 Nov 2010 10:17:30 +0100 Subject: documents <%== in the AS guide --- railties/guides/source/active_support_core_extensions.textile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index d41d8b6c3d..7333a81cf9 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -1189,6 +1189,12 @@ To insert something verbatim use the +raw+ helper rather than calling +html_safe <%= raw @cms.current_template %> <%# inserts @cms.current_template as is %> +or, equivalently, use <%==: + + +<%== @cms.current_template %> <%# inserts @cms.current_template as is %> + + The +raw+ helper calls +html_safe+ for you: -- cgit v1.2.3 From 799a6fa047aa44ab4813dbf9328941b6d620e9b6 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 21 Nov 2010 10:25:23 +0100 Subject: realigns a series of hash arrows --- .../test/controller/new_base/render_template_test.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index 9899036fe8..584f2d772c 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -4,16 +4,16 @@ module RenderTemplate class WithoutLayoutController < ActionController::Base self.view_paths = [ActionView::FixtureResolver.new( - "test/basic.html.erb" => "Hello from basic.html.erb", - "shared.html.erb" => "Elastica", - "locals.html.erb" => "The secret is <%= secret %>", - "xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend", - "with_raw.html.erb" => "Hello <%=raw 'this is raw' %>", - "with_implicit_raw.html.erb"=> "Hello <%== 'this is also raw' %>", - "test/with_json.html.erb" => "<%= render :template => 'test/with_json.json' %>", - "test/with_json.json.erb" => "<%= render :template => 'test/final' %>", - "test/final.json.erb" => "{ final: json }", - "test/with_error.html.erb" => "<%= idontexist %>" + "test/basic.html.erb" => "Hello from basic.html.erb", + "shared.html.erb" => "Elastica", + "locals.html.erb" => "The secret is <%= secret %>", + "xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend", + "with_raw.html.erb" => "Hello <%=raw 'this is raw' %>", + "with_implicit_raw.html.erb" => "Hello <%== 'this is also raw' %>", + "test/with_json.html.erb" => "<%= render :template => 'test/with_json.json' %>", + "test/with_json.json.erb" => "<%= render :template => 'test/final' %>", + "test/final.json.erb" => "{ final: json }", + "test/with_error.html.erb" => "<%= idontexist %>" )] def index -- cgit v1.2.3 From 16d7ba0698ef28efab31940ba4d3aed237814c7e Mon Sep 17 00:00:00 2001 From: Matthew Rudy Jacobs Date: Sun, 21 Nov 2010 20:45:01 +0800 Subject: Cleaner way to extract the Postgres version --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 7dd81abc7e..a4b1aa7154 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -953,8 +953,12 @@ module ActiveRecord else # Mimic PGconn.server_version behavior begin - query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/ - ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i + if query('SELECT version()')[0][0] =~ /PostgreSQL ([0-9.]+)/ + major, minor, tiny = $1.split(".") + (major.to_i * 10000) + (minor.to_i * 100) + tiny.to_i + else + 0 + end rescue 0 end -- cgit v1.2.3 From 51007f1dbafe029ed85b2a296736a00e6b24ec58 Mon Sep 17 00:00:00 2001 From: Brian Alexander Date: Sun, 21 Nov 2010 19:54:05 -0800 Subject: Previous version inaccurately suggested that resources :posts, :path => "/admin" would route "/admin/posts" to the PostsController but it actually routed "/admin" to the PostsController --- railties/guides/source/routing.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 918279f9eb..2f5c88b8c3 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -194,7 +194,7 @@ end or, for a single case -resources :posts, :path => "/admin" +resources :posts, :path => "/admin/posts" In each of these cases, the named routes remain the same as if you did not use +scope+. In the last case, the following paths map to +PostsController+: -- cgit v1.2.3 From f821055dc7bead5ab348f63279cd4321d4d9c3c8 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Sun, 21 Nov 2010 02:20:09 +0100 Subject: Do not create table_name_prefix method on isolated module if the method already exists. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/engine.rb | 6 ++++-- railties/test/railties/engine_test.rb | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 62fb781c19..85fa4424c4 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -363,8 +363,10 @@ module Rails _railtie end - define_method(:table_name_prefix) do - "#{name}_" + unless mod.respond_to?(:table_name_prefix) + define_method(:table_name_prefix) do + "#{name}_" + end end end end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 701b6816c8..7548c6318e 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -683,5 +683,24 @@ module RailtiesTest app_generators = Rails.application.config.generators.options[:rails] assert_equal :test_unit , app_generators[:test_framework] end + + test "do not create table_name_prefix method if it already exists" do + @plugin.write "lib/bukkits.rb", <<-RUBY + module Bukkits + def self.table_name_prefix + "foo" + end + + class Engine < ::Rails::Engine + isolate_namespace(Bukkits) + end + end + RUBY + + boot_rails + require "#{rails_root}/config/environment" + + assert_equal "foo", Bukkits.table_name_prefix + end end end -- cgit v1.2.3 From 115abd52b6bf6801dce7c3fbcb0e29d4213a5bb2 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Sun, 21 Nov 2010 02:47:20 +0100 Subject: Ensure that app generator is not loaded twice to get rid of warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/generators.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 27a4007c20..66c4088a68 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -302,6 +302,7 @@ module Rails $LOAD_PATH.each do |base| Dir[File.join(base, "{rails/generators,generators}", "**", "*_generator.rb")].each do |path| begin + path = path.sub("#{base}/", "") require path rescue Exception => e # No problem -- cgit v1.2.3 From 8c17d30d687b0b9719d6998b1dc8144490132b8d Mon Sep 17 00:00:00 2001 From: "David N. Welton" Date: Mon, 22 Nov 2010 15:09:58 +0100 Subject: Slightly more natural sounding phrase. --- railties/guides/source/generators.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index c0d3116fe4..e0796d2f72 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -201,7 +201,7 @@ config.generators do |g| end -If we generate another resource with the scaffold generator, we can notice that neither stylesheets nor fixtures are created anymore. If you want to customize it further, for example to use DataMapper and RSpec instead of Active Record and TestUnit, it's just a matter of adding their gems to your application and configuring your generators. +If we generate another resource with the scaffold generator, we can see that neither stylesheets nor fixtures are created anymore. If you want to customize it further, for example to use DataMapper and RSpec instead of Active Record and TestUnit, it's just a matter of adding their gems to your application and configuring your generators. To demonstrate this, we are going to create a new helper generator that simply adds some instance variable readers. First, we create a generator: -- cgit v1.2.3 From 2515ad8a3e03402c1d5a5f9b46c48a3a9bf75081 Mon Sep 17 00:00:00 2001 From: Pavel Gorbokon Date: Mon, 22 Nov 2010 16:37:33 +0200 Subject: Fix ruby syntax errors in railties/guides docs --- railties/guides/source/active_record_validations_callbacks.textile | 2 +- railties/guides/source/security.textile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 9ce0423244..0824ba450c 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -355,7 +355,7 @@ This helper validates that the specified attributes are not empty. It uses the + class Person < ActiveRecord::Base - validates :name, :presence => true, :login, :email + validates :name, :login, :email, :presence => true end diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 6f766430c1..5b24d8c8e3 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -550,7 +550,7 @@ Ruby uses a slightly different approach than many other languages to match the e class File < ActiveRecord::Base - validates :name, format => /^[\w\.\-\+]+$/ + validates :name, :format => /^[\w\.\-\+]+$/ end -- cgit v1.2.3 From 0598bd4d2db332e05fb4189169441fb1ad12e9b1 Mon Sep 17 00:00:00 2001 From: "David N. Welton" Date: Mon, 22 Nov 2010 16:05:53 +0100 Subject: Explain that NamedBase makes the variable 'name' available to the script. --- railties/guides/source/generators.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index e0796d2f72..99c34ed30f 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -92,7 +92,7 @@ class InitializerGenerator < Rails::Generators::NamedBase end -First, notice that we are inheriting from +Rails::Generators::NamedBase+ instead of +Rails::Generators::Base+. This means that our generator expects at least one argument, which will be the name of the initializer. +First, notice that we are inheriting from +Rails::Generators::NamedBase+ instead of +Rails::Generators::Base+. This means that our generator expects at least one argument, which will be the name of the initializer, and will be available in our code in the variable +name+. We can see that by invoking the description of this new generator (don't forget to delete the old generator file): -- cgit v1.2.3 From 559c4292bcd132e87b50aa8f0807f2684cf234b8 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 22 Nov 2010 14:07:28 -0200 Subject: Remove unneeded metaprogramming and method generation in favor of direct definition --- .../lib/action_controller/metal/renderers.rb | 34 +++++++--------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index f9b226b7c9..41bcae75c9 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -15,30 +15,12 @@ module ActionController end module ClassMethods - def _write_render_options - renderers = _renderers.map do |name, value| - <<-RUBY_EVAL - if options.key?(:#{name}) - _process_options(options) - return _render_option_#{name}(options.delete(:#{name}), options) - end - RUBY_EVAL - end - - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def _handle_render_options(options) - #{renderers.join} - end - RUBY_EVAL - end - def use_renderers(*args) new = _renderers.dup args.each do |key| new[key] = RENDERERS[key] end self._renderers = new.freeze - _write_render_options end alias use_renderer use_renderers end @@ -47,11 +29,20 @@ module ActionController _handle_render_options(options) || super end + def _handle_render_options(options) + _renderers.each do |name, value| + if options.key?(name.to_sym) + _process_options(options) + return send("_render_option_#{name}", options.delete(name.to_sym), options) + end + end + nil + end + RENDERERS = {} def self.add(key, &block) define_method("_render_option_#{key}", &block) RENDERERS[key] = block - All._write_render_options end module All @@ -61,13 +52,8 @@ module ActionController INCLUDED = [] included do self._renderers = RENDERERS - _write_render_options INCLUDED << self end - - def self._write_render_options - INCLUDED.each(&:_write_render_options) - end end add :json do |json, options| -- cgit v1.2.3 From 6e559168dc68a32bdbdf87e26ebfcb464be7e886 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 22 Nov 2010 18:47:47 -0200 Subject: There's no need for ternary op here --- actionpack/lib/action_controller/metal/mime_responds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index f7dd0dcb69..6fe1f4ece5 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -291,7 +291,7 @@ module ActionController #:nodoc: alias :all :any def custom(mime_type, &block) - mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s) + mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type) @order << mime_type @responses[mime_type] ||= block end -- cgit v1.2.3 From c937ddb5cec87168c4eb0d6c3030f771e80c2e72 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 22 Nov 2010 19:00:05 -0200 Subject: Remove unused constant --- actionpack/lib/action_controller/metal/renderers.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 41bcae75c9..67cf08445d 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -49,10 +49,8 @@ module ActionController extend ActiveSupport::Concern include Renderers - INCLUDED = [] included do self._renderers = RENDERERS - INCLUDED << self end end -- cgit v1.2.3 From 61950a4b05ce1b5640ac3f3720f9a3368ce95a29 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 02:21:57 -0500 Subject: current code ignores http header "Accept" if it has ....,*/* . MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible to a device to send request such that */* appear at the beginning of the "Accept" header. This patch ensures that "Accept" header is ignored for such cases too. Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/mime_negotiation.rb | 2 +- actionpack/test/controller/mime_responds_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 5bb42cca3a..afce4d9651 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -48,7 +48,7 @@ module ActionDispatch @env["action_dispatch.request.formats"] ||= if parameters[:format] Array(Mime[parameters[:format]]) - elsif xhr? || (accept && accept !~ /,\s*\*\/\*/) + elsif xhr? || (accept && accept !~ /,\s*\*\/\*|\*\/\*,/) accepts else [Mime::HTML] diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index b6ce9f7d34..c68b3ab13f 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -216,6 +216,12 @@ class RespondToControllerTest < ActionController::TestCase assert_response 406 end + def test_json_or_yaml_with_leading_star_star + @request.accept = "*/*, application/json" + get :json_xml_or_html + assert_equal 'HTML', @response.body + end + def test_json_or_yaml xhr :get, :json_or_yaml assert_equal 'JSON', @response.body -- cgit v1.2.3 From f5fba917f8dbf45fcbf04b4b26cf54eb3b300057 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 08:53:47 -0500 Subject: failing test for #6022 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/test/controller/mime_responds_test.rb | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index c68b3ab13f..2de9b28e68 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -2,6 +2,14 @@ require 'abstract_unit' require 'controller/fake_models' require 'active_support/core_ext/hash/conversions' +class StarStarMimeController < ActionController::Base + layout nil + + def index + render + end +end + class RespondToController < ActionController::Base layout :set_layout @@ -160,6 +168,32 @@ class RespondToController < ActionController::Base end end +class StarStarMimeControllerTest < ActionController::TestCase + tests StarStarMimeController + + def setup; super; end + def teardown; super; end + + def test_javascript_with_format + @request.accept = "text/javascript" + get :index, :format => 'js' + assert_match "function addition(a,b){ return a+b; }", @response.body + end + + def test_javascript_with_no_format + @request.accept = "text/javascript" + get :index + assert_match "function addition(a,b){ return a+b; }", @response.body + end + + def test_javascript_with_no_format_only_star_star + @request.accept = "*/*" + get :index + assert_match "function addition(a,b){ return a+b; }", @response.body + end + +end + class RespondToControllerTest < ActionController::TestCase tests RespondToController -- cgit v1.2.3 From eb6ccc9953a5e952737174995b5230f0b2c56b1f Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 08:58:01 -0500 Subject: do not assume that there is no space between leading */* and comma MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/mime_negotiation.rb | 2 +- actionpack/test/controller/mime_responds_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index afce4d9651..2b401114f0 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -48,7 +48,7 @@ module ActionDispatch @env["action_dispatch.request.formats"] ||= if parameters[:format] Array(Mime[parameters[:format]]) - elsif xhr? || (accept && accept !~ /,\s*\*\/\*|\*\/\*,/) + elsif xhr? || (accept && accept !~ /,\s*\*\/\*|\*\/\*\s*,/) accepts else [Mime::HTML] diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 2de9b28e68..7a799411db 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -254,6 +254,10 @@ class RespondToControllerTest < ActionController::TestCase @request.accept = "*/*, application/json" get :json_xml_or_html assert_equal 'HTML', @response.body + + @request.accept = "*/* , application/json" + get :json_xml_or_html + assert_equal 'HTML', @response.body end def test_json_or_yaml -- cgit v1.2.3 From cbb2651cc0e102b8f519acc41035adc32a8cc01b Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 08:58:49 -0500 Subject: add the fixture file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/test/fixtures/star_star_mime/index.js.erb | 1 + 1 file changed, 1 insertion(+) create mode 100644 actionpack/test/fixtures/star_star_mime/index.js.erb diff --git a/actionpack/test/fixtures/star_star_mime/index.js.erb b/actionpack/test/fixtures/star_star_mime/index.js.erb new file mode 100644 index 0000000000..4da4181f56 --- /dev/null +++ b/actionpack/test/fixtures/star_star_mime/index.js.erb @@ -0,0 +1 @@ +function addition(a,b){ return a+b; } -- cgit v1.2.3 From d04a21b36281a0608cdd72c02e4df3b1136f8887 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 09:07:44 -0500 Subject: declare regex as a constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/mime_negotiation.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 2b401114f0..68ba1a81b5 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -42,13 +42,15 @@ module ActionDispatch formats.first end + BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/ + def formats accept = @env['HTTP_ACCEPT'] @env["action_dispatch.request.formats"] ||= if parameters[:format] Array(Mime[parameters[:format]]) - elsif xhr? || (accept && accept !~ /,\s*\*\/\*|\*\/\*\s*,/) + elsif xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS) accepts else [Mime::HTML] -- cgit v1.2.3 From b798a59bd582d8590d4fb8df44885d41cd5299f3 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 11:27:26 -0500 Subject: test for text/*, application/* and image/* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/test/dispatch/mime_type_test.rb | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 4c2b95550c..48755e6b5f 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -6,10 +6,36 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse single" do Mime::LOOKUP.keys.each do |mime_type| - assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type) + unless image_type == 'image/*' + assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type) + end end end + test "parse text with trailing star star" do + accept = "text/*" + expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS + parsed = Mime::Type.parse(accept) + assert_equal 9, parsed.size + assert_equal expect, parsed + end + + test "parse application with trailing star star" do + accept = "application/*" + expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::R + parsed = Mime::Type.parse(accept) + assert_equal 9, parsed.size + assert_equal expect, parsed + end + + test "parse image with trailing star star" do + accept = "image/*" + parsed = Mime::Type.parse(accept) + assert_equal 2, parsed.size + assert parsed.include?(Mime::PNG) + assert_equal 'image/*', parsed[1].instance_variable_get('@string') + end + test "parse without q" do accept = "text/xml,application/xhtml+xml,text/yaml,application/xml,text/html,image/png,text/plain,application/pdf,*/*" expect = [Mime::HTML, Mime::XML, Mime::YAML, Mime::PNG, Mime::TEXT, Mime::PDF, Mime::ALL] -- cgit v1.2.3 From 6f6e754bac4c78f657feb0ea119447546aa87197 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 11:34:37 -0500 Subject: implement code that handles text/*, appplication/*, and image/* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/mime_type.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 8f1c9b6691..3b5c1d50f2 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -103,9 +103,14 @@ module Mime ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last } end + def parse(accept_header) if accept_header !~ /,/ - [Mime::Type.lookup(accept_header)] + if result = Regexp.new('(\w+)\/\*').match(accept_header) + parse_data_with_trailing_star(result[1]) + else + [Mime::Type.lookup(accept_header)] + end else # keep track of creation order to keep the subsequent sort stable list = [] @@ -160,6 +165,16 @@ module Mime list end end + + # input: 'text' + # returend value: [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT] + # + # input: 'application' + # returend value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM + def parse_data_with_trailing_star(input) + keys = Mime::LOOKUP.keys.select{|k| Regexp.new(input).match(k)} + Mime::LOOKUP.select {|k,_| keys.include?(k)}.collect{|i| i[1]}.inject([]){|all,e| all.include?(e) ? all : all << e} + end end def initialize(string, symbol = nil, synonyms = []) -- cgit v1.2.3 From 4154c659feee47f246a162e2086bf07ad149ec6d Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 11:35:24 -0500 Subject: fix test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/test/dispatch/mime_type_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 48755e6b5f..bea93adc19 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -6,7 +6,7 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse single" do Mime::LOOKUP.keys.each do |mime_type| - unless image_type == 'image/*' + unless mime_type == 'image/*' assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type) end end @@ -14,7 +14,7 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse text with trailing star star" do accept = "text/*" - expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS + expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT] parsed = Mime::Type.parse(accept) assert_equal 9, parsed.size assert_equal expect, parsed @@ -22,7 +22,7 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse application with trailing star star" do accept = "application/*" - expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::R + expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::PDF, Mime::URL_ENCODED_FORM] parsed = Mime::Type.parse(accept) assert_equal 9, parsed.size assert_equal expect, parsed -- cgit v1.2.3 From c9147ebfa06023248b5e738f0928973ddc084da7 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 11:36:36 -0500 Subject: cleanup Mime::LOOKUP after registering image/gif MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/test/dispatch/mime_type_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index bea93adc19..ab5bbe3a19 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -71,6 +71,7 @@ class MimeTypeTest < ActiveSupport::TestCase end ensure Mime.module_eval { remove_const :GIF if const_defined?(:GIF) } + Mime::LOOKUP.reject!{|key,_| key == 'image/gif'} end end -- cgit v1.2.3 From 23ea19e20d6f7df762635b1c6d83369922d5ad7b Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 11:39:58 -0500 Subject: rename star star to just one star MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/test/dispatch/mime_type_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index ab5bbe3a19..3a7ab740f4 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -12,7 +12,7 @@ class MimeTypeTest < ActiveSupport::TestCase end end - test "parse text with trailing star star" do + test "parse text with trailing star" do accept = "text/*" expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT] parsed = Mime::Type.parse(accept) @@ -20,7 +20,7 @@ class MimeTypeTest < ActiveSupport::TestCase assert_equal expect, parsed end - test "parse application with trailing star star" do + test "parse application with trailing star" do accept = "application/*" expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::PDF, Mime::URL_ENCODED_FORM] parsed = Mime::Type.parse(accept) @@ -28,7 +28,7 @@ class MimeTypeTest < ActiveSupport::TestCase assert_equal expect, parsed end - test "parse image with trailing star star" do + test "parse image with trailing star" do accept = "image/*" parsed = Mime::Type.parse(accept) assert_equal 2, parsed.size -- cgit v1.2.3 From cdbbf6fd6bef3f286503859c585ada8fe66a3875 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 11:51:43 -0500 Subject: move the mime registration code to setup so that it could be cleaned up on teardown. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the test code test/controller/mime_responds_test.rb impacts test/dispatch/mime_type_test.rb. dispatch/mime_type_test.rb runs independently fine but when run as part of rake breaks because of new mime types registered in controller/mime_responds_test.rb Signed-off-by: José Valim --- actionpack/test/controller/mime_responds_test.rb | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 7a799411db..0da051822a 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -97,7 +97,6 @@ class RespondToController < ActionController::Base end end - Mime::Type.register("text/x-mobile", :mobile) def custom_constant_handling respond_to do |type| @@ -134,7 +133,6 @@ class RespondToController < ActionController::Base end end - Mime::Type.register_alias("text/html", :iphone) def iphone_with_html_response_type request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone" @@ -200,10 +198,16 @@ class RespondToControllerTest < ActionController::TestCase def setup super @request.host = "www.example.com" + Mime::Type.register_alias("text/html", :iphone) + Mime::Type.register("text/x-mobile", :mobile) end def teardown super + Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } + Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) } + Mime::LOOKUP.reject!{|key,_| key == 'text/x-mobile'} + Mime::LOOKUP.reject!{|key,_| key == 'text/iphone'} end def test_html @@ -616,6 +620,10 @@ class RespondWithControllerTest < ActionController::TestCase def teardown super + Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } + Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) } + Mime::LOOKUP.reject!{|key,_| key == 'text/x-mobile'} + Mime::LOOKUP.reject!{|key,_| key == 'text/iphone'} end def test_using_resource @@ -996,6 +1004,15 @@ class MimeControllerLayoutsTest < ActionController::TestCase def setup super @request.host = "www.example.com" + Mime::Type.register_alias("text/html", :iphone) + end + + def teardown + super + Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } + Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) } + Mime::LOOKUP.reject!{|key,_| key == 'text/x-mobile'} + Mime::LOOKUP.reject!{|key,_| key == 'text/iphone'} end def test_missing_layout_renders_properly -- cgit v1.2.3 From 8154f4b816ea1bb2489fba1829ee99cb75fdf696 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 13:32:37 -0500 Subject: Compile regex only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/mime_type.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 3b5c1d50f2..f2ccf1784b 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -80,6 +80,9 @@ module Mime end class << self + + TRAILING_STAR_REGEXP = /(\w+)\/\*/ + def lookup(string) LOOKUP[string] end @@ -103,11 +106,10 @@ module Mime ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last } end - def parse(accept_header) if accept_header !~ /,/ - if result = Regexp.new('(\w+)\/\*').match(accept_header) - parse_data_with_trailing_star(result[1]) + if accept_header =~ TRAILING_STAR_REGEXP + parse_data_with_trailing_star($1) else [Mime::Type.lookup(accept_header)] end -- cgit v1.2.3 From c5bfc6abe4fd160814f09b02afe68d7f4e25e3d6 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 13:38:36 -0500 Subject: string include method is 10x faster than creating a new regex object every single time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/mime_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index f2ccf1784b..af7b62b863 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -174,7 +174,7 @@ module Mime # input: 'application' # returend value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM def parse_data_with_trailing_star(input) - keys = Mime::LOOKUP.keys.select{|k| Regexp.new(input).match(k)} + keys = Mime::LOOKUP.keys.select{|k| k.include?(input)} Mime::LOOKUP.select {|k,_| keys.include?(k)}.collect{|i| i[1]}.inject([]){|all,e| all.include?(e) ? all : all << e} end end -- cgit v1.2.3 From 794b34306eea0c7e1e99f5741df16129e8e22b3f Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 13:55:43 -0500 Subject: remove select, collect and then inject with something better MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/mime_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index af7b62b863..2250cfa88a 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -175,7 +175,7 @@ module Mime # returend value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM def parse_data_with_trailing_star(input) keys = Mime::LOOKUP.keys.select{|k| k.include?(input)} - Mime::LOOKUP.select {|k,_| keys.include?(k)}.collect{|i| i[1]}.inject([]){|all,e| all.include?(e) ? all : all << e} + Mime::LOOKUP.values_at(*keys).uniq end end -- cgit v1.2.3 From 21fd93cedda9d96a81b06b96c541120a5da7a71a Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 13:59:46 -0500 Subject: make test more resilient to future changes by not testing the count and not testing the internal value of the registered mime type. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ideally all mime type registration should be cleaned up in teardown. Signed-off-by: José Valim --- actionpack/test/dispatch/mime_type_test.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 3a7ab740f4..28e8d420d5 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -31,9 +31,7 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse image with trailing star" do accept = "image/*" parsed = Mime::Type.parse(accept) - assert_equal 2, parsed.size assert parsed.include?(Mime::PNG) - assert_equal 'image/*', parsed[1].instance_variable_get('@string') end test "parse without q" do -- cgit v1.2.3 From 1c68e55ae5e90b7c072a1f6030ea3dd4becd8a07 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 22 Nov 2010 17:32:15 -0500 Subject: Revert "unscoped takes care of named_scopes too" This reverts commit 126fbd7ed8a310bf940414c1b7ddab06b03d400e. --- activerecord/lib/active_record/base.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d7a7101404..f588475bbf 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -856,7 +856,8 @@ module ActiveRecord #:nodoc: # limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } # - # Assuming that published is a named_scope following two statements are same. + # It is recommended to use block form of unscoped because chaining unscoped with named_scope + # does not work. Assuming that published is a named_scope following two statements are same. # # Post.unscoped.published # Post.published -- cgit v1.2.3 From 66c09372f3ef3d4ca2b99d44cf1859d585b9dcb3 Mon Sep 17 00:00:00 2001 From: Alex Rothenberg Date: Tue, 23 Nov 2010 06:10:19 +0800 Subject: Removed ids_in_list_limit in favor of in_clause_length defined in database_limits.rb --- activerecord/lib/active_record/association_preload.rb | 4 ++-- .../active_record/connection_adapters/abstract_adapter.rb | 5 ----- activerecord/test/cases/associations/eager_test.rb | 14 +++++++------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 9743b1b4a7..5eb1071ba2 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -391,9 +391,9 @@ module ActiveRecord # Some databases impose a limit on the number of ids in a list (in Oracle its 1000) # Make several smaller queries if necessary or make one query if the adapter supports it def associated_records(ids) - max_ids_in_a_list = connection.ids_in_list_limit || ids.size + in_clause_length = connection.in_clause_length || ids.size records = [] - ids.each_slice(max_ids_in_a_list) do |some_ids| + ids.each_slice(in_clause_length) do |some_ids| records += yield(some_ids) end records diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index ada1560ce2..f3fba9a3a9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -91,11 +91,6 @@ module ActiveRecord false end - # Does this adapter restrict the number of ids you can use in a list. Oracle has a limit of 1000. - def ids_in_list_limit - nil - end - # QUOTING ================================================== # Override to return the quoted table name. Defaults to column quoting. diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index c00b8a1cde..ea86ac29d0 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -80,31 +80,31 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle - Post.connection.expects(:ids_in_list_limit).at_least_once.returns(5) + Post.connection.expects(:in_clause_length).at_least_once.returns(5) posts = Post.find(:all, :include=>:comments) assert_equal 7, posts.size end def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle - Post.connection.expects(:ids_in_list_limit).at_least_once.returns(nil) + Post.connection.expects(:in_clause_length).at_least_once.returns(nil) posts = Post.find(:all, :include=>:comments) assert_equal 7, posts.size end def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle - Post.connection.expects(:ids_in_list_limit).at_least_once.returns(5) + Post.connection.expects(:in_clause_length).at_least_once.returns(5) posts = Post.find(:all, :include=>:categories) assert_equal 7, posts.size end def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle - Post.connection.expects(:ids_in_list_limit).at_least_once.returns(nil) + Post.connection.expects(:in_clause_length).at_least_once.returns(nil) posts = Post.find(:all, :include=>:categories) assert_equal 7, posts.size end def test_load_associated_records_in_one_query_when_adapter_has_no_limit - Post.connection.expects(:ids_in_list_limit).at_least_once.returns(nil) + Post.connection.expects(:in_clause_length).at_least_once.returns(nil) Post.expects(:i_was_called).with([1,2,3,4,5,6,7]).returns([1]) associated_records = Post.send(:associated_records, [1,2,3,4,5,6,7]) do |some_ids| Post.i_was_called(some_ids) @@ -113,7 +113,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_load_associated_records_in_several_queries_when_many_ids_passed - Post.connection.expects(:ids_in_list_limit).at_least_once.returns(5) + Post.connection.expects(:in_clause_length).at_least_once.returns(5) Post.expects(:i_was_called).with([1,2,3,4,5]).returns([1]) Post.expects(:i_was_called).with([6,7]).returns([6]) associated_records = Post.send(:associated_records, [1,2,3,4,5,6,7]) do |some_ids| @@ -123,7 +123,7 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_load_associated_records_in_one_query_when_a_few_ids_passed - Post.connection.expects(:ids_in_list_limit).at_least_once.returns(5) + Post.connection.expects(:in_clause_length).at_least_once.returns(5) Post.expects(:i_was_called).with([1,2,3]).returns([1]) associated_records = Post.send(:associated_records, [1,2,3]) do |some_ids| Post.i_was_called(some_ids) -- cgit v1.2.3 From 5c86286dd6f8b58241492004e1fd872e217aca08 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 21 Nov 2010 13:46:25 +0100 Subject: add respond_to? to ActionDispatch::Integration::Runner since Runner uses method_missing to delegate to the integration session it also should define respond_to? accordingly --- actionpack/lib/action_dispatch/testing/integration.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index fee8cad9f5..e0a17130f8 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -363,6 +363,10 @@ module ActionDispatch integration_session.url_options end + def respond_to?(method, include_private = false) + @integration_session.respond_to?(method, include_private) || super + end + # Delegate unhandled messages to the current session instance. def method_missing(sym, *args, &block) reset! unless integration_session -- cgit v1.2.3 From 4395d493c5df5476e9e31df3336fc81c8a3e5d3c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 22 Nov 2010 15:51:29 -0800 Subject: sort lists before comparing them --- actionpack/test/dispatch/mime_type_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 28e8d420d5..43123e68b2 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -14,18 +14,18 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse text with trailing star" do accept = "text/*" - expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT] + expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT].sort_by(&:to_s) parsed = Mime::Type.parse(accept) assert_equal 9, parsed.size - assert_equal expect, parsed + assert_equal expect, parsed.sort_by(&:to_s) end test "parse application with trailing star" do accept = "application/*" - expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::PDF, Mime::URL_ENCODED_FORM] + expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::PDF, Mime::URL_ENCODED_FORM].sort_by(&:to_s) parsed = Mime::Type.parse(accept) assert_equal 9, parsed.size - assert_equal expect, parsed + assert_equal expect, parsed.sort_by(&:to_s) end test "parse image with trailing star" do -- cgit v1.2.3 From e83d15376d7ca10e20a720727f22cc9cdbbd662c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 22 Nov 2010 15:57:03 -0800 Subject: adding a test for the runner module [#6027 state:resolved] --- actionpack/test/controller/runner_test.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 actionpack/test/controller/runner_test.rb diff --git a/actionpack/test/controller/runner_test.rb b/actionpack/test/controller/runner_test.rb new file mode 100644 index 0000000000..24c220dcd5 --- /dev/null +++ b/actionpack/test/controller/runner_test.rb @@ -0,0 +1,22 @@ +require 'abstract_unit' +require 'action_dispatch/testing/integration' + +module ActionDispatch + class RunnerTest < Test::Unit::TestCase + class MyRunner + include Integration::Runner + + def initialize(session) + @integration_session = session + end + + def hi; end + end + + def test_respond_to? + runner = MyRunner.new(Class.new { def x; end }.new) + assert runner.respond_to?(:hi) + assert runner.respond_to?(:x) + end + end +end -- cgit v1.2.3 From 4d31ee1e0e2eccae0c9964214455ff75c647bd7f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 22 Nov 2010 16:14:37 -0800 Subject: removing unused variables --- .../lib/active_record/associations/through_association_scope.rb | 4 ++-- activerecord/test/cases/associations/has_many_associations_test.rb | 3 +-- .../test/cases/associations/has_many_through_associations_test.rb | 1 - activerecord/test/cases/associations/has_one_associations_test.rb | 7 +++---- activerecord/test/cases/associations/inverse_associations_test.rb | 2 -- activerecord/test/cases/associations/join_model_test.rb | 2 +- 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index bd8e304e99..acddfda924 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -16,7 +16,7 @@ module ActiveRecord :readonly => @reflection.options[:readonly] } end - + def construct_create_scope construct_owner_attributes(@reflection) end @@ -51,7 +51,7 @@ module ActiveRecord def construct_select(custom_select = nil) distinct = "DISTINCT " if @reflection.options[:uniq] - selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*" + custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*" end def construct_joins(custom_joins = nil) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 33c53e695b..fb772bb8e6 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -67,8 +67,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_no_sql_should_be_fired_if_association_already_loaded - car = Car.create(:name => 'honda') - bulb = car.bulbs.create + Car.create(:name => 'honda') bulbs = Car.first.bulbs bulbs.inspect # to load all instances of bulbs assert_no_queries do diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 94e1eb8c89..52432b0428 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -354,7 +354,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist - author = authors(:mary) post = Post.create!(:title => "TITLE", :body => "BODY") assert_equal [], post.author_favorites end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 6fbeff8aa9..64449df8f5 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -163,7 +163,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase firm = ExclusivelyDependentFirm.find(9) assert_not_nil firm.account - account_id = firm.account.id assert_equal [], Account.destroyed_account_ids[firm.id] firm.destroy @@ -180,7 +179,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase def test_dependence_with_restrict firm = RestrictedFirm.new(:name => 'restrict') firm.save! - account = firm.create_account(:credit_limit => 10) + firm.create_account(:credit_limit => 10) assert_not_nil firm.account assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy } end @@ -197,8 +196,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase def test_build_association_twice_without_saving_affects_nothing count_of_account = Account.count firm = Firm.find(:first) - account1 = firm.build_account("credit_limit" => 1000) - account2 = firm.build_account("credit_limit" => 2000) + firm.build_account("credit_limit" => 1000) + firm.build_account("credit_limit" => 2000) assert_equal count_of_account, Account.count end diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index 081583038f..0491b2d1fa 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -484,7 +484,6 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase def test_child_instance_should_be_shared_with_replaced_via_accessor_parent face = faces(:confused) - old_man = face.polymorphic_man new_man = Man.new assert_not_nil face.polymorphic_man @@ -499,7 +498,6 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase def test_child_instance_should_be_shared_with_replaced_via_method_parent face = faces(:confused) - old_man = face.polymorphic_man new_man = Man.new assert_not_nil face.polymorphic_man diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 96edcfbb35..1ece961d2e 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -642,7 +642,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_preload_nil_polymorphic_belongs_to assert_nothing_raised do - taggings = Tagging.find(:all, :include => :taggable, :conditions => ['taggable_type IS NULL']) + Tagging.find(:all, :include => :taggable, :conditions => ['taggable_type IS NULL']) end end -- cgit v1.2.3 From 4ece7e06fd0009f024fd167107324122c564d551 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 22 Nov 2010 17:06:55 -0800 Subject: removing space error --- activerecord/lib/active_record/associations/belongs_to_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index b624951cd9..b438620c8f 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -61,7 +61,7 @@ module ActiveRecord set_inverse_instance(the_target, @owner) the_target end - + def construct_find_scope { :conditions => conditions } end -- cgit v1.2.3 From dc320d5873e54338a917ced26d849a64001edf28 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 22 Nov 2010 17:23:37 -0800 Subject: skip cloning if arguments are blank --- .../lib/active_record/relation/query_methods.rb | 30 ++++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9e7503a60d..0a4c119849 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -13,7 +13,7 @@ module ActiveRecord def includes(*args) args.reject! {|a| a.blank? } - return clone if args.empty? + return self if args.empty? relation = clone relation.includes_values = (relation.includes_values + args).flatten.uniq @@ -21,14 +21,18 @@ module ActiveRecord end def eager_load(*args) + return self if args.blank? + relation = clone - relation.eager_load_values += args unless args.blank? + relation.eager_load_values += args relation end def preload(*args) + return self if args.blank? + relation = clone - relation.preload_values += args unless args.blank? + relation.preload_values += args relation end @@ -43,22 +47,28 @@ module ActiveRecord end def group(*args) + return self if args.blank? + relation = clone - relation.group_values += args.flatten unless args.blank? + relation.group_values += args.flatten relation end def order(*args) + return self if args.blank? + relation = clone - relation.order_values += args.flatten unless args.blank? + relation.order_values += args.flatten relation end def joins(*args) + return self if args.blank? + relation = clone args.flatten! - relation.joins_values += args unless args.blank? + relation.joins_values += args relation end @@ -70,14 +80,18 @@ module ActiveRecord end def where(opts, *rest) + return self if opts.blank? + relation = clone - relation.where_values += build_where(opts, rest) unless opts.blank? + relation.where_values += build_where(opts, rest) relation end def having(*args) + return self if args.blank? + relation = clone - relation.having_values += build_where(*args) unless args.blank? + relation.having_values += build_where(*args) relation end -- cgit v1.2.3 From 9938a3fc78bf8e066d2cdfe17f3710e0e6b85a36 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 23 Nov 2010 00:32:05 -0200 Subject: Update to Prototype 1.7 [#6042 state:committed] --- .../app/templates/public/javascripts/prototype.js | 733 ++++++++++++--------- 1 file changed, 407 insertions(+), 326 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js index 06249a6ae3..474b2231bb 100644 --- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js +++ b/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js @@ -1,4 +1,4 @@ -/* Prototype JavaScript framework, version 1.7_rc2 +/* Prototype JavaScript framework, version 1.7 * (c) 2005-2010 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. @@ -8,7 +8,7 @@ var Prototype = { - Version: '1.7_rc2', + Version: '1.7', Browser: (function(){ var ua = navigator.userAgent; @@ -166,10 +166,12 @@ var Class = (function() { NUMBER_TYPE = 'Number', STRING_TYPE = 'String', OBJECT_TYPE = 'Object', + FUNCTION_CLASS = '[object Function]', BOOLEAN_CLASS = '[object Boolean]', NUMBER_CLASS = '[object Number]', STRING_CLASS = '[object String]', ARRAY_CLASS = '[object Array]', + DATE_CLASS = '[object Date]', NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && typeof JSON.stringify === 'function' && JSON.stringify(0) === '0' && @@ -322,7 +324,7 @@ var Class = (function() { } function isFunction(object) { - return typeof object === "function"; + return _toString.call(object) === FUNCTION_CLASS; } function isString(object) { @@ -333,6 +335,10 @@ var Class = (function() { return _toString.call(object) === NUMBER_CLASS; } + function isDate(object) { + return _toString.call(object) === DATE_CLASS; + } + function isUndefined(object) { return typeof object === "undefined"; } @@ -352,6 +358,7 @@ var Class = (function() { isFunction: isFunction, isString: isString, isNumber: isNumber, + isDate: isDate, isUndefined: isUndefined }); })(); @@ -1079,9 +1086,10 @@ Array.from = $A; slice = arrayProto.slice, _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available - function each(iterator) { - for (var i = 0, length = this.length; i < length; i++) - iterator(this[i]); + function each(iterator, context) { + for (var i = 0, length = this.length >>> 0; i < length; i++) { + if (i in this) iterator.call(context, this[i], i, this); + } } if (!_each) _each = each; @@ -1287,8 +1295,14 @@ var Hash = Class.create(Enumerable, (function() { var key = encodeURIComponent(pair.key), values = pair.value; if (values && typeof values == 'object') { - if (Object.isArray(values)) - return results.concat(values.map(toQueryPair.curry(key))); + if (Object.isArray(values)) { + var queryValues = []; + for (var i = 0, len = values.length, value; i < len; i++) { + value = values[i]; + queryValues.push(toQueryPair(key, value)); + } + return results.concat(queryValues); + } } else results.push(toQueryPair(key, values)); return results; }).join('&'); @@ -1468,9 +1482,7 @@ Ajax.Base = Class.create({ this.options.method = this.options.method.toLowerCase(); - if (Object.isString(this.options.parameters)) - this.options.parameters = this.options.parameters.toQueryParams(); - else if (Object.isHash(this.options.parameters)) + if (Object.isHash(this.options.parameters)) this.options.parameters = this.options.parameters.toObject(); } }); @@ -1486,22 +1498,21 @@ Ajax.Request = Class.create(Ajax.Base, { request: function(url) { this.url = url; this.method = this.options.method; - var params = Object.clone(this.options.parameters); + var params = Object.isString(this.options.parameters) ? + this.options.parameters : + Object.toQueryString(this.options.parameters); if (!['get', 'post'].include(this.method)) { - params['_method'] = this.method; + params += (params ? '&' : '') + "_method=" + this.method; this.method = 'post'; } - this.parameters = params; - - if (params = Object.toQueryString(params)) { - if (this.method == 'get') - this.url += (this.url.include('?') ? '&' : '?') + params; - else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - params += '&_='; + if (params && this.method === 'get') { + this.url += (this.url.include('?') ? '&' : '?') + params; } + this.parameters = params.toQueryParams(); + try { var response = new Ajax.Response(this); if (this.options.onCreate) this.options.onCreate(response); @@ -1570,11 +1581,12 @@ Ajax.Request = Class.create(Ajax.Base, { success: function() { var status = this.getStatus(); - return !status || (status >= 200 && status < 300); + return !status || (status >= 200 && status < 300) || status == 304; }, getStatus: function() { try { + if (this.transport.status === 1223) return 204; return this.transport.status || 0; } catch (e) { return 0 } }, @@ -1849,6 +1861,11 @@ if (!Node.ELEMENT_NODE) { (function(global) { + function shouldUseCache(tagName, attributes) { + if (tagName === 'select') return false; + if ('type' in attributes) return false; + return true; + } var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ try { @@ -1866,13 +1883,19 @@ if (!Node.ELEMENT_NODE) { attributes = attributes || { }; tagName = tagName.toLowerCase(); var cache = Element.cache; + if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { tagName = '<' + tagName + ' name="' + attributes.name + '">'; delete attributes.name; return Element.writeAttribute(document.createElement(tagName), attributes); } + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); - return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); + + var node = shouldUseCache(tagName, attributes) ? + cache[tagName].cloneNode(false) : document.createElement(tagName); + + return Element.writeAttribute(node, attributes); }; Object.extend(global.Element, element || { }); @@ -1883,7 +1906,7 @@ if (!Node.ELEMENT_NODE) { Element.idCounter = 1; Element.cache = { }; -function purgeElement(element) { +Element._purgeElement = function(element) { var uid = element._prototypeUID; if (uid) { Element.stopObserving(element); @@ -1948,6 +1971,21 @@ Element.Methods = { } })(); + var LINK_ELEMENT_INNERHTML_BUGGY = (function() { + try { + var el = document.createElement('div'); + el.innerHTML = ""; + var isBuggy = (el.childNodes.length === 0); + el = null; + return isBuggy; + } catch(e) { + return true; + } + })(); + + var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY || + TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY; + var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { var s = document.createElement("script"), isBuggy = false; @@ -1962,8 +2000,10 @@ Element.Methods = { return isBuggy; })(); + function update(element, content) { element = $(element); + var purgeElement = Element._purgeElement; var descendants = element.getElementsByTagName('*'), i = descendants.length; @@ -1984,7 +2024,7 @@ Element.Methods = { return element; } - if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) { + if (ANY_INNERHTML_BUGGY) { if (tagName in Element._insertionTranslations.tags) { while (element.firstChild) { element.removeChild(element.firstChild); @@ -1993,6 +2033,12 @@ Element.Methods = { .each(function(node) { element.appendChild(node) }); + } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf(' -1) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true); + nodes.each(function(node) { element.appendChild(node) }); } else { element.innerHTML = content.stripScripts(); @@ -2402,117 +2448,6 @@ Element.Methods = { return element; }, - cumulativeOffset: function(element) { - var valueT = 0, valueL = 0; - if (element.parentNode) { - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - } - return Element._returnOffset(valueL, valueT); - }, - - positionedOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - if (element) { - if (element.tagName.toUpperCase() == 'BODY') break; - var p = Element.getStyle(element, 'position'); - if (p !== 'static') break; - } - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - absolutize: function(element) { - element = $(element); - if (Element.getStyle(element, 'position') == 'absolute') return element; - - var offsets = Element.positionedOffset(element), - top = offsets[1], - left = offsets[0], - width = element.clientWidth, - height = element.clientHeight; - - element._originalLeft = left - parseFloat(element.style.left || 0); - element._originalTop = top - parseFloat(element.style.top || 0); - element._originalWidth = element.style.width; - element._originalHeight = element.style.height; - - element.style.position = 'absolute'; - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.width = width + 'px'; - element.style.height = height + 'px'; - return element; - }, - - relativize: function(element) { - element = $(element); - if (Element.getStyle(element, 'position') == 'relative') return element; - - element.style.position = 'relative'; - var top = parseFloat(element.style.top || 0) - (element._originalTop || 0), - left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); - - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.height = element._originalHeight; - element.style.width = element._originalWidth; - return element; - }, - - cumulativeScrollOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - getOffsetParent: function(element) { - if (element.offsetParent) return $(element.offsetParent); - if (element == document.body) return $(element); - - while ((element = element.parentNode) && element != document.body) - if (Element.getStyle(element, 'position') != 'static') - return $(element); - - return $(document.body); - }, - - viewportOffset: function(forElement) { - var valueT = 0, - valueL = 0, - element = forElement; - - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - - if (element.offsetParent == document.body && - Element.getStyle(element, 'position') == 'absolute') break; - - } while (element = element.offsetParent); - - element = forElement; - do { - if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; - } - } while (element = element.parentNode); - - return Element._returnOffset(valueL, valueT); - }, - clonePosition: function(element, source) { var options = Object.extend({ setLeft: true, @@ -2566,8 +2501,6 @@ if (Prototype.Browser.Opera) { Element.Methods.getStyle = Element.Methods.getStyle.wrap( function(proceed, element, style) { switch (style) { - case 'left': case 'top': case 'right': case 'bottom': - if (proceed(element, 'position') === 'static') return null; case 'height': case 'width': if (!Element.visible(element)) return null; @@ -2603,37 +2536,6 @@ if (Prototype.Browser.Opera) { } else if (Prototype.Browser.IE) { - Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( - function(proceed, element) { - element = $(element); - if (!element.parentNode) return $(document.body); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - - $w('positionedOffset viewportOffset').each(function(method) { - Element.Methods[method] = Element.Methods[method].wrap( - function(proceed, element) { - element = $(element); - if (!element.parentNode) return Element._returnOffset(0, 0); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - var offsetParent = element.getOffsetParent(); - if (offsetParent && offsetParent.getStyle('position') === 'fixed') - offsetParent.setStyle({ zoom: 1 }); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - }); - Element.Methods.getStyle = function(element, style) { element = $(element); style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); @@ -2862,20 +2764,6 @@ else if (Prototype.Browser.WebKit) { return element; }; - - Element.Methods.cumulativeOffset = function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - if (element.offsetParent == document.body) - if (Element.getStyle(element, 'position') == 'absolute') break; - - element = element.offsetParent; - } while (element); - - return Element._returnOffset(valueL, valueT); - }; } if ('outerHTML' in document.documentElement) { @@ -2914,11 +2802,20 @@ Element._returnOffset = function(l, t) { return result; }; -Element._getContentFromAnonymousElement = function(tagName, html) { +Element._getContentFromAnonymousElement = function(tagName, html, force) { var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; - if (t) { - div.innerHTML = t[0] + html + t[1]; + + var workaround = false; + if (t) workaround = true; + else if (force) { + workaround = true; + t = ['', '', 0]; + } + + if (workaround) { + div.innerHTML = ' ' + t[0] + html + t[1]; + div.removeChild(div.firstChild); for (var i = t[2]; i--; ) { div = div.firstChild; } @@ -3077,7 +2974,8 @@ Element.addMethods = function(methods) { "FORM": Object.clone(Form.Methods), "INPUT": Object.clone(Form.Element.Methods), "SELECT": Object.clone(Form.Element.Methods), - "TEXTAREA": Object.clone(Form.Element.Methods) + "TEXTAREA": Object.clone(Form.Element.Methods), + "BUTTON": Object.clone(Form.Element.Methods) }); } @@ -3264,6 +3162,8 @@ Element.addMethods({ purge: function(element) { if (!(element = $(element))) return; + var purgeElement = Element._purgeElement; + purgeElement(element); var descendants = element.getElementsByTagName('*'), @@ -3283,11 +3183,13 @@ Element.addMethods({ return (Number(match[1]) / 100); } - function getPixelValue(value, property) { + function getPixelValue(value, property, context) { + var element = null; if (Object.isElement(value)) { element = value; value = element.getStyle(property); } + if (value === null) { return null; } @@ -3296,7 +3198,9 @@ Element.addMethods({ return window.parseFloat(value); } - if (/\d/.test(value) && element.runtimeStyle) { + var isPercentage = value.include('%'), isViewport = (context === document.viewport); + + if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) { var style = element.style.left, rStyle = element.runtimeStyle.left; element.runtimeStyle.left = element.currentStyle.left; element.style.left = value || 0; @@ -3307,18 +3211,33 @@ Element.addMethods({ return value; } - if (value.include('%')) { + if (element && isPercentage) { + context = context || element.parentNode; var decimal = toDecimal(value); - var whole; - if (property.include('left') || property.include('right') || - property.include('width')) { - whole = $(element.parentNode).measure('width'); - } else if (property.include('top') || property.include('bottom') || - property.include('height')) { - whole = $(element.parentNode).measure('height'); + var whole = null; + var position = element.getStyle('position'); + + var isHorizontal = property.include('left') || property.include('right') || + property.include('width'); + + var isVertical = property.include('top') || property.include('bottom') || + property.include('height'); + + if (context === document.viewport) { + if (isHorizontal) { + whole = document.viewport.getWidth(); + } else if (isVertical) { + whole = document.viewport.getHeight(); + } + } else { + if (isHorizontal) { + whole = $(context).measure('width'); + } else if (isVertical) { + whole = $(context).measure('height'); + } } - return whole * decimal; + return (whole === null) ? 0 : whole * decimal; } return 0; @@ -3410,6 +3329,14 @@ Element.addMethods({ var position = element.getStyle('position'), width = element.getStyle('width'); + if (width === "0px" || width === null) { + element.style.display = 'block'; + width = element.getStyle('width'); + } + + var context = (position === 'fixed') ? document.viewport : + element.parentNode; + element.setStyle({ position: 'absolute', visibility: 'hidden', @@ -3420,9 +3347,9 @@ Element.addMethods({ var newWidth; if (width && (positionedWidth === width)) { - newWidth = getPixelValue(width); - } else if (width && (position === 'absolute' || position === 'fixed')) { - newWidth = getPixelValue(width); + newWidth = getPixelValue(element, 'width', context); + } else if (position === 'absolute' || position === 'fixed') { + newWidth = getPixelValue(element, 'width', context); } else { var parent = element.parentNode, pLayout = $(parent).getLayout(); @@ -3453,6 +3380,7 @@ Element.addMethods({ if (!(property in COMPUTATIONS)) { throw "Property not found."; } + return this._set(property, COMPUTATIONS[property].call(this, this.element)); }, @@ -3505,7 +3433,10 @@ Element.addMethods({ if (!this._preComputing) this._begin(); var bHeight = this.get('border-box-height'); - if (bHeight <= 0) return 0; + if (bHeight <= 0) { + if (!this._preComputing) this._end(); + return 0; + } var bTop = this.get('border-top'), bBottom = this.get('border-bottom'); @@ -3522,7 +3453,10 @@ Element.addMethods({ if (!this._preComputing) this._begin(); var bWidth = this.get('border-box-width'); - if (bWidth <= 0) return 0; + if (bWidth <= 0) { + if (!this._preComputing) this._end(); + return 0; + } var bLeft = this.get('border-left'), bRight = this.get('border-right'); @@ -3552,11 +3486,17 @@ Element.addMethods({ }, 'border-box-height': function(element) { - return element.offsetHeight; + if (!this._preComputing) this._begin(); + var height = element.offsetHeight; + if (!this._preComputing) this._end(); + return height; }, 'border-box-width': function(element) { - return element.offsetWidth; + if (!this._preComputing) this._begin(); + var width = element.offsetWidth; + if (!this._preComputing) this._end(); + return width; }, 'margin-box-height': function(element) { @@ -3626,23 +3566,19 @@ Element.addMethods({ }, 'border-top': function(element) { - return Object.isNumber(element.clientTop) ? element.clientTop : - getPixelValue(element, 'borderTopWidth'); + return getPixelValue(element, 'borderTopWidth'); }, 'border-bottom': function(element) { - return Object.isNumber(element.clientBottom) ? element.clientBottom : - getPixelValue(element, 'borderBottomWidth'); + return getPixelValue(element, 'borderBottomWidth'); }, 'border-left': function(element) { - return Object.isNumber(element.clientLeft) ? element.clientLeft : - getPixelValue(element, 'borderLeftWidth'); + return getPixelValue(element, 'borderLeftWidth'); }, 'border-right': function(element) { - return Object.isNumber(element.clientRight) ? element.clientRight : - getPixelValue(element, 'borderRightWidth'); + return getPixelValue(element, 'borderRightWidth'); }, 'margin-top': function(element) { @@ -3721,23 +3657,52 @@ Element.addMethods({ } function getDimensions(element) { - var layout = $(element).getLayout(); - return { - width: layout.get('width'), - height: layout.get('height') + element = $(element); + var display = Element.getStyle(element, 'display'); + + if (display && display !== 'none') { + return { width: element.offsetWidth, height: element.offsetHeight }; + } + + var style = element.style; + var originalStyles = { + visibility: style.visibility, + position: style.position, + display: style.display + }; + + var newStyles = { + visibility: 'hidden', + display: 'block' }; + + if (originalStyles.position !== 'fixed') + newStyles.position = 'absolute'; + + Element.setStyle(element, newStyles); + + var dimensions = { + width: element.offsetWidth, + height: element.offsetHeight + }; + + Element.setStyle(element, originalStyles); + + return dimensions; } function getOffsetParent(element) { - if (isDetached(element)) return $(document.body); + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); var isInline = (Element.getStyle(element, 'display') === 'inline'); if (!isInline && element.offsetParent) return $(element.offsetParent); - if (element === document.body) return $(element); while ((element = element.parentNode) && element !== document.body) { if (Element.getStyle(element, 'position') !== 'static') { - return (element.nodeName === 'HTML') ? $(document.body) : $(element); + return isHtml(element) ? $(document.body) : $(element); } } @@ -3746,16 +3711,21 @@ Element.addMethods({ function cumulativeOffset(element) { + element = $(element); var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); + if (element.parentNode) { + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + } return new Element.Offset(valueL, valueT); } function positionedOffset(element) { + element = $(element); + var layout = element.getLayout(); var valueT = 0, valueL = 0; @@ -3787,6 +3757,7 @@ Element.addMethods({ } function viewportOffset(forElement) { + element = $(element); var valueT = 0, valueL = 0, docBody = document.body; var element = forElement; @@ -3852,6 +3823,57 @@ Element.addMethods({ return element; } + if (Prototype.Browser.IE) { + getOffsetParent = getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + positionedOffset = positionedOffset.wrap(function(proceed, element) { + element = $(element); + if (!element.parentNode) return new Element.Offset(0, 0); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + hasLayout(offsetParent); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + }); + } else if (Prototype.Browser.Webkit) { + cumulativeOffset = function(element) { + element = $(element); + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return new Element.Offset(valueL, valueT); + }; + } + + Element.addMethods({ getLayout: getLayout, measure: measure, @@ -3869,6 +3891,14 @@ Element.addMethods({ return element.nodeName.toUpperCase() === 'BODY'; } + function isHtml(element) { + return element.nodeName.toUpperCase() === 'HTML'; + } + + function isDocument(element) { + return element.nodeType === Node.DOCUMENT_NODE; + } + function isDetached(element) { return element !== document.body && !Element.descendantOf(element, document.body); @@ -3880,32 +3910,10 @@ Element.addMethods({ element = $(element); if (isDetached(element)) return new Element.Offset(0, 0); - var rect = element.getBoundingClientRect(), + var rect = element.getBoundingClientRect(), docEl = document.documentElement; return new Element.Offset(rect.left - docEl.clientLeft, rect.top - docEl.clientTop); - }, - - positionedOffset: function(element) { - element = $(element); - var parent = element.getOffsetParent(); - if (isDetached(element)) return new Element.Offset(0, 0); - - if (element.offsetParent && - element.offsetParent.nodeName.toUpperCase() === 'HTML') { - return positionedOffset(element); - } - - var eOffset = element.viewportOffset(), - pOffset = isBody(parent) ? viewportOffset(parent) : - parent.viewportOffset(); - var retOffset = eOffset.relativeTo(pOffset); - - var layout = element.getLayout(); - var top = retOffset.top - layout.get('margin-top'); - var left = retOffset.left - layout.get('margin-left'); - - return new Element.Offset(left, top); } }); } @@ -4962,24 +4970,34 @@ var Form = { serializeElements: function(elements, options) { if (typeof options != 'object') options = { hash: !!options }; else if (Object.isUndefined(options.hash)) options.hash = true; - var key, value, submitted = false, submit = options.submit; + var key, value, submitted = false, submit = options.submit, accumulator, initial; + + if (options.hash) { + initial = {}; + accumulator = function(result, key, value) { + if (key in result) { + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } else result[key] = value; + return result; + }; + } else { + initial = ''; + accumulator = function(result, key, value) { + return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value); + } + } - var data = elements.inject({ }, function(result, element) { + return elements.inject(initial, function(result, element) { if (!element.disabled && element.name) { key = element.name; value = $(element).getValue(); if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && submit !== false && (!submit || key == submit) && (submitted = true)))) { - if (key in result) { - if (!Object.isArray(result[key])) result[key] = [result[key]]; - result[key].push(value); - } - else result[key] = value; + result = accumulator(result, key, value); } } return result; }); - - return options.hash ? data : Object.toQueryString(data); } }; @@ -5046,7 +5064,8 @@ Form.Methods = { focusFirstElement: function(form) { form = $(form); - form.findFirstElement().activate(); + var element = form.findFirstElement(); + if (element) element.activate(); return form; }, @@ -5153,67 +5172,77 @@ var $F = Form.Element.Methods.getValue; /*--------------------------------------------------------------------------*/ -Form.Element.Serializers = { - input: function(element, value) { +Form.Element.Serializers = (function() { + function input(element, value) { switch (element.type.toLowerCase()) { case 'checkbox': case 'radio': - return Form.Element.Serializers.inputSelector(element, value); + return inputSelector(element, value); default: - return Form.Element.Serializers.textarea(element, value); + return valueSelector(element, value); } - }, + } - inputSelector: function(element, value) { - if (Object.isUndefined(value)) return element.checked ? element.value : null; + function inputSelector(element, value) { + if (Object.isUndefined(value)) + return element.checked ? element.value : null; else element.checked = !!value; - }, + } - textarea: function(element, value) { + function valueSelector(element, value) { if (Object.isUndefined(value)) return element.value; else element.value = value; - }, + } - select: function(element, value) { + function select(element, value) { if (Object.isUndefined(value)) - return this[element.type == 'select-one' ? - 'selectOne' : 'selectMany'](element); - else { - var opt, currentValue, single = !Object.isArray(value); - for (var i = 0, length = element.length; i < length; i++) { - opt = element.options[i]; - currentValue = this.optionValue(opt); - if (single) { - if (currentValue == value) { - opt.selected = true; - return; - } + return (element.type === 'select-one' ? selectOne : selectMany)(element); + + var opt, currentValue, single = !Object.isArray(value); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + currentValue = this.optionValue(opt); + if (single) { + if (currentValue == value) { + opt.selected = true; + return; } - else opt.selected = value.include(currentValue); } + else opt.selected = value.include(currentValue); } - }, + } - selectOne: function(element) { + function selectOne(element) { var index = element.selectedIndex; - return index >= 0 ? this.optionValue(element.options[index]) : null; - }, + return index >= 0 ? optionValue(element.options[index]) : null; + } - selectMany: function(element) { + function selectMany(element) { var values, length = element.length; if (!length) return null; for (var i = 0, values = []; i < length; i++) { var opt = element.options[i]; - if (opt.selected) values.push(this.optionValue(opt)); + if (opt.selected) values.push(optionValue(opt)); } return values; - }, + } - optionValue: function(opt) { - return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + function optionValue(opt) { + return Element.hasAttribute(opt, 'value') ? opt.value : opt.text; } -}; + + return { + input: input, + inputSelector: inputSelector, + textarea: valueSelector, + select: select, + selectOne: selectOne, + selectMany: selectMany, + optionValue: optionValue, + button: valueSelector + }; +})(); /*--------------------------------------------------------------------------*/ @@ -5324,24 +5353,53 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl && 'onmouseleave' in docEl; + + + var isIELegacyEvent = function(event) { return false; }; + + if (window.attachEvent) { + if (window.addEventListener) { + isIELegacyEvent = function(event) { + return !(event instanceof window.Event); + }; + } else { + isIELegacyEvent = function(event) { return true; }; + } + } + var _isButton; - if (Prototype.Browser.IE) { - var buttonMap = { 0: 1, 1: 4, 2: 2 }; - _isButton = function(event, code) { - return event.button === buttonMap[code]; - }; - } else if (Prototype.Browser.WebKit) { - _isButton = function(event, code) { - switch (code) { - case 0: return event.which == 1 && !event.metaKey; - case 1: return event.which == 1 && event.metaKey; - default: return false; + + function _isButtonForDOMEvents(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + } + + var legacyButtonMap = { 0: 1, 1: 4, 2: 2 }; + function _isButtonForLegacyEvents(event, code) { + return event.button === legacyButtonMap[code]; + } + + function _isButtonForWebKit(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 2 || (event.which == 1 && event.metaKey); + case 2: return event.which == 3; + default: return false; + } + } + + if (window.attachEvent) { + if (!window.addEventListener) { + _isButton = _isButtonForLegacyEvents; + } else { + _isButton = function(event, code) { + return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) : + _isButtonForDOMEvents(event, code); } - }; + } + } else if (Prototype.Browser.WebKit) { + _isButton = _isButtonForWebKit; } else { - _isButton = function(event, code) { - return event.which ? (event.which === code + 1) : (event.button === code); - }; + _isButton = _isButtonForDOMEvents; } function isLeftClick(event) { return _isButton(event, 0) } @@ -5371,6 +5429,7 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { function findElement(event, expression) { var element = Event.element(event); + if (!expression) return element; while (element) { if (Object.isElement(element) && Prototype.Selector.match(element, expression)) { @@ -5411,49 +5470,59 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { event.stopped = true; } + Event.Methods = { - isLeftClick: isLeftClick, + isLeftClick: isLeftClick, isMiddleClick: isMiddleClick, - isRightClick: isRightClick, + isRightClick: isRightClick, - element: element, + element: element, findElement: findElement, - pointer: pointer, + pointer: pointer, pointerX: pointerX, pointerY: pointerY, stop: stop }; - var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { m[name] = Event.Methods[name].methodize(); return m; }); - if (Prototype.Browser.IE) { + if (window.attachEvent) { function _relatedTarget(event) { var element; switch (event.type) { - case 'mouseover': element = event.fromElement; break; - case 'mouseout': element = event.toElement; break; - default: return null; + case 'mouseover': + case 'mouseenter': + element = event.fromElement; + break; + case 'mouseout': + case 'mouseleave': + element = event.toElement; + break; + default: + return null; } return Element.extend(element); } - Object.extend(methods, { + var additionalMethods = { stopPropagation: function() { this.cancelBubble = true }, preventDefault: function() { this.returnValue = false }, inspect: function() { return '[object Event]' } - }); + }; Event.extend = function(event, element) { if (!event) return false; - if (event._extendedByPrototype) return event; + if (!isIELegacyEvent(event)) return event; + + if (event._extendedByPrototype) return event; event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); Object.extend(event, { @@ -5463,12 +5532,18 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { pageY: pointer.y }); - return Object.extend(event, methods); + Object.extend(event, methods); + Object.extend(event, additionalMethods); + + return event; }; } else { + Event.extend = Prototype.K; + } + + if (window.addEventListener) { Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; Object.extend(Event.prototype, methods); - Event.extend = Prototype.K; } function _createResponder(element, eventName, handler) { @@ -5567,7 +5642,7 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { element.addEventListener("dataavailable", responder, false); else { element.attachEvent("ondataavailable", responder); - element.attachEvent("onfilterchange", responder); + element.attachEvent("onlosecapture", responder); } } else { var actualEventName = _getDOMEventName(eventName); @@ -5605,7 +5680,13 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { return element; } - var responder = responders.find( function(r) { return r.handler === handler; }); + var i = responders.length, responder; + while (i--) { + if (responders[i].handler === handler) { + responder = responders[i]; + break; + } + } if (!responder) return element; if (eventName.include(':')) { @@ -5613,7 +5694,7 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { element.removeEventListener("dataavailable", responder, false); else { element.detachEvent("ondataavailable", responder); - element.detachEvent("onfilterchange", responder); + element.detachEvent("onlosecapture", responder); } } else { var actualEventName = _getDOMEventName(eventName); @@ -5640,10 +5721,10 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { var event; if (document.createEvent) { event = document.createEvent('HTMLEvents'); - event.initEvent('dataavailable', true, true); + event.initEvent('dataavailable', bubble, true); } else { event = document.createEventObject(); - event.eventType = bubble ? 'ondataavailable' : 'onfilterchange'; + event.eventType = bubble ? 'ondataavailable' : 'onlosecapture'; } event.eventName = eventName; @@ -5677,7 +5758,7 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { }, handleEvent: function(event) { - var element = event.findElement(this.selector); + var element = Event.findElement(event, this.selector); if (element) this.callback.call(this.element, event, element); } }); -- cgit v1.2.3 From de829f871becc87424d75a5050883a02435c01e4 Mon Sep 17 00:00:00 2001 From: Ray Baxter Date: Mon, 22 Nov 2010 22:11:56 -0800 Subject: fix typo --- activerecord/lib/active_record/autosave_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index cb5bc06580..73ac8e82c6 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -89,7 +89,7 @@ module ActiveRecord # post = Post.create(:title => 'ruby rocks') # post.comments.create(:body => 'hello world') # post.comments[0].body = 'hi everyone' - # post.save # => saves both post and comment, with 'hi everyone' as title + # post.save # => saves both post and comment, with 'hi everyone' as body # # Destroying one of the associated models as part of the parent's save action # is as simple as marking it for destruction: -- cgit v1.2.3 From 2fe43b694f36ddb2062a91eebe61a035147265b1 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Tue, 23 Nov 2010 00:31:03 +0100 Subject: :subdomain, :domain and :tld_length options can now be used in url_for, allowing for easy manipulation of the host during link generation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_controller/metal/url_for.rb | 4 +- actionpack/lib/action_dispatch/http/url.rb | 59 ++++++++++++++-------- .../lib/action_dispatch/routing/route_set.rb | 38 +++++++++++--- actionpack/lib/action_dispatch/routing/url_for.rb | 6 +++ actionpack/test/controller/url_for_test.rb | 23 ++++++++- actionpack/test/dispatch/request_test.rb | 6 +-- 6 files changed, 105 insertions(+), 31 deletions(-) diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 333eeaeffb..6fc0cf1fb8 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -6,7 +6,8 @@ module ActionController def url_options @_url_options ||= super.reverse_merge( - :host => request.host_with_port, + :host => request.host, + :port => request.optional_port, :protocol => request.protocol, :_path_segments => request.symbolized_path_parameters ).freeze @@ -20,5 +21,6 @@ module ActionController @_url_options end end + end end diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 9c9eed2c6d..1f7633cbea 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -4,6 +4,27 @@ module ActionDispatch mattr_accessor :tld_length self.tld_length = 1 + def self.extract_domain(host, tld_length = @@tld_length) + return nil unless named_host?(host) + + host.split('.').last(1 + tld_length).join('.') + end + + def self.extract_subdomains(host, tld_length = @@tld_length) + return [] unless named_host?(host) + parts = host.split('.') + parts[0..-(tld_length+2)] + end + + def self.extract_subdomain(host, tld_length = @@tld_length) + extract_subdomains(host, tld_length).join('.') + end + + def self.named_host?(host) + !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host)) + end + + # Returns the complete URL used for this request. def url protocol + host_with_port + fullpath @@ -31,15 +52,18 @@ module ActionDispatch # Returns a \host:\port string for this request, such as "example.com" or # "example.com:8080". def host_with_port - "#{host}#{port_string}" + opt_port = optional_port ? ":#{optional_port}" : nil + "#{host}#{opt_port}" end # Returns the port number of this request as an integer. def port - if raw_host_with_port =~ /:(\d+)$/ - $1.to_i - else - standard_port + @port ||= begin + if raw_host_with_port =~ /:(\d+)$/ + $1.to_i + else + standard_port + end end end @@ -56,10 +80,10 @@ module ActionDispatch port == standard_port end - # Returns a \port suffix like ":8080" if the \port number of this request + # Returns a \port suffix like "8080" if the \port number of this request # is not the default HTTP \port 80 or HTTPS \port 443. - def port_string - port == standard_port ? '' : ":#{port}" + def optional_port + standard_port? ? nil : port end def server_port @@ -69,9 +93,7 @@ module ActionDispatch # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify # a different tld_length, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk". def domain(tld_length = @@tld_length) - return nil unless named_host?(host) - - host.split('.').last(1 + tld_length).join('.') + ActionDispatch::Http::URL.extract_domain(host, tld_length) end # Returns all the \subdomains as an array, so ["dev", "www"] would be @@ -79,20 +101,17 @@ module ActionDispatch # such as 2 to catch ["www"] instead of ["www", "rubyonrails"] # in "www.rubyonrails.co.uk". def subdomains(tld_length = @@tld_length) - return [] unless named_host?(host) - parts = host.split('.') - parts[0..-(tld_length+2)] + ActionDispatch::Http::URL.extract_subdomains(host, tld_length) end + # Returns all the \subdomains as a string, so "dev.www" would be + # returned for "dev.www.rubyonrails.org". You can specify a different tld_length, + # such as 2 to catch ["www"] instead of "www.rubyonrails" + # in "www.rubyonrails.co.uk". def subdomain(tld_length = @@tld_length) - subdomains(tld_length).join('.') + subdomains(tld_length) end - private - - def named_host?(host) - !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host)) - end end end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 32f41934f1..d823fd710e 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -485,7 +485,8 @@ module ActionDispatch Generator.new(options, recall, self, extras).generate end - RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :script_name] + RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length, + :trailing_slash, :script_name, :anchor, :params, :only_path ] def _generate_prefix(options = {}) nil @@ -504,11 +505,8 @@ module ActionDispatch rewritten_url << (options[:protocol] || "http") rewritten_url << "://" unless rewritten_url.match("://") rewritten_url << rewrite_authentication(options) - - raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host] - - rewritten_url << options[:host] - rewritten_url << ":#{options.delete(:port)}" if options.key?(:port) + rewritten_url << host_from_options(options) + rewritten_url << ":#{options.delete(:port)}" if options[:port] end script_name = options.delete(:script_name) @@ -562,6 +560,34 @@ module ActionDispatch end private + + def host_from_options(options) + computed_host = subdomain_and_domain(options) || options[:host] + unless computed_host + raise ArgumentError, "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" + end + computed_host + end + + def subdomain_and_domain(options) + tld_length = options[:tld_length] || ActionDispatch::Http::URL.tld_length + + current_domain = ActionDispatch::Http::URL.extract_domain(options[:host], tld_length) + current_subdomain = ActionDispatch::Http::URL.extract_subdomain(options[:host], tld_length) + + domain_parts = if options[:subdomain] && options[:domain] + [options[:subdomain], options[:domain]] + elsif options[:subdomain] + [options[:subdomain], current_domain] + elsif options[:domain] + [current_subdomain, options[:domain]] + else + nil + end + + domain_parts ? domain_parts.join('.') : nil + end + def handle_positional_args(options) return unless args = options.delete(:_positional_args) diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index bfdea41f60..6c3fc5126a 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -115,6 +115,12 @@ module ActionDispatch # * :host - Specifies the host the link should be targeted at. # If :only_path is false, this option must be # provided either explicitly, or via +default_url_options+. + # * :subdomain - Specifies the subdomain of the link, using the +tld_length+ + # to split the domain from the host. + # * :domain - Specifies the domain of the link, using the +tld_length+ + # to split the subdomain from the host. + # * :tld_length - Optionally specify the tld length (only used if :subdomain + # or :domain are supplied). # * :port - Optionally specify the port to connect to. # * :anchor - An anchor name to be appended to the path. # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2009/" diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 4c07ca4cc3..1f62d29e80 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -17,7 +17,7 @@ module AbstractController end def test_exception_is_thrown_without_host - assert_raise RuntimeError do + assert_raise ArgumentError do W.new.url_for :controller => 'c', :action => 'a', :id => 'i' end end @@ -60,6 +60,27 @@ module AbstractController ) end + def test_subdomain_may_be_changed + add_host! + assert_equal('http://api.basecamphq.com/c/a/i', + W.new.url_for(:subdomain => 'api', :controller => 'c', :action => 'a', :id => 'i') + ) + end + + def test_domain_may_be_changed + add_host! + assert_equal('http://www.37signals.com/c/a/i', + W.new.url_for(:domain => '37signals.com', :controller => 'c', :action => 'a', :id => 'i') + ) + end + + def test_tld_length_may_be_changed + add_host! + assert_equal('http://mobile.www.basecamphq.com/c/a/i', + W.new.url_for(:subdomain => 'mobile', :tld_length => 2, :controller => 'c', :action => 'a', :id => 'i') + ) + end + def test_port add_host! assert_equal('http://www.basecamphq.com:3000/c/a/i', diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 4764a8c2a8..d140ea8358 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -164,12 +164,12 @@ class RequestTest < ActiveSupport::TestCase assert !request.standard_port? end - test "port string" do + test "optional port" do request = stub_request 'HTTP_HOST' => 'www.example.org:80' - assert_equal "", request.port_string + assert_equal nil, request.optional_port request = stub_request 'HTTP_HOST' => 'www.example.org:8080' - assert_equal ":8080", request.port_string + assert_equal 8080, request.optional_port end test "full path" do -- cgit v1.2.3 From 56b12140246303440da18c0c5ea139b3b17b0282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 23 Nov 2010 10:05:47 +0100 Subject: Speed up subdomain and domain calculus. --- actionpack/lib/action_dispatch/routing/route_set.rb | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index d823fd710e..ebced9cabe 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -570,22 +570,14 @@ module ActionDispatch end def subdomain_and_domain(options) + return nil unless options[:subdomain] || options[:domain] tld_length = options[:tld_length] || ActionDispatch::Http::URL.tld_length - current_domain = ActionDispatch::Http::URL.extract_domain(options[:host], tld_length) - current_subdomain = ActionDispatch::Http::URL.extract_subdomain(options[:host], tld_length) - - domain_parts = if options[:subdomain] && options[:domain] - [options[:subdomain], options[:domain]] - elsif options[:subdomain] - [options[:subdomain], current_domain] - elsif options[:domain] - [current_subdomain, options[:domain]] - else - nil - end - - domain_parts ? domain_parts.join('.') : nil + host = "" + host << (options[:subdomain] || ActionDispatch::Http::URL.extract_subdomain(options[:host], tld_length)) + host << "." + host << (options[:domain] || ActionDispatch::Http::URL.extract_domain(options[:host], tld_length)) + host end def handle_positional_args(options) -- cgit v1.2.3 From da583df50c32d50261b682664fe43fd5e2f58f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 23 Nov 2010 10:09:24 +0100 Subject: Remove bazillion warnings from AP suite. --- actionpack/lib/action_dispatch/testing/integration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index e0a17130f8..8fe74c3c80 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -364,7 +364,7 @@ module ActionDispatch end def respond_to?(method, include_private = false) - @integration_session.respond_to?(method, include_private) || super + integration_session.respond_to?(method, include_private) || super end # Delegate unhandled messages to the current session instance. -- cgit v1.2.3 From fd64bba648762cfa6ffb603e0298502be873aa6f Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Tue, 23 Nov 2010 17:53:59 +0100 Subject: i18n guide: fix RedCloth artifacts that were rendering bad format and broken links on 2.3 warnings --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 1a83b84004..25c24ac7d7 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -127,7 +127,7 @@ If you want to translate your Rails application to a *single language other than However, you would probably like to *provide support for more locales* in your application. In such case, you need to set and pass the locale between requests. -WARNING: You may be tempted to store the chosen locale in a _session_ or a _cookie_. *Do not do so*. The locale should be transparent and a part of the URL. This way you don't break people's basic assumptions about the web itself: if you send a URL of some page to a friend, she should see the same page, same content. A fancy word for this would be that you're being "_RESTful_":http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about the RESTful approach in "Stefan Tilkov's articles":http://www.infoq.com/articles/rest-introduction. There may be some exceptions to this rule, which are discussed below. +WARNING: You may be tempted to store the chosen locale in a _session_ or a cookie. *Do not do so*. The locale should be transparent and a part of the URL. This way you don't break people's basic assumptions about the web itself: if you send a URL of some page to a friend, she should see the same page, same content. A fancy word for this would be that you're being "RESTful":http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about the RESTful approach in "Stefan Tilkov's articles":http://www.infoq.com/articles/rest-introduction. There may be some exceptions to this rule, which are discussed below. The _setting part_ is easy. You can set the locale in a +before_filter+ in the +ApplicationController+ like this: -- cgit v1.2.3 From 14055ea28285107af04ed0ce4ef0ec542a9a9530 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 07:38:08 +0800 Subject: No need to define a local var here. --- activerecord/lib/active_record/base.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index aa028c6f36..f9743b4fea 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1569,8 +1569,7 @@ MSG # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings). def attribute_present?(attribute) - value = read_attribute(attribute) - !value.blank? + !read_attribute(attribute).blank? end # Returns the column object for the named attribute. -- cgit v1.2.3 From a9e963d51d262a0693f0b83508c40a250421f826 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 07:38:58 +0800 Subject: Remove confusing parenthesis. --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f9743b4fea..18d30d2540 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1595,7 +1595,7 @@ MSG # Delegates to == def eql?(comparison_object) - self == (comparison_object) + self == comparison_object end # Delegates to id in order to allow two records of the same type and id to work with something like: -- cgit v1.2.3 From 9c161599ac1e37eedd56bece4d975dde8cdaa151 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 07:46:56 +0800 Subject: Remove uneeded local var definition. --- activerecord/lib/active_record/nested_attributes.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index f1d3eaed38..a2101a02eb 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -322,9 +322,8 @@ module ActiveRecord def assign_nested_attributes_for_one_to_one_association(association_name, attributes) options = self.nested_attributes_options[association_name] attributes = attributes.with_indifferent_access - check_existing_record = (options[:update_only] || !attributes['id'].blank?) - if check_existing_record && (record = send(association_name)) && + 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) -- cgit v1.2.3 From 861cdc4c59dabdc8cca42a9f29b7cccb3cd23505 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 07:48:49 +0800 Subject: Remove unneeded local var. --- activerecord/lib/active_record/nested_attributes.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index a2101a02eb..15f83a8579 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -446,8 +446,7 @@ module ActiveRecord end def raise_nested_attributes_record_not_found(association_name, record_id) - reflection = self.class.reflect_on_association(association_name) - raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{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 end end -- cgit v1.2.3 From b8df3a9197252ec62f390ef1f8cd0e0e827c6252 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:41:39 +0800 Subject: Use params default. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index f3fba9a3a9..0282493219 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -204,8 +204,7 @@ module ActiveRecord protected - def log(sql, name) - name ||= "SQL" + def log(sql, name = "SQL") @instrumenter.instrument("sql.active_record", :sql => sql, :name => name, :connection_id => object_id) do yield -- cgit v1.2.3 From 1b531b7ee2054beadb23853d79b15d588b9ffd88 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:35:11 +0800 Subject: Remove explicit return. --- activerecord/lib/active_record/associations/has_many_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 830a82980d..a682ec52d8 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -46,7 +46,7 @@ module ActiveRecord count = [ @reflection.options[:limit], count ].min end - return count + count end def has_cached_counter? -- cgit v1.2.3 From 63c9185b62076f416c63b472a356bf9faac143db Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:38:35 +0800 Subject: Remove explicit return and avoid creating local var. --- activerecord/lib/active_record/associations/has_many_association.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index a682ec52d8..6423536fb9 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -112,8 +112,7 @@ module ActiveRecord end def we_can_set_the_inverse_on_this?(record) - inverse = @reflection.inverse_of - return !inverse.nil? + !@reflection.inverse_of.nil? end end end -- cgit v1.2.3 From 6ffe0ef55bbabf59a533402a6420942c7397e441 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:43:28 +0800 Subject: Avoid creating local var. --- activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 5ca1923d89..c2cd9e8d5e 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -40,8 +40,7 @@ module ActiveRecord if @connection.respond_to?(:encoding) @connection.encoding.to_s else - encoding = @connection.execute('PRAGMA encoding') - encoding[0]['encoding'] + @connection.execute('PRAGMA encoding')[0]['encoding'] end end -- cgit v1.2.3 From d29d793c9096a732274298cdba07c654e9128484 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:51:01 +0800 Subject: Don't create local vars. --- activerecord/lib/active_record/locking/optimistic.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index bf626301f1..9e1a33a6bf 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -110,12 +110,9 @@ module ActiveRecord return super unless locking_enabled? if persisted? - lock_col = self.class.locking_column - previous_value = send(lock_col).to_i - table = self.class.arel_table predicate = table[self.class.primary_key].eq(id) - predicate = predicate.and(table[self.class.locking_column].eq(previous_value)) + predicate = predicate.and(table[self.class.locking_column].eq(send(self.class.locking_column).to_i)) affected_rows = self.class.unscoped.where(predicate).delete_all -- cgit v1.2.3 From e2bad8a2e7e020901ddb74c3404a1c339b5a99f9 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 21 Nov 2010 00:46:28 +0800 Subject: No need to create a variables to use them once. --- activerecord/lib/active_record/validations/uniqueness.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 3eba7510ac..853808eebf 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -14,17 +14,14 @@ module ActiveRecord def validate_each(record, attribute, value) finder_class = find_finder_class_for(record) - table = finder_class.unscoped - - table_name = record.class.quoted_table_name if value && record.class.serialized_attributes.key?(attribute.to_s) value = YAML.dump value end - sql, params = mount_sql_and_params(finder_class, table_name, attribute, value) + sql, params = mount_sql_and_params(finder_class, record.class.quoted_table_name, attribute, value) - relation = table.where(sql, *params) + relation = finder_class.unscoped.where(sql, *params) Array.wrap(options[:scope]).each do |scope_item| scope_value = record.send(scope_item) -- cgit v1.2.3 From 9f35799221c8a3c06b3f34a38525654b59598cfd Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 20 Nov 2010 13:37:54 -0300 Subject: Refactor && simplify count_records. --- activerecord/lib/active_record/associations/has_many_association.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 6423536fb9..23831e0b08 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -42,11 +42,7 @@ module ActiveRecord # documented side-effect of the method that may avoid an extra SELECT. @target ||= [] and loaded if count == 0 - if @reflection.options[:limit] - count = [ @reflection.options[:limit], count ].min - end - - count + @reflection.options[:limit] ? [@reflection.options[:limit], count].min : count end def has_cached_counter? -- cgit v1.2.3 From 0250c3eca40f5f2b3da698b11ff4f01f58db5a69 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 23 Nov 2010 22:51:51 +0100 Subject: copy-edits RDoc of 2fe43b6 --- actionpack/lib/action_dispatch/routing/url_for.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index 6c3fc5126a..d4db78a25a 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -119,8 +119,9 @@ module ActionDispatch # to split the domain from the host. # * :domain - Specifies the domain of the link, using the +tld_length+ # to split the subdomain from the host. - # * :tld_length - Optionally specify the tld length (only used if :subdomain - # or :domain are supplied). + # * :tld_length - Number of labels the TLD id composed of, only used if + # :subdomain or :domain are supplied. Defaults to + # ActionDispatch::Http::URL.tld_length, which in turn defaults to 1. # * :port - Optionally specify the port to connect to. # * :anchor - An anchor name to be appended to the path. # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2009/" -- cgit v1.2.3 From 769575300acb778ce812208073952109bc45a9fd Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 24 Nov 2010 09:19:27 +1100 Subject: Fix formatting issue with rake routes output for the namespace method --- actionpack/lib/action_dispatch/routing/mapper.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 879ccc5791..432a574419 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -588,13 +588,13 @@ module ActionDispatch # # This generates the following routes: # - # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"} - # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"} - # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"} - # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"} - # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"} - # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"} - # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"} + # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"} + # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"} + # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"} + # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"} + # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"} + # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"} + # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"} # === Supported options # # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ all default to the name of the namespace. -- cgit v1.2.3 From 04af510b5678ea7bd4cd170257e7e3ef4423f834 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 24 Nov 2010 09:21:25 +1100 Subject: Paths in ActionDispatch::Scoping documentation should be for /admin/posts, not /admin/photos --- actionpack/lib/action_dispatch/routing/mapper.rb | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 432a574419..37d14944d6 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -439,13 +439,13 @@ module ActionDispatch # This will create a number of routes for each of the posts and comments # controller. For Admin::PostsController, Rails will create: # - # GET /admin/photos - # GET /admin/photos/new - # POST /admin/photos - # GET /admin/photos/1 - # GET /admin/photos/1/edit - # PUT /admin/photos/1 - # DELETE /admin/photos/1 + # GET /admin/posts + # GET /admin/posts/new + # POST /admin/posts + # GET /admin/posts/1 + # GET /admin/posts/1/edit + # PUT /admin/posts/1 + # DELETE /admin/posts/1 # # If you want to route /posts (without the prefix /admin) to # Admin::PostsController, you could use @@ -473,13 +473,13 @@ module ActionDispatch # not use scope. In the last case, the following paths map to # PostsController: # - # GET /admin/photos - # GET /admin/photos/new - # POST /admin/photos - # GET /admin/photos/1 - # GET /admin/photos/1/edit - # PUT /admin/photos/1 - # DELETE /admin/photos/1 + # GET /admin/posts + # GET /admin/posts/new + # POST /admin/posts + # GET /admin/posts/1 + # GET /admin/posts/1/edit + # PUT /admin/posts/1 + # DELETE /admin/posts/1 module Scoping def initialize(*args) #:nodoc: @scope = {} -- cgit v1.2.3 From 2974f57aaedd22f44b387ec72ca9f42a2982bb02 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 24 Nov 2010 09:25:01 +1100 Subject: Remove non-sensical first couple of lines for scope method documentation --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 37d14944d6..8b1578769d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -486,8 +486,6 @@ module ActionDispatch super end - # Used to route /photos (without the prefix /admin) - # to Admin::PostsController: # === Supported options # [:module] # If you want to route /posts (without the prefix /admin) to -- cgit v1.2.3 From 01af50d8b6be7b167c30f29c7407ce88bdfc9b2a Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 24 Nov 2010 09:27:12 +1100 Subject: Add :module option documentation for the resources method --- actionpack/lib/action_dispatch/routing/mapper.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 8b1578769d..1f3a831b7f 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -967,6 +967,7 @@ module ActionDispatch # GET /photos/:id/edit # PUT /photos/:id # DELETE /photos/:id + # # === Supported options # [:path_names] # Allows you to change the paths of the seven default actions. @@ -975,6 +976,13 @@ module ActionDispatch # resources :posts, :path_names => { :new => "brand_new" } # # The above example will now change /posts/new to /posts/brand_new + # + # [:module] + # Set the module where the controller can be found. Defaults to nothing. + # + # resources :posts, :module => "admin" + # + # All requests to the posts resources will now go to +Admin::PostsController+. def resources(*resources, &block) options = resources.extract_options! -- cgit v1.2.3 From fd47a4bf438e37a20756e0341cd7cd4e472fd3cb Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 24 Nov 2010 09:28:21 +1100 Subject: Document the :path option for resources :posts --- actionpack/lib/action_dispatch/routing/mapper.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 1f3a831b7f..f4d31eeff1 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -983,6 +983,14 @@ module ActionDispatch # resources :posts, :module => "admin" # # All requests to the posts resources will now go to +Admin::PostsController+. + # + # [:path] + # + # Set a path prefix for this resource. + # + # resources :posts, :path => "admin" + # + # All actions for this resource will now be at +/admin/posts+. def resources(*resources, &block) options = resources.extract_options! -- cgit v1.2.3 From ca7b0a0d1a424aec0973fe22c299b8f04e309784 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 10:13:43 -0800 Subject: dup is working better --- activerecord/lib/active_record/base.rb | 12 +++++--- activerecord/test/cases/base_test.rb | 4 --- activerecord/test/cases/duplication_test.rb | 43 +++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 activerecord/test/cases/duplication_test.rb diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9e15784f61..0d3df938e6 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1420,7 +1420,7 @@ MSG @attributes = coder['attributes'] @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {} @readonly = @destroyed = @marked_for_destruction = false - @persisted = true + @persisted = false _run_find_callbacks _run_initialize_callbacks end @@ -1615,11 +1615,15 @@ MSG @attributes.frozen? end + def initialize_dup(other) + super + init_with 'attributes' => other.attributes + self + end + # Returns duplicated record with unfreezed attributes. def dup - obj = super - obj.instance_variable_set('@attributes', @attributes.dup) - obj + super end # Returns +true+ if the record is read only. Records loaded through joins with piggy-back diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 26f388ca46..c8efabed30 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1443,10 +1443,6 @@ class BasicsTest < ActiveRecord::TestCase ActiveRecord::Base.logger = original_logger end - def test_dup - assert !Minimalistic.new.freeze.dup.frozen? - end - def test_compute_type_success assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author') end diff --git a/activerecord/test/cases/duplication_test.rb b/activerecord/test/cases/duplication_test.rb new file mode 100644 index 0000000000..610894a03e --- /dev/null +++ b/activerecord/test/cases/duplication_test.rb @@ -0,0 +1,43 @@ +require "cases/helper" +require 'models/topic' + +module ActiveRecord + class DuplicationTest < ActiveRecord::TestCase + fixtures :topics + + def test_dup + assert !Minimalistic.new.freeze.dup.frozen? + end + + def test_dup_not_persisted + topic = Topic.first + duped = topic.dup + + assert !duped.persisted?, 'topic not persisted' + assert duped.new_record?, 'topic is new' + end + + def test_dup_has_no_id + topic = Topic.first + duped = topic.dup + assert_nil duped.id + end + + def test_clone_persisted + topic = Topic.first + cloned = topic.clone + assert cloned.persisted?, 'topic persisted' + assert !cloned.new_record?, 'topic is not new' + end + + def test_clone_keeps_frozen + topic = Topic.first + topic.freeze + + cloned = topic.clone + assert cloned.persisted?, 'topic persisted' + assert !cloned.new_record?, 'topic is not new' + assert cloned.frozen?, 'topic is frozen' + end + end +end -- cgit v1.2.3 From 5badf60d128cb958fa7a0e3f140517b71b88c7ac Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 10:58:19 -0800 Subject: dup keeps changes --- activerecord/lib/active_record/base.rb | 49 +++++++++++++---------------- activerecord/test/cases/duplication_test.rb | 19 +++++++++-- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 0d3df938e6..be8ebd4d59 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1382,30 +1382,6 @@ MSG result end - # Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone - # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is - # application specific and is therefore left to the application to implement according to its need. - def initialize_copy(other) - _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) - cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) - cloned_attributes.delete(self.class.primary_key) - - @attributes = cloned_attributes - - @changed_attributes = {} - attributes_from_column_definition.each do |attr, orig_value| - @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr]) - end - - clear_aggregation_cache - clear_association_cache - @attributes_cache = {} - @persisted = false - ensure_proper_type - - populate_with_current_scope_attributes - end - # Initialize an empty model object from +coder+. +coder+ must contain # the attributes necessary for initializing an empty model object. For # example: @@ -1420,7 +1396,7 @@ MSG @attributes = coder['attributes'] @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {} @readonly = @destroyed = @marked_for_destruction = false - @persisted = false + @persisted = true _run_find_callbacks _run_initialize_callbacks end @@ -1615,15 +1591,32 @@ MSG @attributes.frozen? end + # Duped objects have no id assigned and are treated as new records. Note + # that this is a "shallow" clone as it copies the object's attributes + # only, not its associations. The extent of a "deep" dup is application + # specific and is therefore left to the application to implement according + # to its need. def initialize_dup(other) super - init_with 'attributes' => other.attributes + cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) + cloned_attributes.delete(self.class.primary_key) + + @attributes = cloned_attributes + @changed_attributes = other.changed_attributes.dup + @attributes_cache = {} + @persisted = false + + _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) + clear_aggregation_cache + clear_association_cache + ensure_proper_type + populate_with_current_scope_attributes self end - # Returns duplicated record with unfreezed attributes. - def dup + def initialize_clone(other) super + @persisted = other.persisted? end # Returns +true+ if the record is read only. Records loaded through joins with piggy-back diff --git a/activerecord/test/cases/duplication_test.rb b/activerecord/test/cases/duplication_test.rb index 610894a03e..f9d614230b 100644 --- a/activerecord/test/cases/duplication_test.rb +++ b/activerecord/test/cases/duplication_test.rb @@ -6,7 +6,7 @@ module ActiveRecord fixtures :topics def test_dup - assert !Minimalistic.new.freeze.dup.frozen? + assert !Topic.new.freeze.dup.frozen? end def test_dup_not_persisted @@ -26,6 +26,7 @@ module ActiveRecord def test_clone_persisted topic = Topic.first cloned = topic.clone + assert topic.persisted?, 'topic persisted' assert cloned.persisted?, 'topic persisted' assert !cloned.new_record?, 'topic is not new' end @@ -37,7 +38,21 @@ module ActiveRecord cloned = topic.clone assert cloned.persisted?, 'topic persisted' assert !cloned.new_record?, 'topic is not new' - assert cloned.frozen?, 'topic is frozen' + assert cloned.frozen?, 'topic should be frozen' + end + + def test_dup_with_modified_attributes + topic = Topic.first + topic.author_name = 'Aaron' + duped = topic.dup + assert_equal 'Aaron', duped.author_name + end + + def test_dup_with_changes + topic = Topic.first + topic.author_name = 'Aaron' + duped = topic.dup + assert_equal topic.changes, duped.changes end end end -- cgit v1.2.3 From 035d00b665a645a12ecd1f9c70dba06bbd4f6201 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 10:59:23 -0800 Subject: making sure changes to dup will not touch original --- activerecord/test/cases/duplication_test.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/activerecord/test/cases/duplication_test.rb b/activerecord/test/cases/duplication_test.rb index f9d614230b..c6f7e3ef16 100644 --- a/activerecord/test/cases/duplication_test.rb +++ b/activerecord/test/cases/duplication_test.rb @@ -54,5 +54,15 @@ module ActiveRecord duped = topic.dup assert_equal topic.changes, duped.changes end + + def test_dup_topics_are_independent + topic = Topic.first + topic.author_name = 'Aaron' + duped = topic.dup + + duped.author_name = 'meow' + + assert_not_equal topic.changes, duped.changes + end end end -- cgit v1.2.3 From 4c1f76eaab670ffa95d185374ea91f0d2e2818c7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 10:59:58 -0800 Subject: initialize_clone can go away --- activerecord/lib/active_record/base.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index be8ebd4d59..6f0e71cc7b 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1614,11 +1614,6 @@ MSG self end - def initialize_clone(other) - super - @persisted = other.persisted? - end - # Returns +true+ if the record is read only. Records loaded through joins with piggy-back # attributes will be marked as read only since they cannot be saved. def readonly? -- cgit v1.2.3 From 28f4df0908180783f8cf863a8bbb30c1c139bd8d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 11:10:56 -0800 Subject: testing duped attributes are independent --- activerecord/test/cases/duplication_test.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/activerecord/test/cases/duplication_test.rb b/activerecord/test/cases/duplication_test.rb index c6f7e3ef16..1ca6e72db0 100644 --- a/activerecord/test/cases/duplication_test.rb +++ b/activerecord/test/cases/duplication_test.rb @@ -41,6 +41,13 @@ module ActiveRecord assert cloned.frozen?, 'topic should be frozen' end + def test_clone_is_shallow + topic = Topic.first + cloned = topic.clone + topic.author_name = 'Aaron' + assert_equal 'Aaron', cloned.author_name + end + def test_dup_with_modified_attributes topic = Topic.first topic.author_name = 'Aaron' @@ -64,5 +71,16 @@ module ActiveRecord assert_not_equal topic.changes, duped.changes end + + def test_dup_attributes_are_independent + topic = Topic.first + duped = topic.dup + + duped.author_name = 'meow' + topic.author_name = 'Aaron' + + assert_equal 'Aaron', topic.author_name + assert_equal 'meow', duped.author_name + end end end -- cgit v1.2.3 From c5858a6df3efefbab8925623b1e6c064c476fb26 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 11:12:46 -0800 Subject: adding a specific case for clone testing --- activerecord/test/cases/clone_test.rb | 33 +++++++++++++++++++++++++++++ activerecord/test/cases/duplication_test.rb | 25 ---------------------- 2 files changed, 33 insertions(+), 25 deletions(-) create mode 100644 activerecord/test/cases/clone_test.rb diff --git a/activerecord/test/cases/clone_test.rb b/activerecord/test/cases/clone_test.rb new file mode 100644 index 0000000000..d91646efca --- /dev/null +++ b/activerecord/test/cases/clone_test.rb @@ -0,0 +1,33 @@ +require "cases/helper" +require 'models/topic' + +module ActiveRecord + class CloneTest < ActiveRecord::TestCase + fixtures :topics + + def test_persisted + topic = Topic.first + cloned = topic.clone + assert topic.persisted?, 'topic persisted' + assert cloned.persisted?, 'topic persisted' + assert !cloned.new_record?, 'topic is not new' + end + + def test_stays_frozen + topic = Topic.first + topic.freeze + + cloned = topic.clone + assert cloned.persisted?, 'topic persisted' + assert !cloned.new_record?, 'topic is not new' + assert cloned.frozen?, 'topic should be frozen' + end + + def test_shallow + topic = Topic.first + cloned = topic.clone + topic.author_name = 'Aaron' + assert_equal 'Aaron', cloned.author_name + end + end +end diff --git a/activerecord/test/cases/duplication_test.rb b/activerecord/test/cases/duplication_test.rb index 1ca6e72db0..ecea843bbf 100644 --- a/activerecord/test/cases/duplication_test.rb +++ b/activerecord/test/cases/duplication_test.rb @@ -23,31 +23,6 @@ module ActiveRecord assert_nil duped.id end - def test_clone_persisted - topic = Topic.first - cloned = topic.clone - assert topic.persisted?, 'topic persisted' - assert cloned.persisted?, 'topic persisted' - assert !cloned.new_record?, 'topic is not new' - end - - def test_clone_keeps_frozen - topic = Topic.first - topic.freeze - - cloned = topic.clone - assert cloned.persisted?, 'topic persisted' - assert !cloned.new_record?, 'topic is not new' - assert cloned.frozen?, 'topic should be frozen' - end - - def test_clone_is_shallow - topic = Topic.first - cloned = topic.clone - topic.author_name = 'Aaron' - assert_equal 'Aaron', cloned.author_name - end - def test_dup_with_modified_attributes topic = Topic.first topic.author_name = 'Aaron' -- cgit v1.2.3 From 6a8f1b8b402b31ae43872c9d1f37b931c85799e7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 11:13:24 -0800 Subject: renaming duplication test to dup test --- activerecord/test/cases/dup_test.rb | 61 +++++++++++++++++++++++++++++ activerecord/test/cases/duplication_test.rb | 61 ----------------------------- 2 files changed, 61 insertions(+), 61 deletions(-) create mode 100644 activerecord/test/cases/dup_test.rb delete mode 100644 activerecord/test/cases/duplication_test.rb diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb new file mode 100644 index 0000000000..fa528c4936 --- /dev/null +++ b/activerecord/test/cases/dup_test.rb @@ -0,0 +1,61 @@ +require "cases/helper" +require 'models/topic' + +module ActiveRecord + class DupTest < ActiveRecord::TestCase + fixtures :topics + + def test_dup + assert !Topic.new.freeze.dup.frozen? + end + + def test_dup_not_persisted + topic = Topic.first + duped = topic.dup + + assert !duped.persisted?, 'topic not persisted' + assert duped.new_record?, 'topic is new' + end + + def test_dup_has_no_id + topic = Topic.first + duped = topic.dup + assert_nil duped.id + end + + def test_dup_with_modified_attributes + topic = Topic.first + topic.author_name = 'Aaron' + duped = topic.dup + assert_equal 'Aaron', duped.author_name + end + + def test_dup_with_changes + topic = Topic.first + topic.author_name = 'Aaron' + duped = topic.dup + assert_equal topic.changes, duped.changes + end + + def test_dup_topics_are_independent + topic = Topic.first + topic.author_name = 'Aaron' + duped = topic.dup + + duped.author_name = 'meow' + + assert_not_equal topic.changes, duped.changes + end + + def test_dup_attributes_are_independent + topic = Topic.first + duped = topic.dup + + duped.author_name = 'meow' + topic.author_name = 'Aaron' + + assert_equal 'Aaron', topic.author_name + assert_equal 'meow', duped.author_name + end + end +end diff --git a/activerecord/test/cases/duplication_test.rb b/activerecord/test/cases/duplication_test.rb deleted file mode 100644 index ecea843bbf..0000000000 --- a/activerecord/test/cases/duplication_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -require "cases/helper" -require 'models/topic' - -module ActiveRecord - class DuplicationTest < ActiveRecord::TestCase - fixtures :topics - - def test_dup - assert !Topic.new.freeze.dup.frozen? - end - - def test_dup_not_persisted - topic = Topic.first - duped = topic.dup - - assert !duped.persisted?, 'topic not persisted' - assert duped.new_record?, 'topic is new' - end - - def test_dup_has_no_id - topic = Topic.first - duped = topic.dup - assert_nil duped.id - end - - def test_dup_with_modified_attributes - topic = Topic.first - topic.author_name = 'Aaron' - duped = topic.dup - assert_equal 'Aaron', duped.author_name - end - - def test_dup_with_changes - topic = Topic.first - topic.author_name = 'Aaron' - duped = topic.dup - assert_equal topic.changes, duped.changes - end - - def test_dup_topics_are_independent - topic = Topic.first - topic.author_name = 'Aaron' - duped = topic.dup - - duped.author_name = 'meow' - - assert_not_equal topic.changes, duped.changes - end - - def test_dup_attributes_are_independent - topic = Topic.first - duped = topic.dup - - duped.author_name = 'meow' - topic.author_name = 'Aaron' - - assert_equal 'Aaron', topic.author_name - assert_equal 'meow', duped.author_name - end - end -end -- cgit v1.2.3 From 93d78b831831a1c8e324d4c5404b99e81fe77725 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 11:30:14 -0800 Subject: fixing more dup tests --- activerecord/lib/active_record/base.rb | 8 +++---- activerecord/test/cases/base_test.rb | 43 +++++++++++++++++----------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 6f0e71cc7b..f9de4b7918 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1598,20 +1598,20 @@ MSG # to its need. def initialize_dup(other) super + _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) @attributes = cloned_attributes @changed_attributes = other.changed_attributes.dup - @attributes_cache = {} - @persisted = false - _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) clear_aggregation_cache clear_association_cache + @attributes_cache = {} + @persisted = false + ensure_proper_type populate_with_current_scope_attributes - self end # Returns +true+ if the record is read only. Records loaded through joins with piggy-back diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index c8efabed30..e3f4f2bd08 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -669,36 +669,36 @@ class BasicsTest < ActiveRecord::TestCase assert_equal true, Topic.find(1).persisted? end - def test_clone + def test_dup topic = Topic.find(1) - cloned_topic = nil - assert_nothing_raised { cloned_topic = topic.clone } - assert_equal topic.title, cloned_topic.title - assert !cloned_topic.persisted? + duped_topic = nil + assert_nothing_raised { duped_topic = topic.dup } + assert_equal topic.title, duped_topic.title + assert !duped_topic.persisted? - # test if the attributes have been cloned + # test if the attributes have been duped topic.title = "a" - cloned_topic.title = "b" + duped_topic.title = "b" assert_equal "a", topic.title - assert_equal "b", cloned_topic.title + assert_equal "b", duped_topic.title - # test if the attribute values have been cloned + # test if the attribute values have been duped topic.title = {"a" => "b"} - cloned_topic = topic.clone - cloned_topic.title["a"] = "c" + duped_topic = topic.dup + duped_topic.title["a"] = "c" assert_equal "b", topic.title["a"] - # test if attributes set as part of after_initialize are cloned correctly - assert_equal topic.author_email_address, cloned_topic.author_email_address + # test if attributes set as part of after_initialize are duped correctly + assert_equal topic.author_email_address, duped_topic.author_email_address # test if saved clone object differs from original - cloned_topic.save - assert cloned_topic.persisted? - assert_not_equal cloned_topic.id, topic.id + duped_topic.save + assert duped_topic.persisted? + assert_not_equal duped_topic.id, topic.id - cloned_topic.reload + duped_topic.reload # FIXME: I think this is poor behavior, and will fix it with #5686 - assert_equal({'a' => 'c'}.to_s, cloned_topic.title) + assert_equal({'a' => 'c'}.to_s, duped_topic.title) end def test_clone_with_aggregate_of_same_name_as_attribute @@ -721,12 +721,13 @@ class BasicsTest < ActiveRecord::TestCase assert_not_equal clone.id, dev.id end - def test_clone_does_not_clone_associations + def test_dup_does_not_copy_associations author = authors(:david) assert_not_equal [], author.posts + author.send(:clear_association_cache) - author_clone = author.clone - assert_equal [], author_clone.posts + author_dup = author.dup + assert_equal [], author_dup.posts end def test_clone_preserves_subtype -- cgit v1.2.3 From 064c28d6c290cb9b6222b2348e38e713b82a89d6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 11:57:33 -0800 Subject: fixing dup regressions --- activerecord/lib/active_record/base.rb | 12 +++++++---- activerecord/test/cases/base_test.rb | 32 ++++++++++++++--------------- activerecord/test/cases/dirty_test.rb | 8 ++++---- activerecord/test/cases/dup_test.rb | 9 +++++--- activerecord/test/cases/inheritance_test.rb | 2 +- 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f9de4b7918..7cc4f957fe 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1592,8 +1592,8 @@ MSG end # Duped objects have no id assigned and are treated as new records. Note - # that this is a "shallow" clone as it copies the object's attributes - # only, not its associations. The extent of a "deep" dup is application + # that this is a "shallow" copy as it copies the object's attributes + # only, not its associations. The extent of a "deep" copy is application # specific and is therefore left to the application to implement according # to its need. def initialize_dup(other) @@ -1602,8 +1602,12 @@ MSG cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) - @attributes = cloned_attributes - @changed_attributes = other.changed_attributes.dup + @attributes = cloned_attributes + + @changed_attributes = {} + attributes_from_column_definition.each do |attr, orig_value| + @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr]) + end clear_aggregation_cache clear_association_cache diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index e3f4f2bd08..c3ba1f0c35 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -701,24 +701,24 @@ class BasicsTest < ActiveRecord::TestCase assert_equal({'a' => 'c'}.to_s, duped_topic.title) end - def test_clone_with_aggregate_of_same_name_as_attribute + def test_dup_with_aggregate_of_same_name_as_attribute dev = DeveloperWithAggregate.find(1) assert_kind_of DeveloperSalary, dev.salary - clone = nil - assert_nothing_raised { clone = dev.clone } - assert_kind_of DeveloperSalary, clone.salary - assert_equal dev.salary.amount, clone.salary.amount - assert !clone.persisted? + dup = nil + assert_nothing_raised { dup = dev.dup } + assert_kind_of DeveloperSalary, dup.salary + assert_equal dev.salary.amount, dup.salary.amount + assert !dup.persisted? - # test if the attributes have been cloned - original_amount = clone.salary.amount + # test if the attributes have been dupd + original_amount = dup.salary.amount dev.salary.amount = 1 - assert_equal original_amount, clone.salary.amount + assert_equal original_amount, dup.salary.amount - assert clone.save - assert clone.persisted? - assert_not_equal clone.id, dev.id + assert dup.save + assert dup.persisted? + assert_not_equal dup.id, dev.id end def test_dup_does_not_copy_associations @@ -766,22 +766,22 @@ class BasicsTest < ActiveRecord::TestCase assert !cloned_developer.salary_changed? # ... and cloned instance should behave same end - def test_clone_of_saved_object_marks_attributes_as_dirty + def test_dup_of_saved_object_marks_attributes_as_dirty developer = Developer.create! :name => 'Bjorn', :salary => 100000 assert !developer.name_changed? assert !developer.salary_changed? - cloned_developer = developer.clone + cloned_developer = developer.dup assert cloned_developer.name_changed? # both attributes differ from defaults assert cloned_developer.salary_changed? end - def test_clone_of_saved_object_marks_as_dirty_only_changed_attributes + def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes developer = Developer.create! :name => 'Bjorn' assert !developer.name_changed? # both attributes of saved object should be threated as not changed assert !developer.salary_changed? - cloned_developer = developer.clone + cloned_developer = developer.dup assert cloned_developer.name_changed? # ... but on cloned object should be assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be threated as not changed on cloned instance end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index b1a54af192..a6738fb654 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -338,13 +338,13 @@ class DirtyTest < ActiveRecord::TestCase assert !pirate.changed? end - def test_cloned_objects_should_not_copy_dirty_flag_from_creator + def test_dup_objects_should_not_copy_dirty_flag_from_creator pirate = Pirate.create!(:catchphrase => "shiver me timbers") - pirate_clone = pirate.clone - pirate_clone.reset_catchphrase! + pirate_dup = pirate.dup + pirate_dup.reset_catchphrase! pirate.catchphrase = "I love Rum" assert pirate.catchphrase_changed? - assert !pirate_clone.catchphrase_changed? + assert !pirate_dup.catchphrase_changed? end def test_reverted_changes_are_not_dirty diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index fa528c4936..768474e4ac 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -31,9 +31,12 @@ module ActiveRecord end def test_dup_with_changes - topic = Topic.first - topic.author_name = 'Aaron' - duped = topic.dup + dbtopic = Topic.first + topic = Topic.new + + topic.attributes = dbtopic.attributes + + duped = dbtopic.dup assert_equal topic.changes, duped.changes end diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 31679b2efe..c3da9cdf53 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -16,7 +16,7 @@ class InheritanceTest < ActiveRecord::TestCase def test_class_with_blank_sti_name company = Company.find(:first) - company = company.clone + company = company.dup company.extend(Module.new { def read_attribute(name) return ' ' if name == 'type' -- cgit v1.2.3 From fe4388eb15d44612710f2fc2f81c90a890278b23 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 12:00:17 -0800 Subject: converting clone test to dup test --- activerecord/test/cases/autosave_association_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index b13cb2d7a2..fbf7121468 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -163,15 +163,15 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas firm.account = Account.find(:first) assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! } - firm = Firm.find(:first).clone + firm = Firm.find(:first).dup firm.account = Account.find(:first) assert_queries(2) { firm.save! } - firm = Firm.find(:first).clone - firm.account = Account.find(:first).clone + firm = Firm.find(:first).dup + firm.account = Account.find(:first).dup assert_queries(2) { firm.save! } end - + def test_callbacks_firing_order_on_create eye = Eye.create(:iris_attributes => {:color => 'honey'}) assert_equal [true, false], eye.after_create_callbacks_stack -- cgit v1.2.3 From d717cb29136b8e4f557e6f6ddf076ae3de8476fc Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 13:38:20 -0800 Subject: clone and dup are working on 1.8 --- activerecord/lib/active_record/base.rb | 15 ++++++++++++--- activerecord/test/cases/dup_test.rb | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7cc4f957fe..d2fa3bed35 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1591,19 +1591,28 @@ MSG @attributes.frozen? end + # Backport dup from 1.9 so that initialize_dup() gets called + unless Object.respond_to?(:initialize_dup) + def dup # :nodoc: + copy = super + copy.initialize_dup(self) + copy + end + end + # Duped objects have no id assigned and are treated as new records. Note # that this is a "shallow" copy as it copies the object's attributes # only, not its associations. The extent of a "deep" copy is application # specific and is therefore left to the application to implement according # to its need. def initialize_dup(other) - super - _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) @attributes = cloned_attributes + _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) + @changed_attributes = {} attributes_from_column_definition.each do |attr, orig_value| @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr]) @@ -1612,7 +1621,7 @@ MSG clear_aggregation_cache clear_association_cache @attributes_cache = {} - @persisted = false + @persisted = false ensure_proper_type populate_with_current_scope_attributes diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index 768474e4ac..a1fc639290 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -9,6 +9,21 @@ module ActiveRecord assert !Topic.new.freeze.dup.frozen? end + def test_not_readonly + topic = Topic.first + + duped = topic.dup + assert !topic.readonly?, 'should not be readonly' + end + + def test_is_readonly + topic = Topic.first + topic.readonly! + + duped = topic.dup + assert topic.readonly?, 'should be readonly' + end + def test_dup_not_persisted topic = Topic.first duped = topic.dup -- cgit v1.2.3 From 0456feab1198456069d4b5a9da221e6fd818e3da Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 14:13:03 -0800 Subject: updating changelog for dup and clone semantics --- activerecord/CHANGELOG | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index a3e3051b96..f46db909ba 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,16 @@ *Rails 3.1.0 (unreleased)* +* ActiveRecord::Base#dup and ActiveRecord::Base#clone semantics have changed +to closer match normal Ruby dup and clone semantics. + +* Calling ActiveRecord::Base#clone will result in a shallow copy of the record, +including copying the frozen state. No callbacks will be called. + +* Calling ActiveRecord::Base#dup will duplicate the record, including calling +after initialize hooks. Frozen state will not be copied, and all associations +will be cleared. A duped record will return true for new_record?, have a nil +id field, and is saveable. + * Migrations can be defined as reversible, meaning that the migration system will figure out how to reverse your migration. To use reversible migrations, just define the "change" method. For example: -- cgit v1.2.3 From e4a9af186a7316e2de018ff9306442b9fe0f2400 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 24 Nov 2010 09:31:29 +1100 Subject: Add mention of nested resource to resources documentation --- actionpack/lib/action_dispatch/routing/mapper.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index f4d31eeff1..fb1f0d3f0c 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -968,6 +968,21 @@ module ActionDispatch # PUT /photos/:id # DELETE /photos/:id # + # Resources can also be nested infinitely by using this block syntax: + # + # resources :photos do + # resources :comments + # end + # + # This generates the following comments routes: + # + # GET /photos/:id/comments/new + # POST /photos/:id/comments + # GET /photos/:id/comments/:id + # GET /photos/:id/comments/:id/edit + # PUT /photos/:id/comments/:id + # DELETE /photos/:id/comments/:id + # # === Supported options # [:path_names] # Allows you to change the paths of the seven default actions. -- cgit v1.2.3 From d33dcba72d19beffc4a359f2fb89659f24122e9a Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 23 Nov 2010 14:58:10 -0500 Subject: Do not send id for quoting twice if the primary key is string. [#6022 state:resolved] --- activerecord/lib/active_record/relation/predicate_builder.rb | 3 ++- activerecord/test/cases/relations_test.rb | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 32c7d08daa..7e2ce06dae 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -26,7 +26,8 @@ module ActiveRecord when Range, Arel::Relation attribute.in(value) when ActiveRecord::Base - attribute.eq(value.quoted_id) + sanitized_id = attribute.class == Arel::Attributes::String ? value.id : value.quoted_id + attribute.eq(sanitized_id) when Class # FIXME: I think we need to deprecate this behavior attribute.eq(value.name) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 535bcd4396..1682f34a1d 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -14,11 +14,18 @@ require 'models/bird' require 'models/car' require 'models/engine' require 'models/tyre' +require 'models/minivan' class RelationTest < ActiveRecord::TestCase fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments, - :tags, :taggings, :cars + :tags, :taggings, :cars, :minivans + + def test_do_not_double_quote_string_id + van = Minivan.last + assert van + assert_equal van.id, Minivan.where(:minivan_id => van).to_a.first.minivan_id + end def test_bind_values relation = Post.scoped -- cgit v1.2.3 From 8e8fb8a4291cc288b1189338f0de643c409eb028 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 14:42:55 -0800 Subject: just wrap as a sql literal --- activerecord/lib/active_record/relation/predicate_builder.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 7e2ce06dae..70d84619a1 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -26,8 +26,7 @@ module ActiveRecord when Range, Arel::Relation attribute.in(value) when ActiveRecord::Base - sanitized_id = attribute.class == Arel::Attributes::String ? value.id : value.quoted_id - attribute.eq(sanitized_id) + attribute.eq(Arel.sql(value.quoted_id)) when Class # FIXME: I think we need to deprecate this behavior attribute.eq(value.name) -- cgit v1.2.3 From 8882e65ac8cd499f09bacee4bc4f3f4c328fae05 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 24 Nov 2010 05:49:41 +0800 Subject: No need to return anything --- .../lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb index 15f8e10b7f..e52797042f 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb @@ -79,7 +79,6 @@ module ActionView sources.each do |source| asset_file_path!(path_to_asset(source, false)) end - return sources end def collect_asset_files(*path) -- cgit v1.2.3 From a551d2ec62f205a755d3b760a9943f20addafe35 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 17:28:26 -0800 Subject: fixing variable names. oops! --- activerecord/test/cases/dup_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index a1fc639290..46abe7792c 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -13,7 +13,7 @@ module ActiveRecord topic = Topic.first duped = topic.dup - assert !topic.readonly?, 'should not be readonly' + assert !duped.readonly?, 'should not be readonly' end def test_is_readonly @@ -21,7 +21,7 @@ module ActiveRecord topic.readonly! duped = topic.dup - assert topic.readonly?, 'should be readonly' + assert duped.readonly?, 'should be readonly' end def test_dup_not_persisted -- cgit v1.2.3 From 1aaa8edeb958a263c7a256344f442867c4b90c5f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 17:34:18 -0800 Subject: breaking classes up in to respective files --- activerecord/lib/active_record/associations.rb | 575 +-------------------- .../associations/class_methods/join_dependency.rb | 216 ++++++++ .../join_dependency/join_association.rb | 278 ++++++++++ .../class_methods/join_dependency/join_base.rb | 34 ++ .../class_methods/join_dependency/join_part.rb | 76 +++ 5 files changed, 605 insertions(+), 574 deletions(-) create mode 100644 activerecord/lib/active_record/associations/class_methods/join_dependency.rb create mode 100644 activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb create mode 100644 activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb create mode 100644 activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7da38cd03f..0d9171d876 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -5,6 +5,7 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/module/remove_method' require 'active_support/core_ext/class/attribute' +require 'active_record/associations/class_methods/join_dependency' module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: @@ -1832,580 +1833,6 @@ module ActiveRecord Array.wrap(extensions) end end - - class JoinDependency # :nodoc: - attr_reader :join_parts, :reflections, :table_aliases - - def initialize(base, associations, joins) - @join_parts = [JoinBase.new(base, joins)] - @associations = {} - @reflections = [] - @table_aliases = Hash.new(0) - @table_aliases[base.table_name] = 1 - build(associations) - end - - def graft(*associations) - associations.each do |association| - join_associations.detect {|a| association == a} || - build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type) - end - self - end - - def join_associations - join_parts.last(join_parts.length - 1) - end - - def join_base - join_parts.first - end - - def count_aliases_from_table_joins(name) - # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase - quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase - join_sql = join_base.table_joins.to_s.downcase - join_sql.blank? ? 0 : - # Table names - join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + - # Table aliases - join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size - end - - def instantiate(rows) - primary_key = join_base.aliased_primary_key - parents = {} - - records = rows.map { |model| - primary_id = model[primary_key] - parent = parents[primary_id] ||= join_base.instantiate(model) - construct(parent, @associations, join_associations.dup, model) - parent - }.uniq - - remove_duplicate_results!(join_base.active_record, records, @associations) - records - end - - def remove_duplicate_results!(base, records, associations) - case associations - when Symbol, String - reflection = base.reflections[associations] - remove_uniq_by_reflection(reflection, records) - when Array - associations.each do |association| - remove_duplicate_results!(base, records, association) - end - when Hash - associations.keys.each do |name| - reflection = base.reflections[name] - remove_uniq_by_reflection(reflection, records) - - parent_records = [] - records.each do |record| - if descendant = record.send(reflection.name) - if reflection.collection? - parent_records.concat descendant.target.uniq - else - parent_records << descendant - end - end - end - - remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty? - end - end - end - - protected - - def cache_joined_association(association) - associations = [] - parent = association.parent - while parent != join_base - associations.unshift(parent.reflection.name) - parent = parent.parent - end - ref = @associations - associations.each do |key| - ref = ref[key] - end - ref[association.reflection.name] ||= {} - end - - def build(associations, parent = nil, join_type = Arel::InnerJoin) - parent ||= join_parts.last - case associations - when Symbol, String - reflection = parent.reflections[associations.to_s.intern] or - raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?" - unless join_association = find_join_association(reflection, parent) - @reflections << reflection - join_association = build_join_association(reflection, parent) - join_association.join_type = join_type - @join_parts << join_association - cache_joined_association(join_association) - end - join_association - when Array - associations.each do |association| - build(association, parent, join_type) - end - when Hash - associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name| - join_association = build(name, parent, join_type) - build(associations[name], join_association, join_type) - end - else - raise ConfigurationError, associations.inspect - end - end - - def find_join_association(name_or_reflection, parent) - if String === name_or_reflection - name_or_reflection = name_or_reflection.to_sym - end - - join_associations.detect { |j| - j.reflection == name_or_reflection && j.parent == parent - } - end - - def remove_uniq_by_reflection(reflection, records) - if reflection && reflection.collection? - records.each { |record| record.send(reflection.name).target.uniq! } - end - end - - def build_join_association(reflection, parent) - JoinAssociation.new(reflection, self, parent) - end - - def construct(parent, associations, join_parts, row) - case associations - when Symbol, String - name = associations.to_s - - join_part = join_parts.detect { |j| - j.reflection.name.to_s == name && - j.parent_table_name == parent.class.table_name } - - raise(ConfigurationError, "No such association") unless join_part - - join_parts.delete(join_part) - construct_association(parent, join_part, row) - when Array - associations.each do |association| - construct(parent, association, join_parts, row) - end - when Hash - associations.sort_by { |k,_| k.to_s }.each do |name, assoc| - association = construct(parent, name, join_parts, row) - construct(association, assoc, join_parts, row) if association - end - else - raise ConfigurationError, associations.inspect - end - end - - def construct_association(record, join_part, row) - return if record.id.to_s != join_part.parent.record_id(row).to_s - - macro = join_part.reflection.macro - if macro == :has_one - return if record.instance_variable_defined?("@#{join_part.reflection.name}") - association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil? - set_target_and_inverse(join_part, association, record) - else - return if row[join_part.aliased_primary_key].nil? - association = join_part.instantiate(row) - case macro - when :has_many, :has_and_belongs_to_many - collection = record.send(join_part.reflection.name) - collection.loaded - collection.target.push(association) - collection.__send__(:set_inverse_instance, association, record) - when :belongs_to - set_target_and_inverse(join_part, association, record) - else - raise ConfigurationError, "unknown macro: #{join_part.reflection.macro}" - end - end - association - end - - def set_target_and_inverse(join_part, association, record) - association_proxy = record.send("set_#{join_part.reflection.name}_target", association) - association_proxy.__send__(:set_inverse_instance, association, record) - end - - # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited - # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which - # everything else is being joined onto. A JoinAssociation represents an association which - # is joining to the base. A JoinAssociation may result in more than one actual join - # operations (for example a has_and_belongs_to_many JoinAssociation would result in - # two; one for the join table and one for the target table). - class JoinPart # :nodoc: - # The Active Record class which this join part is associated 'about'; for a JoinBase - # this is the actual base model, for a JoinAssociation this is the target model of the - # association. - attr_reader :active_record - - delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record - - def initialize(active_record) - @active_record = active_record - @cached_record = {} - end - - def ==(other) - raise NotImplementedError - end - - # An Arel::Table for the active_record - def table - raise NotImplementedError - end - - # The prefix to be used when aliasing columns in the active_record's table - def aliased_prefix - raise NotImplementedError - end - - # The alias for the active_record's table - def aliased_table_name - raise NotImplementedError - end - - # The alias for the primary key of the active_record's table - def aliased_primary_key - "#{aliased_prefix}_r0" - end - - # An array of [column_name, alias] pairs for the table - def column_names_with_alias - unless defined?(@column_names_with_alias) - @column_names_with_alias = [] - - ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i| - @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] - end - end - - @column_names_with_alias - end - - def extract_record(row) - Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}] - end - - def record_id(row) - row[aliased_primary_key] - end - - def instantiate(row) - @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row)) - end - end - - class JoinBase < JoinPart # :nodoc: - # Extra joins provided when the JoinDependency was created - attr_reader :table_joins - - def initialize(active_record, joins = nil) - super(active_record) - @table_joins = joins - end - - def ==(other) - other.class == self.class && - other.active_record == active_record - end - - def aliased_prefix - "t0" - end - - def table - Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns) - end - - def aliased_table_name - active_record.table_name - end - end - - class JoinAssociation < JoinPart # :nodoc: - # The reflection of the association represented - attr_reader :reflection - - # The JoinDependency object which this JoinAssociation exists within. This is mainly - # relevant for generating aliases which do not conflict with other joins which are - # part of the query. - attr_reader :join_dependency - - # A JoinBase instance representing the active record we are joining onto. - # (So in Author.has_many :posts, the Author would be that base record.) - attr_reader :parent - - # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin - attr_accessor :join_type - - # These implement abstract methods from the superclass - attr_reader :aliased_prefix, :aliased_table_name - - delegate :options, :through_reflection, :source_reflection, :to => :reflection - delegate :table, :table_name, :to => :parent, :prefix => true - - def initialize(reflection, join_dependency, parent = nil) - reflection.check_validity! - - if reflection.options[:polymorphic] - raise EagerLoadPolymorphicError.new(reflection) - end - - super(reflection.klass) - - @reflection = reflection - @join_dependency = join_dependency - @parent = parent - @join_type = Arel::InnerJoin - - # This must be done eagerly upon initialisation because the alias which is produced - # depends on the state of the join dependency, but we want it to work the same way - # every time. - allocate_aliases - end - - def ==(other) - other.class == self.class && - other.reflection == reflection && - other.parent == parent - end - - def find_parent_in(other_join_dependency) - other_join_dependency.join_parts.detect do |join_part| - self.parent == join_part - end - end - - def join_to(relation) - send("join_#{reflection.macro}_to", relation) - end - - def join_relation(joining_relation) - self.join_type = Arel::OuterJoin - joining_relation.joins(self) - end - - def table - @table ||= Arel::Table.new( - table_name, :as => aliased_table_name, - :engine => arel_engine, :columns => active_record.columns - ) - end - - # More semantic name given we are talking about associations - alias_method :target_table, :table - - protected - - def aliased_table_name_for(name, suffix = nil) - if @join_dependency.table_aliases[name].zero? - @join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name) - end - - if !@join_dependency.table_aliases[name].zero? # We need an alias - name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" - @join_dependency.table_aliases[name] += 1 - if @join_dependency.table_aliases[name] == 1 # First time we've seen this name - # Also need to count the aliases from the table_aliases to avoid incorrect count - @join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name) - end - table_index = @join_dependency.table_aliases[name] - name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 - else - @join_dependency.table_aliases[name] += 1 - end - - name - end - - def pluralize(table_name) - ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name - end - - def interpolate_sql(sql) - instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__) - end - - private - - def allocate_aliases - @aliased_prefix = "t#{ join_dependency.join_parts.size }" - @aliased_table_name = aliased_table_name_for(table_name) - - if reflection.macro == :has_and_belongs_to_many - @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join") - elsif [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through] - @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join") - end - end - - def process_conditions(conditions, table_name) - Arel.sql(interpolate_sql(sanitize_sql(conditions, table_name))) - end - - def join_target_table(relation, *conditions) - relation = relation.join(target_table, join_type) - - # If the target table is an STI model then we must be sure to only include records of - # its type and its sub-types. - unless active_record.descends_from_active_record? - sti_column = target_table[active_record.inheritance_column] - - sti_condition = sti_column.eq(active_record.sti_name) - active_record.descendants.each do |subclass| - sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) - end - - conditions << sti_condition - end - - # If the reflection has conditions, add them - if options[:conditions] - conditions << process_conditions(options[:conditions], aliased_table_name) - end - - relation = relation.on(*conditions) - end - - def join_has_and_belongs_to_many_to(relation) - join_table = Arel::Table.new( - options[:join_table], :engine => arel_engine, - :as => @aliased_join_table_name - ) - - fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key - klass_fk = options[:association_foreign_key] || reflection.klass.to_s.foreign_key - - relation = relation.join(join_table, join_type) - relation = relation.on( - join_table[fk]. - eq(parent_table[reflection.active_record.primary_key]) - ) - - join_target_table( - relation, - target_table[reflection.klass.primary_key]. - eq(join_table[klass_fk]) - ) - end - - def join_has_many_to(relation) - if reflection.options[:through] - join_has_many_through_to(relation) - elsif reflection.options[:as] - join_has_many_polymorphic_to(relation) - else - foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key - primary_key = options[:primary_key] || parent.primary_key - - join_target_table( - relation, - target_table[foreign_key]. - eq(parent_table[primary_key]) - ) - end - end - alias :join_has_one_to :join_has_many_to - - def join_has_many_through_to(relation) - join_table = Arel::Table.new( - through_reflection.klass.table_name, :engine => arel_engine, - :as => @aliased_join_table_name - ) - - jt_conditions = [] - jt_foreign_key = first_key = second_key = nil - - if through_reflection.options[:as] # has_many :through against a polymorphic join - as_key = through_reflection.options[:as].to_s - jt_foreign_key = as_key + '_id' - - jt_conditions << - join_table[as_key + '_type']. - eq(parent.active_record.base_class.name) - else - jt_foreign_key = through_reflection.primary_key_name - end - - case source_reflection.macro - when :has_many - second_key = options[:foreign_key] || primary_key - - if source_reflection.options[:as] - first_key = "#{source_reflection.options[:as]}_id" - else - first_key = through_reflection.klass.base_class.to_s.foreign_key - end - - unless through_reflection.klass.descends_from_active_record? - jt_conditions << - join_table[through_reflection.active_record.inheritance_column]. - eq(through_reflection.klass.sti_name) - end - when :belongs_to - first_key = primary_key - - if reflection.options[:source_type] - second_key = source_reflection.association_foreign_key - - jt_conditions << - join_table[reflection.source_reflection.options[:foreign_type]]. - eq(reflection.options[:source_type]) - else - second_key = source_reflection.primary_key_name - end - end - - jt_conditions << - parent_table[parent.primary_key]. - eq(join_table[jt_foreign_key]) - - if through_reflection.options[:conditions] - jt_conditions << process_conditions(through_reflection.options[:conditions], aliased_table_name) - end - - relation = relation.join(join_table, join_type).on(*jt_conditions) - - join_target_table( - relation, - target_table[first_key].eq(join_table[second_key]) - ) - end - - def join_has_many_polymorphic_to(relation) - join_target_table( - relation, - target_table["#{reflection.options[:as]}_id"]. - eq(parent_table[parent.primary_key]), - target_table["#{reflection.options[:as]}_type"]. - eq(parent.active_record.base_class.name) - ) - end - - def join_belongs_to_to(relation) - foreign_key = options[:foreign_key] || reflection.primary_key_name - primary_key = options[:primary_key] || reflection.klass.primary_key - - join_target_table( - relation, - target_table[primary_key].eq(parent_table[foreign_key]) - ) - end - end - end end end end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb new file mode 100644 index 0000000000..79fdb2d4cb --- /dev/null +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -0,0 +1,216 @@ +require 'active_record/associations/class_methods/join_dependency/join_part' +require 'active_record/associations/class_methods/join_dependency/join_base' +require 'active_record/associations/class_methods/join_dependency/join_association' + +module ActiveRecord + module Associations + module ClassMethods + class JoinDependency # :nodoc: + attr_reader :join_parts, :reflections, :table_aliases + + def initialize(base, associations, joins) + @join_parts = [JoinBase.new(base, joins)] + @associations = {} + @reflections = [] + @table_aliases = Hash.new(0) + @table_aliases[base.table_name] = 1 + build(associations) + end + + def graft(*associations) + associations.each do |association| + join_associations.detect {|a| association == a} || + build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type) + end + self + end + + def join_associations + join_parts.last(join_parts.length - 1) + end + + def join_base + join_parts.first + end + + def count_aliases_from_table_joins(name) + # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase + quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase + join_sql = join_base.table_joins.to_s.downcase + join_sql.blank? ? 0 : + # Table names + join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + + # Table aliases + join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size + end + + def instantiate(rows) + primary_key = join_base.aliased_primary_key + parents = {} + + records = rows.map { |model| + primary_id = model[primary_key] + parent = parents[primary_id] ||= join_base.instantiate(model) + construct(parent, @associations, join_associations.dup, model) + parent + }.uniq + + remove_duplicate_results!(join_base.active_record, records, @associations) + records + end + + def remove_duplicate_results!(base, records, associations) + case associations + when Symbol, String + reflection = base.reflections[associations] + remove_uniq_by_reflection(reflection, records) + when Array + associations.each do |association| + remove_duplicate_results!(base, records, association) + end + when Hash + associations.keys.each do |name| + reflection = base.reflections[name] + remove_uniq_by_reflection(reflection, records) + + parent_records = [] + records.each do |record| + if descendant = record.send(reflection.name) + if reflection.collection? + parent_records.concat descendant.target.uniq + else + parent_records << descendant + end + end + end + + remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty? + end + end + end + + protected + + def cache_joined_association(association) + associations = [] + parent = association.parent + while parent != join_base + associations.unshift(parent.reflection.name) + parent = parent.parent + end + ref = @associations + associations.each do |key| + ref = ref[key] + end + ref[association.reflection.name] ||= {} + end + + def build(associations, parent = nil, join_type = Arel::InnerJoin) + parent ||= join_parts.last + case associations + when Symbol, String + reflection = parent.reflections[associations.to_s.intern] or + raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?" + unless join_association = find_join_association(reflection, parent) + @reflections << reflection + join_association = build_join_association(reflection, parent) + join_association.join_type = join_type + @join_parts << join_association + cache_joined_association(join_association) + end + join_association + when Array + associations.each do |association| + build(association, parent, join_type) + end + when Hash + associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name| + join_association = build(name, parent, join_type) + build(associations[name], join_association, join_type) + end + else + raise ConfigurationError, associations.inspect + end + end + + def find_join_association(name_or_reflection, parent) + if String === name_or_reflection + name_or_reflection = name_or_reflection.to_sym + end + + join_associations.detect { |j| + j.reflection == name_or_reflection && j.parent == parent + } + end + + def remove_uniq_by_reflection(reflection, records) + if reflection && reflection.collection? + records.each { |record| record.send(reflection.name).target.uniq! } + end + end + + def build_join_association(reflection, parent) + JoinAssociation.new(reflection, self, parent) + end + + def construct(parent, associations, join_parts, row) + case associations + when Symbol, String + name = associations.to_s + + join_part = join_parts.detect { |j| + j.reflection.name.to_s == name && + j.parent_table_name == parent.class.table_name } + + raise(ConfigurationError, "No such association") unless join_part + + join_parts.delete(join_part) + construct_association(parent, join_part, row) + when Array + associations.each do |association| + construct(parent, association, join_parts, row) + end + when Hash + associations.sort_by { |k,_| k.to_s }.each do |name, assoc| + association = construct(parent, name, join_parts, row) + construct(association, assoc, join_parts, row) if association + end + else + raise ConfigurationError, associations.inspect + end + end + + def construct_association(record, join_part, row) + return if record.id.to_s != join_part.parent.record_id(row).to_s + + macro = join_part.reflection.macro + if macro == :has_one + return if record.instance_variable_defined?("@#{join_part.reflection.name}") + association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil? + set_target_and_inverse(join_part, association, record) + else + return if row[join_part.aliased_primary_key].nil? + association = join_part.instantiate(row) + case macro + when :has_many, :has_and_belongs_to_many + collection = record.send(join_part.reflection.name) + collection.loaded + collection.target.push(association) + collection.__send__(:set_inverse_instance, association, record) + when :belongs_to + set_target_and_inverse(join_part, association, record) + else + raise ConfigurationError, "unknown macro: #{join_part.reflection.macro}" + end + end + association + end + + def set_target_and_inverse(join_part, association, record) + association_proxy = record.send("set_#{join_part.reflection.name}_target", association) + association_proxy.__send__(:set_inverse_instance, association, record) + end + end + end + end +end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb new file mode 100644 index 0000000000..5e5c01c77a --- /dev/null +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -0,0 +1,278 @@ +module ActiveRecord + module Associations + module ClassMethods + class JoinDependency # :nodoc: + class JoinAssociation < JoinPart # :nodoc: + # The reflection of the association represented + attr_reader :reflection + + # The JoinDependency object which this JoinAssociation exists within. This is mainly + # relevant for generating aliases which do not conflict with other joins which are + # part of the query. + attr_reader :join_dependency + + # A JoinBase instance representing the active record we are joining onto. + # (So in Author.has_many :posts, the Author would be that base record.) + attr_reader :parent + + # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin + attr_accessor :join_type + + # These implement abstract methods from the superclass + attr_reader :aliased_prefix, :aliased_table_name + + delegate :options, :through_reflection, :source_reflection, :to => :reflection + delegate :table, :table_name, :to => :parent, :prefix => true + + def initialize(reflection, join_dependency, parent = nil) + reflection.check_validity! + + if reflection.options[:polymorphic] + raise EagerLoadPolymorphicError.new(reflection) + end + + super(reflection.klass) + + @reflection = reflection + @join_dependency = join_dependency + @parent = parent + @join_type = Arel::InnerJoin + + # This must be done eagerly upon initialisation because the alias which is produced + # depends on the state of the join dependency, but we want it to work the same way + # every time. + allocate_aliases + end + + def ==(other) + other.class == self.class && + other.reflection == reflection && + other.parent == parent + end + + def find_parent_in(other_join_dependency) + other_join_dependency.join_parts.detect do |join_part| + self.parent == join_part + end + end + + def join_to(relation) + send("join_#{reflection.macro}_to", relation) + end + + def join_relation(joining_relation) + self.join_type = Arel::OuterJoin + joining_relation.joins(self) + end + + def table + @table ||= Arel::Table.new( + table_name, :as => aliased_table_name, + :engine => arel_engine, :columns => active_record.columns + ) + end + + # More semantic name given we are talking about associations + alias_method :target_table, :table + + protected + + def aliased_table_name_for(name, suffix = nil) + if @join_dependency.table_aliases[name].zero? + @join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name) + end + + if !@join_dependency.table_aliases[name].zero? # We need an alias + name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" + @join_dependency.table_aliases[name] += 1 + if @join_dependency.table_aliases[name] == 1 # First time we've seen this name + # Also need to count the aliases from the table_aliases to avoid incorrect count + @join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name) + end + table_index = @join_dependency.table_aliases[name] + name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 + else + @join_dependency.table_aliases[name] += 1 + end + + name + end + + def pluralize(table_name) + ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name + end + + def interpolate_sql(sql) + instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__) + end + + private + + def allocate_aliases + @aliased_prefix = "t#{ join_dependency.join_parts.size }" + @aliased_table_name = aliased_table_name_for(table_name) + + if reflection.macro == :has_and_belongs_to_many + @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join") + elsif [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through] + @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join") + end + end + + def process_conditions(conditions, table_name) + Arel.sql(interpolate_sql(sanitize_sql(conditions, table_name))) + end + + def join_target_table(relation, *conditions) + relation = relation.join(target_table, join_type) + + # If the target table is an STI model then we must be sure to only include records of + # its type and its sub-types. + unless active_record.descends_from_active_record? + sti_column = target_table[active_record.inheritance_column] + + sti_condition = sti_column.eq(active_record.sti_name) + active_record.descendants.each do |subclass| + sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) + end + + conditions << sti_condition + end + + # If the reflection has conditions, add them + if options[:conditions] + conditions << process_conditions(options[:conditions], aliased_table_name) + end + + relation = relation.on(*conditions) + end + + def join_has_and_belongs_to_many_to(relation) + join_table = Arel::Table.new( + options[:join_table], :engine => arel_engine, + :as => @aliased_join_table_name + ) + + fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key + klass_fk = options[:association_foreign_key] || reflection.klass.to_s.foreign_key + + relation = relation.join(join_table, join_type) + relation = relation.on( + join_table[fk]. + eq(parent_table[reflection.active_record.primary_key]) + ) + + join_target_table( + relation, + target_table[reflection.klass.primary_key]. + eq(join_table[klass_fk]) + ) + end + + def join_has_many_to(relation) + if reflection.options[:through] + join_has_many_through_to(relation) + elsif reflection.options[:as] + join_has_many_polymorphic_to(relation) + else + foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key + primary_key = options[:primary_key] || parent.primary_key + + join_target_table( + relation, + target_table[foreign_key]. + eq(parent_table[primary_key]) + ) + end + end + alias :join_has_one_to :join_has_many_to + + def join_has_many_through_to(relation) + join_table = Arel::Table.new( + through_reflection.klass.table_name, :engine => arel_engine, + :as => @aliased_join_table_name + ) + + jt_conditions = [] + jt_foreign_key = first_key = second_key = nil + + if through_reflection.options[:as] # has_many :through against a polymorphic join + as_key = through_reflection.options[:as].to_s + jt_foreign_key = as_key + '_id' + + jt_conditions << + join_table[as_key + '_type']. + eq(parent.active_record.base_class.name) + else + jt_foreign_key = through_reflection.primary_key_name + end + + case source_reflection.macro + when :has_many + second_key = options[:foreign_key] || primary_key + + if source_reflection.options[:as] + first_key = "#{source_reflection.options[:as]}_id" + else + first_key = through_reflection.klass.base_class.to_s.foreign_key + end + + unless through_reflection.klass.descends_from_active_record? + jt_conditions << + join_table[through_reflection.active_record.inheritance_column]. + eq(through_reflection.klass.sti_name) + end + when :belongs_to + first_key = primary_key + + if reflection.options[:source_type] + second_key = source_reflection.association_foreign_key + + jt_conditions << + join_table[reflection.source_reflection.options[:foreign_type]]. + eq(reflection.options[:source_type]) + else + second_key = source_reflection.primary_key_name + end + end + + jt_conditions << + parent_table[parent.primary_key]. + eq(join_table[jt_foreign_key]) + + if through_reflection.options[:conditions] + jt_conditions << process_conditions(through_reflection.options[:conditions], aliased_table_name) + end + + relation = relation.join(join_table, join_type).on(*jt_conditions) + + join_target_table( + relation, + target_table[first_key].eq(join_table[second_key]) + ) + end + + def join_has_many_polymorphic_to(relation) + join_target_table( + relation, + target_table["#{reflection.options[:as]}_id"]. + eq(parent_table[parent.primary_key]), + target_table["#{reflection.options[:as]}_type"]. + eq(parent.active_record.base_class.name) + ) + end + + def join_belongs_to_to(relation) + foreign_key = options[:foreign_key] || reflection.primary_key_name + primary_key = options[:primary_key] || reflection.klass.primary_key + + join_target_table( + relation, + target_table[primary_key].eq(parent_table[foreign_key]) + ) + end + end + end + end + end +end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb new file mode 100644 index 0000000000..ed05003f66 --- /dev/null +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb @@ -0,0 +1,34 @@ +module ActiveRecord + module Associations + module ClassMethods + class JoinDependency # :nodoc: + class JoinBase < JoinPart # :nodoc: + # Extra joins provided when the JoinDependency was created + attr_reader :table_joins + + def initialize(active_record, joins = nil) + super(active_record) + @table_joins = joins + end + + def ==(other) + other.class == self.class && + other.active_record == active_record + end + + def aliased_prefix + "t0" + end + + def table + Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns) + end + + def aliased_table_name + active_record.table_name + end + end + end + end + end +end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb new file mode 100644 index 0000000000..64d751344d --- /dev/null +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb @@ -0,0 +1,76 @@ +module ActiveRecord + module Associations + module ClassMethods + class JoinDependency # :nodoc: + # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited + # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which + # everything else is being joined onto. A JoinAssociation represents an association which + # is joining to the base. A JoinAssociation may result in more than one actual join + # operations (for example a has_and_belongs_to_many JoinAssociation would result in + # two; one for the join table and one for the target table). + class JoinPart # :nodoc: + # The Active Record class which this join part is associated 'about'; for a JoinBase + # this is the actual base model, for a JoinAssociation this is the target model of the + # association. + attr_reader :active_record + + delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record + + def initialize(active_record) + @active_record = active_record + @cached_record = {} + end + + def ==(other) + raise NotImplementedError + end + + # An Arel::Table for the active_record + def table + raise NotImplementedError + end + + # The prefix to be used when aliasing columns in the active_record's table + def aliased_prefix + raise NotImplementedError + end + + # The alias for the active_record's table + def aliased_table_name + raise NotImplementedError + end + + # The alias for the primary key of the active_record's table + def aliased_primary_key + "#{aliased_prefix}_r0" + end + + # An array of [column_name, alias] pairs for the table + def column_names_with_alias + unless defined?(@column_names_with_alias) + @column_names_with_alias = [] + + ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i| + @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] + end + end + + @column_names_with_alias + end + + def extract_record(row) + Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}] + end + + def record_id(row) + row[aliased_primary_key] + end + + def instantiate(row) + @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row)) + end + end + end + end + end +end -- cgit v1.2.3 From 0687b21de879d53157e52a2b688e34a1bd1e31f0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 17:52:51 -0800 Subject: removing ternary --- activerecord/lib/active_record/associations/has_many_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 23831e0b08..156279a67a 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -42,7 +42,7 @@ module ActiveRecord # documented side-effect of the method that may avoid an extra SELECT. @target ||= [] and loaded if count == 0 - @reflection.options[:limit] ? [@reflection.options[:limit], count].min : count + [@reflection.options[:limit], count].compact.min end def has_cached_counter? -- cgit v1.2.3 From e39138478b38d7a5e0a13756b1bdfa8b43226846 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Wed, 24 Nov 2010 10:10:38 +0100 Subject: port_string bought back to life as it is part of the public api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/url.rb | 11 ++++++++--- actionpack/test/dispatch/request_test.rb | 10 +++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 1f7633cbea..1e7054f381 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -52,8 +52,7 @@ module ActionDispatch # Returns a \host:\port string for this request, such as "example.com" or # "example.com:8080". def host_with_port - opt_port = optional_port ? ":#{optional_port}" : nil - "#{host}#{opt_port}" + "#{host}#{port_string}" end # Returns the port number of this request as an integer. @@ -80,12 +79,18 @@ module ActionDispatch port == standard_port end - # Returns a \port suffix like "8080" if the \port number of this request + # Returns a number \port suffix like 8080 if the \port number of this request # is not the default HTTP \port 80 or HTTPS \port 443. def optional_port standard_port? ? nil : port end + # Returns a string \port suffix, including colon, like ":8080" if the \port + # number of this request is not the default HTTP \port 80 or HTTPS \port 443. + def port_string + standard_port? ? '' : ":#{port}" + end + def server_port @env['SERVER_PORT'].to_i end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index d140ea8358..8f672c1149 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -172,6 +172,14 @@ class RequestTest < ActiveSupport::TestCase assert_equal 8080, request.optional_port end + test "port string" do + request = stub_request 'HTTP_HOST' => 'www.example.org:80' + assert_equal '', request.port_string + + request = stub_request 'HTTP_HOST' => 'www.example.org:8080' + assert_equal ':8080', request.port_string + end + test "full path" do request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri', 'QUERY_STRING' => 'mapped=1' assert_equal "/path/of/some/uri?mapped=1", request.fullpath @@ -392,7 +400,7 @@ class RequestTest < ActiveSupport::TestCase mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" } request = nil begin - request = stub_request(mock_rack_env) + request = stub_request(mock_rack_env) request.parameters rescue TypeError => e # rack will raise a TypeError when parsing this query string -- cgit v1.2.3 From 8624065bffd203e1f590ca431d468f9b38e9fb7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 24 Nov 2010 12:38:52 +0100 Subject: Allow template handlers to store temp data. --- actionpack/lib/action_view/template.rb | 5 +++++ actionpack/test/template/lookup_context_test.rb | 29 +++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 6c6d659246..831a19654e 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -184,6 +184,11 @@ module ActionView end end + # Used to store template data by template handlers. + def data + @data ||= {} + end + def inspect @inspect ||= if defined?(Rails.root) diff --git a/actionpack/test/template/lookup_context_test.rb b/actionpack/test/template/lookup_context_test.rb index 6d3b26e131..c9dd27cf2a 100644 --- a/actionpack/test/template/lookup_context_test.rb +++ b/actionpack/test/template/lookup_context_test.rb @@ -180,6 +180,16 @@ class LookupContextTest < ActiveSupport::TestCase assert_not_equal template, old_template end + + test "data can be stored in cached templates" do + template = @lookup_context.find("hello_world", "test") + template.data["cached"] = "data" + assert_equal "Hello world!", template.source + + template = @lookup_context.find("hello_world", "test") + assert_equal "data", template.data["cached"] + assert_equal "Hello world!", template.source + end end class LookupContextWithFalseCaching < ActiveSupport::TestCase @@ -205,7 +215,7 @@ class LookupContextWithFalseCaching < ActiveSupport::TestCase assert_equal "Bar", template.source end - test "if no template was found in the second lookup, give it higher preference" do + test "if no template was found in the second lookup, with no cache, raise error" do template = @lookup_context.find("foo", "test", true) assert_equal "Foo", template.source @@ -215,7 +225,7 @@ class LookupContextWithFalseCaching < ActiveSupport::TestCase end end - test "if no template was cached in the first lookup, do not use the cache in the second" do + test "if no template was cached in the first lookup, retrieval should work in the second call" do @resolver.hash.clear assert_raise ActionView::MissingTemplate do @lookup_context.find("foo", "test", true) @@ -225,4 +235,19 @@ class LookupContextWithFalseCaching < ActiveSupport::TestCase template = @lookup_context.find("foo", "test", true) assert_equal "Foo", template.source end + + test "data can be stored as long as template was not updated" do + template = @lookup_context.find("foo", "test", true) + template.data["cached"] = "data" + assert_equal "Foo", template.source + + template = @lookup_context.find("foo", "test", true) + assert_equal "data", template.data["cached"] + assert_equal "Foo", template.source + + @resolver.hash["test/_foo.erb"][1] = Time.now.utc + template = @lookup_context.find("foo", "test", true) + assert_nil template.data["cached"] + assert_equal "Foo", template.source + end end \ No newline at end of file -- cgit v1.2.3 From 47f39d26a76430ad50dae79f212d118849b2af40 Mon Sep 17 00:00:00 2001 From: Franck Verrot Date: Wed, 24 Nov 2010 16:32:02 +0800 Subject: Testing that dup is resetting the timestamps --- activerecord/test/cases/dup_test.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index 46abe7792c..0236f9b0a1 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -51,7 +51,12 @@ module ActiveRecord topic.attributes = dbtopic.attributes + #duped has no timestamp values duped = dbtopic.dup + + #clear topic timestamp values + topic.send(:clear_timestamp_attributes) + assert_equal topic.changes, duped.changes end @@ -75,5 +80,24 @@ module ActiveRecord assert_equal 'Aaron', topic.author_name assert_equal 'meow', duped.author_name end + + def test_dup_timestamps_are_cleared + topic = Topic.first + assert_not_nil topic.updated_at + assert_not_nil topic.created_at + + # temporary change to the topic object + topic.updated_at -= 3.days + + #dup should not preserve the timestamps if present + new_topic = topic.dup + assert_nil new_topic.updated_at + assert_nil new_topic.created_at + + new_topic.save + assert_not_nil new_topic.updated_at + assert_not_nil new_topic.created_at + end + end end -- cgit v1.2.3 From c17cb7326d6af7c4226e955abd3f89db95fabb33 Mon Sep 17 00:00:00 2001 From: Franck Verrot Date: Wed, 24 Nov 2010 16:32:41 +0800 Subject: Dup should reset the timestamps as it is considered a new record --- activerecord/lib/active_record/base.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d2fa3bed35..49b30f4779 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1625,6 +1625,7 @@ MSG ensure_proper_type populate_with_current_scope_attributes + clear_timestamp_attributes end # Returns +true+ if the record is read only. Records loaded through joins with piggy-back @@ -1831,6 +1832,16 @@ MSG create_with.each { |att,value| self.respond_to?(:"#{att}=") && self.send("#{att}=", value) } if create_with end end + + # Clear attributes and changged_attributes + def clear_timestamp_attributes + %w(created_at created_on updated_at updated_on).each do |attribute_name| + if self.has_attribute?(attribute_name) + self[attribute_name] = nil + self.changed_attributes.delete(attribute_name) + end + end + end end Base.class_eval do -- cgit v1.2.3 From 7b77a1fc71e54f6e42e97b426380303d696051c0 Mon Sep 17 00:00:00 2001 From: Franck Verrot Date: Thu, 25 Nov 2010 02:53:28 +0800 Subject: Typo in the comments of the clear_timestamp_attributes method --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 49b30f4779..1e0fd7c6fd 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1833,7 +1833,7 @@ MSG end end - # Clear attributes and changged_attributes + # Clear attributes and changed_attributes def clear_timestamp_attributes %w(created_at created_on updated_at updated_on).each do |attribute_name| if self.has_attribute?(attribute_name) -- cgit v1.2.3 From 7f8ce38b0d294b5c5d38116f8de63f175f446bd4 Mon Sep 17 00:00:00 2001 From: Franck Verrot Date: Thu, 25 Nov 2010 02:54:59 +0800 Subject: Document the behavior of the dup method: does not preserve timestamps --- activerecord/lib/active_record/base.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 1e0fd7c6fd..b7aac7a7c9 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1605,6 +1605,7 @@ MSG # only, not its associations. The extent of a "deep" copy is application # specific and is therefore left to the application to implement according # to its need. + # The dup method does not preserve the timestamps (created|updated)_(at|on). def initialize_dup(other) cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) -- cgit v1.2.3 From c6030e8562cddcb71898a1dea242affe5f0464f0 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 23 Nov 2010 22:40:41 +0800 Subject: Remove the not needed setup and teardown --- actionpack/test/controller/mime_responds_test.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 0da051822a..8e7d8056a0 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -169,9 +169,6 @@ end class StarStarMimeControllerTest < ActionController::TestCase tests StarStarMimeController - def setup; super; end - def teardown; super; end - def test_javascript_with_format @request.accept = "text/javascript" get :index, :format => 'js' -- cgit v1.2.3 From e8708836252852d8bae87c80472b865812fa9776 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 23 Nov 2010 23:28:57 +0800 Subject: unregister method implementation and test --- actionpack/lib/action_dispatch/http/mime_type.rb | 12 ++++++++++++ actionpack/test/dispatch/mime_type_test.rb | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 2250cfa88a..2b8395fc09 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -177,6 +177,18 @@ module Mime keys = Mime::LOOKUP.keys.select{|k| k.include?(input)} Mime::LOOKUP.values_at(*keys).uniq end + + # This method is opposite of register method. + # + # Usage: + # + # Mime::Type.unregister("text/x-mobile", :mobile) + def unregister(string, symbol) + EXTENSION_LOOKUP.delete(symbol.to_s) + LOOKUP.delete(string) + symbol = symbol.to_s.upcase.intern + Mime.module_eval { remove_const(symbol) if const_defined?(symbol) } + end end def initialize(string, symbol = nil, synonyms = []) diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 43123e68b2..f2d2799a67 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -12,6 +12,23 @@ class MimeTypeTest < ActiveSupport::TestCase end end + test "unregister" do + begin + Mime::Type.register("text/x-mobile", :mobile) + assert defined?(Mime::MOBILE) + assert_equal Mime::MOBILE, Mime::LOOKUP['text/x-mobile'] + assert_equal Mime::MOBILE, Mime::EXTENSION_LOOKUP['mobile'] + + Mime::Type.unregister("text/x-mobile", :mobile) + assert !defined?(Mime::MOBILE), "Mime::MOBILE should not be defined" + assert !Mime::LOOKUP.has_key?('text/x-mobile'), "Mime::LOOKUP should not have key ['text/x-mobile]" + assert !Mime::EXTENSION_LOOKUP.has_key?('mobile'), "Mime::EXTENSION_LOOKUP should not have key ['mobile]" + ensure + Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) } + Mime::LOOKUP.reject!{|key,_| key == 'text/x-mobile'} + end + end + test "parse text with trailing star" do accept = "text/*" expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT].sort_by(&:to_s) -- cgit v1.2.3 From b52a6ba16914d29785e751011e91d7bb91a2356d Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 23 Nov 2010 23:49:15 +0800 Subject: clean up test by using unregister method --- actionpack/test/controller/mime_responds_test.rb | 18 ++++++------------ actionpack/test/dispatch/mime_type_test.rb | 3 +-- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 8e7d8056a0..8315f90fee 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -201,10 +201,8 @@ class RespondToControllerTest < ActionController::TestCase def teardown super - Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } - Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) } - Mime::LOOKUP.reject!{|key,_| key == 'text/x-mobile'} - Mime::LOOKUP.reject!{|key,_| key == 'text/iphone'} + Mime::Type.unregister('text/x-mobile', :iphone) + Mime::Type.unregister('text/iphone', :mobile) end def test_html @@ -617,10 +615,8 @@ class RespondWithControllerTest < ActionController::TestCase def teardown super - Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } - Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) } - Mime::LOOKUP.reject!{|key,_| key == 'text/x-mobile'} - Mime::LOOKUP.reject!{|key,_| key == 'text/iphone'} + Mime::Type.unregister('text/x-mobile', :iphone) + Mime::Type.unregister('text/iphone', :mobile) end def test_using_resource @@ -1006,10 +1002,8 @@ class MimeControllerLayoutsTest < ActionController::TestCase def teardown super - Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } - Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) } - Mime::LOOKUP.reject!{|key,_| key == 'text/x-mobile'} - Mime::LOOKUP.reject!{|key,_| key == 'text/iphone'} + Mime::Type.unregister('text/x-mobile', :iphone) + Mime::Type.unregister('text/iphone', :mobile) end def test_missing_layout_renders_properly diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index f2d2799a67..bf44cb95fa 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -85,8 +85,7 @@ class MimeTypeTest < ActiveSupport::TestCase assert_equal Mime::GIF, Mime::SET.last end ensure - Mime.module_eval { remove_const :GIF if const_defined?(:GIF) } - Mime::LOOKUP.reject!{|key,_| key == 'image/gif'} + Mime::Type.unregister('image/gif', :gif) end end -- cgit v1.2.3 From 5c9abb6cca4b6de7146c94779a34ae943e4ea4d8 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 25 Nov 2010 01:19:43 +0800 Subject: processing image/* is an odditity because there is a test case which expects image/* to not to be expanded. So I am leaving image/* as it is and process only text/* and application/* --- actionpack/lib/action_dispatch/http/mime_type.rb | 2 +- actionpack/test/dispatch/mime_type_test.rb | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 2b8395fc09..5ea49a23c7 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -81,7 +81,7 @@ module Mime class << self - TRAILING_STAR_REGEXP = /(\w+)\/\*/ + TRAILING_STAR_REGEXP = /(text|application)\/\*/ def lookup(string) LOOKUP[string] diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index bf44cb95fa..25e106f519 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -45,12 +45,6 @@ class MimeTypeTest < ActiveSupport::TestCase assert_equal expect, parsed.sort_by(&:to_s) end - test "parse image with trailing star" do - accept = "image/*" - parsed = Mime::Type.parse(accept) - assert parsed.include?(Mime::PNG) - end - test "parse without q" do accept = "text/xml,application/xhtml+xml,text/yaml,application/xml,text/html,image/png,text/plain,application/pdf,*/*" expect = [Mime::HTML, Mime::XML, Mime::YAML, Mime::PNG, Mime::TEXT, Mime::PDF, Mime::ALL] -- cgit v1.2.3 From 18adbe9347727dc3eefe46395d52aafa347a0c73 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 25 Nov 2010 01:24:45 +0800 Subject: process text/* if it appears in the middle of HTTP_ACCEPT parameter --- actionpack/lib/action_dispatch/http/mime_type.rb | 6 +++++- actionpack/test/dispatch/mime_type_test.rb | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 5ea49a23c7..08eab5634a 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -120,7 +120,11 @@ module Mime params, q = header.split(/;\s*q=/) if params params.strip! - list << AcceptItem.new(index, params, q) unless params.empty? + if params =~ TRAILING_STAR_REGEXP + parse_data_with_trailing_star($1).each { |m| list << AcceptItem.new(index, m.to_s, q) } + else + list << AcceptItem.new(index, params, q) unless params.empty? + end end end list.sort! diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 25e106f519..9424d88498 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -29,6 +29,26 @@ class MimeTypeTest < ActiveSupport::TestCase end end + test "parse text with trailing star at the beginning" do + accept = "text/*, text/html, application/json, multipart/form-data" + expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::TEXT, Mime::YAML, Mime::JS, Mime::MULTIPART_FORM] + parsed = Mime::Type.parse(accept) + assert_equal expect.size, parsed.size + Range.new(0,expect.size-1).to_a.each do |index| + assert_equal expect[index], parsed[index], "Failed for index number #{index}" + end + end + + test "parse text with trailing star in the end" do + accept = "text/html, application/json, multipart/form-data, text/*" + expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::XML, Mime::ICS, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT] + parsed = Mime::Type.parse(accept) + assert_equal 10, parsed.size + Range.new(0,expect.size-1).to_a.each do |index| + assert_equal expect[index], parsed[index], "Failed for index number #{index}" + end + end + test "parse text with trailing star" do accept = "text/*" expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT].sort_by(&:to_s) -- cgit v1.2.3 From 66212f69acc3d51af10ff76a18ff4c0bfa305ea5 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 11 Nov 2010 11:41:15 -0500 Subject: If a nested_attribute is being marked for destruction and at the same time an attr_accessor value is being assigned then the value being assigned is being ignored. This patch is a fix for that issue. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5939 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/nested_attributes.rb | 7 ++----- activerecord/test/cases/nested_attributes_test.rb | 15 ++++++++++++++- activerecord/test/models/pet.rb | 8 ++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 15f83a8579..050b521b6a 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -417,11 +417,8 @@ module ActiveRecord # 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) - if has_destroy_flag?(attributes) && allow_destroy - record.mark_for_destruction - else - record.attributes = attributes.except(*UNASSIGNABLE_KEYS) - end + record.attributes = attributes.except(*UNASSIGNABLE_KEYS) + record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy end # Determines if a hash contains a truthy _destroy key. diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 92af53d56f..fb6a239545 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -827,7 +827,7 @@ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase fixtures :owners, :pets def setup - Owner.accepts_nested_attributes_for :pets + Owner.accepts_nested_attributes_for :pets, :allow_destroy => true @owner = owners(:ashley) @pet1, @pet2 = pets(:chew), pets(:mochi) @@ -844,6 +844,19 @@ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase @owner.update_attributes(@params) assert_equal ['Foo', 'Bar'], @owner.pets.map(&:name) end + + def test_attr_accessor_of_child_should_be_value_provided_during_update_attributes + @owner = owners(:ashley) + @pet1 = pets(:chew) + assert_equal nil, $current_user + attributes = {:pets_attributes => { "1"=> { :id => @pet1.id, + :name => "Foo2", + :current_user => "John", + :_destroy=>true }}} + @owner.update_attributes(attributes) + assert_equal 'John', $after_destroy_callback_output + end + end class TestHasOneAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb index a8bf94dd86..570db4c8d5 100644 --- a/activerecord/test/models/pet.rb +++ b/activerecord/test/models/pet.rb @@ -1,5 +1,13 @@ class Pet < ActiveRecord::Base + + attr_accessor :current_user + set_primary_key :pet_id belongs_to :owner, :touch => true has_many :toys + + after_destroy do |record| + $after_destroy_callback_output = record.current_user + end + end -- cgit v1.2.3 From fa2a5ae0339c90d023a7559e681a588219dc3903 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 15 Nov 2010 17:52:39 -0500 Subject: If a user wants json output then try best to render json output. In such cases prefer kind_of(String) over respond_to?(to_str) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5841 state:resolved] Signed-off-by: José Valim --- actionpack/lib/action_controller/metal/renderers.rb | 2 +- actionpack/test/controller/mime_responds_test.rb | 18 ++++++++++++++++++ actionpack/test/lib/controller/fake_models.rb | 6 ++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 67cf08445d..d6f6ab1855 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -55,7 +55,7 @@ module ActionController end add :json do |json, options| - json = json.to_json(options) unless json.respond_to?(:to_str) + json = json.to_json(options) unless json.kind_of?(String) json = "#{options[:callback]}(#{json})" unless options[:callback].blank? self.content_type ||= Mime::JSON self.response_body = json diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 8315f90fee..82969b2979 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -599,6 +599,17 @@ class InheritedRespondWithController < RespondWithController end end +class RenderJsonRespondWithController < RespondWithController + clear_respond_to + respond_to :json + + def index + respond_with(resource) do |format| + format.json { render :json => RenderJsonTestException.new('boom') } + end + end +end + class EmptyRespondWithController < ActionController::Base def index respond_with(Customer.new("david", 13)) @@ -914,6 +925,13 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal "JSON", @response.body end + def test_render_json_object_responds_to_str_still_produce_json + @controller = RenderJsonRespondWithController.new + @request.accept = "application/json" + get :index, :format => :json + assert_equal %Q{{"message":"boom","error":"RenderJsonTestException"}}, @response.body + end + def test_no_double_render_is_raised @request.accept = "text/html" assert_raise ActionView::MissingTemplate do diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index ae0c38184d..bd18cdc1b8 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -194,3 +194,9 @@ class ArelLike a.each { |i| yield i } end end + +class RenderJsonTestException < Exception + def to_json(options = nil) + return { :error => self.class.name, :message => self.to_str }.to_json + end +end -- cgit v1.2.3 From a3ba60fd814a980cc929fcbe0f9d48fa4d0292e5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Nov 2010 18:08:23 -0800 Subject: reduce method calls --- activerecord/lib/active_record/associations/has_many_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 156279a67a..685d818ab3 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -108,7 +108,7 @@ module ActiveRecord end def we_can_set_the_inverse_on_this?(record) - !@reflection.inverse_of.nil? + @reflection.inverse_of end end end -- cgit v1.2.3 From d4b8d3bafa54e233d3f1ef43c43bfb460d9f2435 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 13:29:18 -0800 Subject: moving column_aliases to JoinDependency --- .../associations/class_methods/join_dependency.rb | 8 ++++++++ activerecord/lib/active_record/relation/finder_methods.rb | 11 +---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 79fdb2d4cb..87541e0529 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -33,6 +33,14 @@ module ActiveRecord join_parts.first end + def columns(connection) + join_parts.collect { |join_part| + join_part.column_names_with_alias.collect{ |column_name, aliased_name| + "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}" + } + }.flatten.join(", ") + end + def count_aliases_from_table_joins(name) # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 4192456447..74ec83091c 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -202,7 +202,7 @@ module ActiveRecord end def construct_relation_for_association_find(join_dependency) - relation = except(:includes, :eager_load, :preload, :select).select(column_aliases(join_dependency)) + relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns(connection)) apply_join_dependency(relation, join_dependency) end @@ -349,17 +349,8 @@ module ActiveRecord end end - def column_aliases(join_dependency) - join_dependency.join_parts.collect { |join_part| - join_part.column_names_with_alias.collect{ |column_name, aliased_name| - "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}" - } - }.flatten.join(", ") - end - def using_limitable_reflections?(reflections) reflections.none? { |r| r.collection? } end - end end -- cgit v1.2.3 From afe51afcc6b7c0245fddbd879d369818b54e460a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 13:32:01 -0800 Subject: remove useless join --- .../lib/active_record/associations/class_methods/join_dependency.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 87541e0529..7c7fa964f8 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -38,7 +38,7 @@ module ActiveRecord join_part.column_names_with_alias.collect{ |column_name, aliased_name| "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}" } - }.flatten.join(", ") + }.flatten end def count_aliases_from_table_joins(name) -- cgit v1.2.3 From cdf6cf01cb47bdcbc1e011c8f5f72b161f12bf9c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 13:40:23 -0800 Subject: use ARel rather than generate SQL strings --- .../lib/active_record/associations/class_methods/join_dependency.rb | 5 +++-- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 7c7fa964f8..b1521b3706 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -33,10 +33,11 @@ module ActiveRecord join_parts.first end - def columns(connection) + def columns join_parts.collect { |join_part| + table = Arel::Nodes::TableAlias.new join_part.aliased_table_name, nil join_part.column_names_with_alias.collect{ |column_name, aliased_name| - "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}" + table[column_name].as aliased_name } }.flatten end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 74ec83091c..23ae0b4325 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -202,7 +202,7 @@ module ActiveRecord end def construct_relation_for_association_find(join_dependency) - relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns(connection)) + relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns) apply_join_dependency(relation, join_dependency) end -- cgit v1.2.3 From 34d21b87bb237bf55214bd476be71116a2a5258f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 13:53:55 -0800 Subject: adding a factory method to the join part for generating a table alias --- .../lib/active_record/associations/class_methods/join_dependency.rb | 2 +- .../associations/class_methods/join_dependency/join_part.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index b1521b3706..19d2d55ca0 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -35,7 +35,7 @@ module ActiveRecord def columns join_parts.collect { |join_part| - table = Arel::Nodes::TableAlias.new join_part.aliased_table_name, nil + table = join_part.aliased_table join_part.column_names_with_alias.collect{ |column_name, aliased_name| table[column_name].as aliased_name } diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb index 64d751344d..5d55ca202e 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb @@ -21,6 +21,10 @@ module ActiveRecord @cached_record = {} end + def aliased_table + Arel::Nodes::TableAlias.new aliased_table_name, table + end + def ==(other) raise NotImplementedError end -- cgit v1.2.3 From 38eb01863c9281d142c6494bae485b9c215ec9b7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 14:11:12 -0800 Subject: initialize instance variables in initialize... o_O --- .../associations/class_methods/join_dependency/join_part.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb index 5d55ca202e..0b093b65e9 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb @@ -19,6 +19,7 @@ module ActiveRecord def initialize(active_record) @active_record = active_record @cached_record = {} + @column_names_with_alias = nil end def aliased_table @@ -51,14 +52,13 @@ module ActiveRecord # An array of [column_name, alias] pairs for the table def column_names_with_alias - unless defined?(@column_names_with_alias) + unless @column_names_with_alias @column_names_with_alias = [] ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i| @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] end end - @column_names_with_alias end -- cgit v1.2.3 From ee74f2c6238dbaca00e9b667c3c239f3cae8aca5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 24 Nov 2010 14:16:38 -0800 Subject: alias should be a SQL literal --- .../lib/active_record/associations/class_methods/join_dependency.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 19d2d55ca0..6ab7bd0b06 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -37,7 +37,7 @@ module ActiveRecord join_parts.collect { |join_part| table = join_part.aliased_table join_part.column_names_with_alias.collect{ |column_name, aliased_name| - table[column_name].as aliased_name + table[column_name].as Arel.sql(aliased_name) } }.flatten end -- cgit v1.2.3 From d8692985feb4db9fe8d113549535b658fe6058e5 Mon Sep 17 00:00:00 2001 From: raggi Date: Thu, 25 Nov 2010 06:21:55 +0800 Subject: Don't depend on rubygems loading thread (for Mutex) --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 1 + activerecord/test/cases/locking_test.rb | 1 + activesupport/lib/active_support/core_ext/module/synchronization.rb | 1 + activesupport/test/core_ext/module/synchronization_test.rb | 1 + 4 files changed, 4 insertions(+) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index ca9314ec99..cffa2387de 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -1,3 +1,4 @@ +require 'thread' require 'monitor' require 'set' require 'active_support/core_ext/module/synchronization' diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 4ddcdc010b..f9678cb0c5 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -1,3 +1,4 @@ +require 'thread' require "cases/helper" require 'models/person' require 'models/reader' diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb index 38ce55f26e..ed16c2f71b 100644 --- a/activesupport/lib/active_support/core_ext/module/synchronization.rb +++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb @@ -1,3 +1,4 @@ +require 'thread' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/array/extract_options' diff --git a/activesupport/test/core_ext/module/synchronization_test.rb b/activesupport/test/core_ext/module/synchronization_test.rb index eb850893f0..6c407e2260 100644 --- a/activesupport/test/core_ext/module/synchronization_test.rb +++ b/activesupport/test/core_ext/module/synchronization_test.rb @@ -1,3 +1,4 @@ +require 'thread' require 'abstract_unit' require 'active_support/core_ext/class/attribute_accessors' -- cgit v1.2.3 From cb1570936d70d624f72229c8a19ffbe3a10cc5ca Mon Sep 17 00:00:00 2001 From: raggi Date: Thu, 25 Nov 2010 04:03:04 +0800 Subject: Rakefiles are executables, and rake loads rake, not rakefile code --- Rakefile | 2 +- actionmailer/Rakefile | 2 +- actionpack/Rakefile | 2 +- activemodel/Rakefile | 0 activerecord/Rakefile | 2 +- activeresource/Rakefile | 2 +- activesupport/Rakefile | 0 railties/Rakefile | 2 +- railties/lib/rails/generators/rails/app/templates/Rakefile | 2 +- railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt | 2 +- railties/lib/rails/generators/rails/plugin_new/templates/Rakefile | 4 +--- 11 files changed, 9 insertions(+), 11 deletions(-) mode change 100644 => 100755 Rakefile mode change 100644 => 100755 actionmailer/Rakefile mode change 100644 => 100755 actionpack/Rakefile mode change 100644 => 100755 activemodel/Rakefile mode change 100644 => 100755 activerecord/Rakefile mode change 100644 => 100755 activeresource/Rakefile mode change 100644 => 100755 activesupport/Rakefile mode change 100644 => 100755 railties/Rakefile mode change 100644 => 100755 railties/lib/rails/generators/rails/app/templates/Rakefile mode change 100644 => 100755 railties/lib/rails/generators/rails/plugin_new/templates/Rakefile diff --git a/Rakefile b/Rakefile old mode 100644 new mode 100755 index 8c72312f28..1f3c770c77 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ +#!/usr/bin/env rake gem 'rdoc', '>= 2.5.10' require 'rdoc' -require 'rake' require 'rdoc/task' require 'net/http' diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile old mode 100644 new mode 100755 index cba6e93c8a..123ef9bbbf --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -1,4 +1,4 @@ -require 'rake' +#!/usr/bin/env rake require 'rake/testtask' require 'rake/packagetask' require 'rake/gempackagetask' diff --git a/actionpack/Rakefile b/actionpack/Rakefile old mode 100644 new mode 100755 index a6ce08113f..9030db9f7a --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -1,4 +1,4 @@ -require 'rake' +#!/usr/bin/env rake require 'rake/testtask' require 'rake/packagetask' require 'rake/gempackagetask' diff --git a/activemodel/Rakefile b/activemodel/Rakefile old mode 100644 new mode 100755 diff --git a/activerecord/Rakefile b/activerecord/Rakefile old mode 100644 new mode 100755 index 395c72dfbc..f9b77c1799 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -1,4 +1,4 @@ -require 'rake' +#!/usr/bin/env rake require 'rake/testtask' require 'rake/packagetask' require 'rake/gempackagetask' diff --git a/activeresource/Rakefile b/activeresource/Rakefile old mode 100644 new mode 100755 index 905241acc0..cf01bc1389 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -1,4 +1,4 @@ -require 'rake' +#!/usr/bin/env rake require 'rake/testtask' require 'rake/packagetask' require 'rake/gempackagetask' diff --git a/activesupport/Rakefile b/activesupport/Rakefile old mode 100644 new mode 100755 diff --git a/railties/Rakefile b/railties/Rakefile old mode 100644 new mode 100755 index 9504661c0e..5137bee761 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -1,4 +1,4 @@ -require 'rake' +#!/usr/bin/env rake require 'rake/testtask' require 'rake/gempackagetask' diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile old mode 100644 new mode 100755 index d83cafc3be..4dc1023f1f --- a/railties/lib/rails/generators/rails/app/templates/Rakefile +++ b/railties/lib/rails/generators/rails/app/templates/Rakefile @@ -1,7 +1,7 @@ +#!/usr/bin/env rake # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require File.expand_path('../config/application', __FILE__) -require 'rake' <%= app_const %>.load_tasks diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt b/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt index 733e321c94..77149ae351 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile.tt @@ -1,4 +1,4 @@ -require 'rake' +#!/usr/bin/env rake require 'rake/testtask' require 'rake/rdoctask' diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile old mode 100644 new mode 100755 index 88f50f9f04..12350309bf --- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile @@ -1,12 +1,10 @@ -# encoding: UTF-8 -require 'rubygems' +#!/usr/bin/env rake begin require 'bundler/setup' rescue LoadError puts 'You must `gem install bundler` and `bundle install` to run rake tasks' end -require 'rake' require 'rake/rdoctask' Rake::RDocTask.new(:rdoc) do |rdoc| -- cgit v1.2.3 From ad8e1ba45271ad910ba943840f6f58e973e9a046 Mon Sep 17 00:00:00 2001 From: Cheah Chu Yeow Date: Thu, 25 Nov 2010 16:11:26 +0800 Subject: Fix missing word in ActionMailer::Base documentation. --- actionmailer/lib/action_mailer/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 0bf60a2c24..1c2949f375 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -234,8 +234,8 @@ module ActionMailer #:nodoc: # default :sender => 'system@example.com' # end # - # You can pass in any header value that a Mail::Message, out of the box, ActionMailer::Base - # sets the following: + # You can pass in any header value that a Mail::Message accepts. Out of the box, + # ActionMailer::Base sets the following: # # * :mime_version => "1.0" # * :charset => "UTF-8", -- cgit v1.2.3 From 90b0f6149160e6fedb12ea54f713ec3399bebec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 25 Nov 2010 10:44:42 +0100 Subject: #:nodoc: internal methods. --- actionpack/lib/action_dispatch/routing/mapper.rb | 62 ++++++++++++------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 879ccc5791..0b5437bd4f 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -707,61 +707,61 @@ module ActionDispatch end private - def scope_options + def scope_options #:nodoc: @scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym } end - def merge_path_scope(parent, child) + def merge_path_scope(parent, child) #:nodoc: Mapper.normalize_path("#{parent}/#{child}") end - def merge_shallow_path_scope(parent, child) + def merge_shallow_path_scope(parent, child) #:nodoc: Mapper.normalize_path("#{parent}/#{child}") end - def merge_as_scope(parent, child) + def merge_as_scope(parent, child) #:nodoc: parent ? "#{parent}_#{child}" : child end - def merge_shallow_prefix_scope(parent, child) + def merge_shallow_prefix_scope(parent, child) #:nodoc: parent ? "#{parent}_#{child}" : child end - def merge_module_scope(parent, child) + def merge_module_scope(parent, child) #:nodoc: parent ? "#{parent}/#{child}" : child end - def merge_controller_scope(parent, child) + def merge_controller_scope(parent, child) #:nodoc: child end - def merge_path_names_scope(parent, child) + def merge_path_names_scope(parent, child) #:nodoc: merge_options_scope(parent, child) end - def merge_constraints_scope(parent, child) + def merge_constraints_scope(parent, child) #:nodoc: merge_options_scope(parent, child) end - def merge_defaults_scope(parent, child) + def merge_defaults_scope(parent, child) #:nodoc: merge_options_scope(parent, child) end - def merge_blocks_scope(parent, child) + def merge_blocks_scope(parent, child) #:nodoc: merged = parent ? parent.dup : [] merged << child if child merged end - def merge_options_scope(parent, child) + def merge_options_scope(parent, child) #:nodoc: (parent || {}).except(*override_keys(child)).merge(child) end - def merge_shallow_scope(parent, child) + def merge_shallow_scope(parent, child) #:nodoc: child ? true : false end - def override_keys(child) + def override_keys(child) #:nodoc: child.key?(:only) || child.key?(:except) ? [:only, :except] : [] end end @@ -1167,7 +1167,7 @@ module ActionDispatch @scope[:scope_level_resource] end - def apply_common_behavior_for(method, resources, options, &block) + def apply_common_behavior_for(method, resources, options, &block) #:nodoc: if resources.length > 1 resources.each { |r| send(method, r, options, &block) } return true @@ -1197,23 +1197,23 @@ module ActionDispatch false end - def action_options?(options) + def action_options?(options) #:nodoc: options[:only] || options[:except] end - def scope_action_options? + def scope_action_options? #:nodoc: @scope[:options].is_a?(Hash) && (@scope[:options][:only] || @scope[:options][:except]) end - def scope_action_options + def scope_action_options #:nodoc: @scope[:options].slice(:only, :except) end - def resource_scope? + def resource_scope? #:nodoc: [:resource, :resources].include?(@scope[:scope_level]) end - def resource_method_scope? + def resource_method_scope? #:nodoc: [:collection, :member, :new].include?(@scope[:scope_level]) end @@ -1239,7 +1239,7 @@ module ActionDispatch @scope[:scope_level_resource] = old_resource end - def resource_scope(resource) + def resource_scope(resource) #:nodoc: with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do scope(parent_resource.resource_scope) do yield @@ -1247,30 +1247,30 @@ module ActionDispatch end end - def nested_options + def nested_options #:nodoc: {}.tap do |options| options[:as] = parent_resource.member_name options[:constraints] = { "#{parent_resource.singular}_id".to_sym => id_constraint } if id_constraint? end end - def id_constraint? + def id_constraint? #:nodoc: @scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp) end - def id_constraint + def id_constraint #:nodoc: @scope[:constraints][:id] end - def canonical_action?(action, flag) + def canonical_action?(action, flag) #:nodoc: flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s) end - def shallow_scoping? + def shallow_scoping? #:nodoc: shallow? && @scope[:scope_level] == :member end - def path_for_action(action, path) + def path_for_action(action, path) #:nodoc: prefix = shallow_scoping? ? "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path] @@ -1281,11 +1281,11 @@ module ActionDispatch end end - def action_path(name, path = nil) + def action_path(name, path = nil) #:nodoc: path || @scope[:path_names][name.to_sym] || name.to_s end - def prefix_name_for_action(as, action) + def prefix_name_for_action(as, action) #:nodoc: if as as.to_s elsif !canonical_action?(action, @scope[:scope_level]) @@ -1293,7 +1293,7 @@ module ActionDispatch end end - def name_for_action(as, action) + def name_for_action(as, action) #:nodoc: prefix = prefix_name_for_action(as, action) prefix = Mapper.normalize_name(prefix) if prefix name_prefix = @scope[:as] @@ -1323,7 +1323,7 @@ module ActionDispatch end end - module Shorthand + module Shorthand #:nodoc: def match(*args) if args.size == 1 && args.last.is_a?(Hash) options = args.pop -- cgit v1.2.3 From 731ca00b484379661786fac36c17db7e085603c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 25 Nov 2010 11:49:51 +0100 Subject: Dynamically generaeted helpers on collection should not clobber resources url helper [#6028 state:resolved] --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 ++ actionpack/test/dispatch/routing_test.rb | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 0b5437bd4f..880862c909 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1299,6 +1299,8 @@ module ActionDispatch name_prefix = @scope[:as] if parent_resource + return nil if as.nil? && action.nil? + collection_name = parent_resource.collection_name member_name = parent_resource.member_name end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 0ac8c249cb..bbd010ea6d 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -155,6 +155,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end resources :replies do + collection do + get 'page/:page' => 'replies#index', :page => %r{\d+} + get ':page' => 'replies#index', :page => %r{\d+} + end + new do post :preview end @@ -1241,6 +1246,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper + with_test_routes do + assert_equal '/replies', replies_path + end + end + def test_scoped_controller_with_namespace_and_action with_test_routes do assert_equal '/account/twitter/callback', account_callback_path("twitter") -- cgit v1.2.3 From c4d31d0f13783f3cfadbde666fd2a06996b456a2 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 15:36:08 -0300 Subject: Reuse lock_col variable instead calling locking_column class method. Signed-off-by: Santiago Pastorino --- activerecord/lib/active_record/locking/optimistic.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 9e1a33a6bf..6503004e38 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -89,7 +89,7 @@ module ActiveRecord affected_rows = relation.where( relation.table[self.class.primary_key].eq(quoted_id).and( - relation.table[self.class.locking_column].eq(quote_value(previous_value)) + relation.table[lock_col].eq(quote_value(previous_value)) ) ).arel.update(arel_attributes_values(false, false, attribute_names)) @@ -111,8 +111,9 @@ module ActiveRecord if persisted? table = self.class.arel_table - predicate = table[self.class.primary_key].eq(id) - predicate = predicate.and(table[self.class.locking_column].eq(send(self.class.locking_column).to_i)) + lock_col = self.class.locking_column + predicate = table[self.class.primary_key].eq(id). + and(table[lock_col].eq(send(lock_col).to_i)) affected_rows = self.class.unscoped.where(predicate).delete_all -- cgit v1.2.3 From 1b6b80355c52cc97d96f6d747271ff43efebab0a Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 16:01:41 -0300 Subject: Remove explicit return. Signed-off-by: Santiago Pastorino --- activerecord/lib/active_record/locking/optimistic.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 6503004e38..c0e1dda2bd 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -70,7 +70,7 @@ module ActiveRecord result[self.class.locking_column] ||= 0 end - return result + result end def update(attribute_names = @attributes.keys) #:nodoc: -- cgit v1.2.3 From ac6e9447e521a6ab2e3b60acd00a1ae2b863329e Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 16:11:53 -0300 Subject: Remove return, we are already returning self. Signed-off-by: Santiago Pastorino --- .../lib/active_record/associations/association_collection.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 774103342a..8a98c0fcbe 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -235,12 +235,12 @@ module ActiveRecord # Removes all records from this association. Returns +self+ so method calls may be chained. def clear - return self if length.zero? # forces load_target if it hasn't happened already - - if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy - destroy_all - else - delete_all + unless length.zero? # forces load_target if it hasn't happened already + if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy + destroy_all + else + delete_all + end end self -- cgit v1.2.3 From c91a13f8f557cc1d21fccb5f964d50438cd60a52 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 16:12:30 -0300 Subject: Use ternary instead explicit return. Signed-off-by: Santiago Pastorino --- activerecord/lib/active_record/associations/association_collection.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 8a98c0fcbe..e4e5ffd2b3 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -357,8 +357,7 @@ module ActiveRecord return false unless record.is_a?(@reflection.klass) return include_in_memory?(record) unless record.persisted? load_target if @reflection.options[:finder_sql] && !loaded? - return @target.include?(record) if loaded? - exists?(record) + loaded? ? @target.include?(record) : exists?(record) end def proxy_respond_to?(method, include_private = false) -- cgit v1.2.3 From a3bd62e1bae64569d0c2d45f8068f291e3e0e776 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Nov 2010 16:12:57 -0300 Subject: Remove explicit return. Signed-off-by: Santiago Pastorino --- .../active_record/associations/has_and_belongs_to_many_association.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index da742fa668..2c72fd0004 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -67,7 +67,7 @@ module ActiveRecord relation.insert(attributes) end - return true + true end def delete_records(records) @@ -80,7 +80,7 @@ module ActiveRecord ).delete end end - + def construct_joins "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}" end -- cgit v1.2.3 From 438c0188f8aa07d0e9895a2c13886901ca40ed5e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 25 Nov 2010 20:00:40 -0200 Subject: nil check unneeded ht. exviva --- activerecord/lib/active_record/associations/association_collection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index e4e5ffd2b3..ba9373ba6a 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -236,7 +236,7 @@ module ActiveRecord # Removes all records from this association. Returns +self+ so method calls may be chained. def clear unless length.zero? # forces load_target if it hasn't happened already - if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy + if @reflection.options[:dependent] == :destroy destroy_all else delete_all -- cgit v1.2.3 From 2c08ee97c78b7eb0883f1b0347c69a088c109388 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Fri, 26 Nov 2010 00:21:51 +0100 Subject: move the setting up of the mime collector into the collector on init --- actionpack/lib/action_controller/metal/mime_responds.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 6fe1f4ece5..2e58155b7c 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -258,9 +258,8 @@ module ActionController #:nodoc: # nil if :not_acceptable was sent to the client. # def retrieve_response_from_mimes(mimes=nil, &block) - collector = Collector.new { default_render } mimes ||= collect_mimes_from_class_level - mimes.each { |mime| collector.send(mime) } + collector = Collector.new(mimes) { default_render } block.call(collector) if block_given? if format = request.negotiate_mime(collector.order) @@ -277,8 +276,9 @@ module ActionController #:nodoc: include AbstractController::Collector attr_accessor :order - def initialize(&block) + def initialize(mimes, &block) @order, @responses, @default_response = [], {}, block + mimes.each { |mime| self.send(mime) } end def any(*args, &block) -- cgit v1.2.3 From a98d9d65aef31e3e1c95f59cd65ac7e1cbe76f89 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Fri, 26 Nov 2010 00:21:51 +0100 Subject: move the setting up of the mime collector into the collector on init Signed-off-by: Santiago Pastorino --- actionpack/lib/action_controller/metal/mime_responds.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 6fe1f4ece5..2e58155b7c 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -258,9 +258,8 @@ module ActionController #:nodoc: # nil if :not_acceptable was sent to the client. # def retrieve_response_from_mimes(mimes=nil, &block) - collector = Collector.new { default_render } mimes ||= collect_mimes_from_class_level - mimes.each { |mime| collector.send(mime) } + collector = Collector.new(mimes) { default_render } block.call(collector) if block_given? if format = request.negotiate_mime(collector.order) @@ -277,8 +276,9 @@ module ActionController #:nodoc: include AbstractController::Collector attr_accessor :order - def initialize(&block) + def initialize(mimes, &block) @order, @responses, @default_response = [], {}, block + mimes.each { |mime| self.send(mime) } end def any(*args, &block) -- cgit v1.2.3 From 74049b57fba3d26a5c9ac220619362ff36893b13 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 25 Nov 2010 21:31:14 -0200 Subject: implicit self here --- actionpack/lib/action_controller/metal/mime_responds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 2e58155b7c..9ba37134b8 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -278,7 +278,7 @@ module ActionController #:nodoc: def initialize(mimes, &block) @order, @responses, @default_response = [], {}, block - mimes.each { |mime| self.send(mime) } + mimes.each { |mime| send(mime) } end def any(*args, &block) -- cgit v1.2.3 From 9332cc582ebaa9b6755fa7bdfe0bc04dd51c098d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 26 Nov 2010 00:35:23 +0100 Subject: removes a couple of redundant selfs --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b7aac7a7c9..9b09b14c87 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1837,9 +1837,9 @@ MSG # Clear attributes and changed_attributes def clear_timestamp_attributes %w(created_at created_on updated_at updated_on).each do |attribute_name| - if self.has_attribute?(attribute_name) + if has_attribute?(attribute_name) self[attribute_name] = nil - self.changed_attributes.delete(attribute_name) + changed_attributes.delete(attribute_name) end end end -- cgit v1.2.3 From aab76ce199e14ad86ead6a45c60de297f60b1df6 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 13:51:35 +1100 Subject: There is no more routes_configuration_file method. --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index e39ccbc4b8..5ee8d0d0a3 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -39,8 +39,6 @@ Rails will use that particular setting to configure Active Record. h4. Rails General Configuration -* +config.routes_configuration_file+ overrides the default path for the routes configuration file. This defaults to +config/routes.rb+. - * +config.cache_classes+ controls whether or not application classes should be reloaded on each request. * +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class. -- cgit v1.2.3 From 26837734d6f50684056e059dfe853653a8ec50d0 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 13:54:37 +1100 Subject: Don't mention Rails 2.3, given that this is supposed to be a guide for Rails *3*. --- railties/guides/source/configuring.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 5ee8d0d0a3..952b20f586 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -23,10 +23,10 @@ To run some code before Rails itself is loaded, simply put it above the call to h3. Configuring Rails Components -In general, the work of configuring Rails means configuring the components of Rails, as well as configuring Rails itself. The +application.rb+ and environment-specific configuration files (such as +config/environments/production.rb+) allow you to specify the various settings that you want to pass down to all of the components. For example, the default Rails 2.3 +application.rb+ file includes one setting: +In general, the work of configuring Rails means configuring the components of Rails, as well as configuring Rails itself. The +application.rb+ and environment-specific configuration files (such as +config/environments/production.rb+) allow you to specify the various settings that you want to pass down to all of the components. For example, the default Rails 3.0 +application.rb+ file includes this setting: -config.filter_parameters << :password + config.filter_parameters += [:password] This is a setting for Rails itself. If you want to pass settings to individual Rails components, you can do so via the same +config+ object: -- cgit v1.2.3 From 8b114bec061d83352312d5710689ace4557443ab Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 13:56:39 +1100 Subject: Mention what cache_classes defaults to in all three default environments --- railties/guides/source/configuring.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 952b20f586..b3c9b4ad8d 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -32,14 +32,14 @@ In general, the work of configuring Rails means configuring the components of Ra This is a setting for Rails itself. If you want to pass settings to individual Rails components, you can do so via the same +config+ object: -config.active_record.timestamped_migrations = false + config.active_record.timestamped_migrations = false Rails will use that particular setting to configure Active Record. h4. Rails General Configuration -* +config.cache_classes+ controls whether or not application classes should be reloaded on each request. +* +config.cache_classes+ controls whether or not application classes should be reloaded on each request. Defaults to _true_ in development, _false_ in test and production. * +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class. -- cgit v1.2.3 From eb21791c5bd1881163328d4b0aa815a6e01a4ded Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 13:58:01 +1100 Subject: There is no controller_paths method in Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index b3c9b4ad8d..2cc71f2a12 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -45,8 +45,6 @@ h4. Rails General Configuration * +config.colorize_logging+ (true by default) specifies whether or not to use ANSI color codes when logging information. -* +config.controller_paths+ accepts an array of paths that will be searched for controllers. Defaults to +app/controllers+. - * +config.database_configuration_file+ overrides the default path for the database configuration file. Default to +config/database.yml+. * +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior. -- cgit v1.2.3 From db7c8abbb69f0ac06719b1e69f6276d633669a9e Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 13:59:51 +1100 Subject: There is no database_configuration_file method in Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 2cc71f2a12..c6d4a8a799 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -45,8 +45,6 @@ h4. Rails General Configuration * +config.colorize_logging+ (true by default) specifies whether or not to use ANSI color codes when logging information. -* +config.database_configuration_file+ overrides the default path for the database configuration file. Default to +config/database.yml+. - * +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior. * +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+. -- cgit v1.2.3 From c8af6c28cc82d0c6cbddd326f316405c570eac2d Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:01:09 +1100 Subject: load_once_paths is now autoload_paths in Rails 3 --- railties/guides/source/configuring.textile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index c6d4a8a799..fc4cc623a1 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -39,6 +39,10 @@ Rails will use that particular setting to configure Active Record. h4. Rails General Configuration +* +config.autoload_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +autoload_paths+. + +* +config.autoload_paths+ accepts an array of additional paths to prepend to the load path. By default, all app, lib, vendor and mock paths are included in this list. + * +config.cache_classes+ controls whether or not application classes should be reloaded on each request. Defaults to _true_ in development, _false_ in test and production. * +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class. @@ -49,9 +53,6 @@ h4. Rails General Configuration * +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+. -* +config.load_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +load_paths+. - -* +config.load_paths+ accepts an array of additional paths to prepend to the load path. By default, all app, lib, vendor and mock paths are included in this list. * +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+. -- cgit v1.2.3 From 9979251d7c2ab886410f5448dfec701218cc0d15 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:05:00 +1100 Subject: There is no plugin_loader config option in Rails 3 --- railties/guides/source/configuring.textile | 3 --- 1 file changed, 3 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index fc4cc623a1..dd6aba12d6 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -53,15 +53,12 @@ h4. Rails General Configuration * +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+. - * +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+. * +config.log_path+ overrides the path to the log file to use. Defaults to +log/#{environment}.log+ (e.g. log/development.log or log/production.log). * +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -* +config.plugin_loader+ overrides the class that handles loading each plugin. Defaults to +Rails::Plugin::Loader+. - * +config.plugin_locators+ overrides the class that handle finding the desired plugins that you‘d like to load for your application. By default it is the +Rails::Plugin::FileSystemLocator+. * +config.plugin_paths+ overrides the path to the root of the plugins directory. Defaults to +vendor/plugins+. -- cgit v1.2.3 From f01f7fc62953dd402ac118c07ae1851a181c4a67 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:05:22 +1100 Subject: There is no plugin_locators config option in Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index dd6aba12d6..6e5f917836 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -59,8 +59,6 @@ h4. Rails General Configuration * +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -* +config.plugin_locators+ overrides the class that handle finding the desired plugins that you‘d like to load for your application. By default it is the +Rails::Plugin::FileSystemLocator+. - * +config.plugin_paths+ overrides the path to the root of the plugins directory. Defaults to +vendor/plugins+. * +config.plugins+ accepts the list of plugins to load. If this is set to nil, all plugins will be loaded. If this is set to [], no plugins will be loaded. Otherwise, plugins will be loaded in the order specified. -- cgit v1.2.3 From ffca2d7f1469fd5444b1417e5d2d4bd3d7d0af18 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:05:43 +1100 Subject: There is no plugin_paths config option in Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 6e5f917836..e0ef07fa36 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -59,8 +59,6 @@ h4. Rails General Configuration * +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -* +config.plugin_paths+ overrides the path to the root of the plugins directory. Defaults to +vendor/plugins+. - * +config.plugins+ accepts the list of plugins to load. If this is set to nil, all plugins will be loaded. If this is set to [], no plugins will be loaded. Otherwise, plugins will be loaded in the order specified. * +config.preload_frameworks+ enables or disables preloading all frameworks at startup. -- cgit v1.2.3 From 69ae74210c6b49219b97a8e2e2711a81a523d8b8 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:07:42 +1100 Subject: There is no root_path config option in Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index e0ef07fa36..eee46b47ea 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -65,8 +65,6 @@ h4. Rails General Configuration * +config.reload_plugins+ enables or disables plugin reloading. -* +config.root_path+ configures the root path of the application. - * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. * +config.view_path+ sets the path of the root of an application's views. Defaults to +app/views+. -- cgit v1.2.3 From 9a4d6724573470d74ef9ef4572dbe04a1b5d9b4a Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:08:26 +1100 Subject: There is no view_path config option in Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index eee46b47ea..e614d232fe 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -67,8 +67,6 @@ h4. Rails General Configuration * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. -* +config.view_path+ sets the path of the root of an application's views. Defaults to +app/views+. - * +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_. h4. Configuring i18n -- cgit v1.2.3 From 33298d8e38eab9ef2e528478fbd2d4585f732d82 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:16:57 +1100 Subject: There is no config.action_controller.params_parser method for Rails 3. This is now handled by the ActionDispatch::ParamsParser middleware. --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index e614d232fe..03a360f87f 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -121,8 +121,6 @@ h4. Configuring Action Controller WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. -* +config.action_controller.param_parsers+ provides an array of handlers that can extract information from incoming HTTP requests and add it to the +params+ hash. By default, parsers for multipart forms, URL-encoded forms, XML, and JSON are active. - * +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8". * +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -- cgit v1.2.3 From f1406a905adc77ea719a6f3f85229d43a4766dc5 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:19:17 +1100 Subject: There is no config.action_controller.resource_path_names method for Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 03a360f87f..b4dc223c71 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -125,8 +125,6 @@ WARNING: Threadsafe operation is incompatible with the normal workings of develo * +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -* +config.action_controller.resource_path_names+ is a hash of default names for several RESTful actions. By default, the new action is named +new+ and the edit action is named +edit+. - * +config.action_controller.request_forgery_protection_token+ sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ sets it to +:authenticity_token+ by default. * +config.action_controller.optimise_named_routes+ turns on some optimizations in generating the routing table. It is set to +true+ by default. -- cgit v1.2.3 From 3eec4d4f93df70ea9c421df44c23ca0d4595534c Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:20:33 +1100 Subject: There is no config.action_controller.optimize_named_routes method for Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index b4dc223c71..d6df1e7cbe 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -127,8 +127,6 @@ WARNING: Threadsafe operation is incompatible with the normal workings of develo * +config.action_controller.request_forgery_protection_token+ sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ sets it to +:authenticity_token+ by default. -* +config.action_controller.optimise_named_routes+ turns on some optimizations in generating the routing table. It is set to +true+ by default. - * +config.action_controller.allow_forgery_protection+ enables or disables CSRF protection. By default this is +false+ in test mode and +true+ in all other modes. * +config.action_controller.relative_url_root+ can be used to tell Rails that you are deploying to a subdirectory. The default is +ENV['RAILS_RELATIVE_URL_ROOT']+. -- cgit v1.2.3 From b4ec8ad09c028a5e322a04eacf487b775a672ec2 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:21:04 +1100 Subject: Separate Action Dispatch and Action Pack sections in Configuration Guide. --- railties/guides/source/configuring.textile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index d6df1e7cbe..c948f680f8 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -131,8 +131,6 @@ WARNING: Threadsafe operation is incompatible with the normal workings of develo * +config.action_controller.relative_url_root+ can be used to tell Rails that you are deploying to a subdirectory. The default is +ENV['RAILS_RELATIVE_URL_ROOT']+. -* +config.action_dispatch.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class. - The caching code adds two additional settings: * +ActionController::Base.page_cache_directory+ sets the directory where Rails will create cached pages for your web server. The default is +Rails.public_path+ (which is usually set to +Rails.root + "/public"+). @@ -147,6 +145,10 @@ The Active Record session store can also be configured: * +ActiveRecord::SessionStore::Session.data_column_name+ sets the name of the column which stores marshaled session data. Defaults to +data+. +h4. Configuring Action Dispatch + +* +config.action_dispatch.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class. + h4. Configuring Action View There are only a few configuration options for Action View, starting with four on +ActionView::Base+: -- cgit v1.2.3 From 3d8d21b5132bb6852fe9533710d9105ab4d30ec8 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:23:40 +1100 Subject: There is no config.action_view.warn_cache_misses method for Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index c948f680f8..0803c1e7f4 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -155,8 +155,6 @@ There are only a few configuration options for Action View, starting with four o * +config.action_view.debug_rjs+ specifies whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it). The default is +false+. -* +config.action_view.warn_cache_misses+ tells Rails to display a warning whenever an action results in a cache miss on your view paths. The default is +false+. - * +config.action_view.field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is Proc.new{ |html_tag, instance| %Q(%<div class="field_with_errors">#{html_tag}</div>).html_safe } * +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+. -- cgit v1.2.3 From 8b49fae3eaa8d227261be06124ee7a8041ea77d7 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:24:51 +1100 Subject: There is no config.action_mailer.template_root method for Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 0803c1e7f4..67028c683b 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -167,8 +167,6 @@ h4. Configuring Action Mailer There are a number of settings available on +config.action_mailer+: -* +config.action_mailer.template_root+ gives the root folder for Action Mailer templates. - * +config.action_mailer.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Mailer. Set to nil to disable logging. * +config.action_mailer.smtp_settings+ allows detailed configuration for the +:smtp+ delivery method. It accepts a hash of options, which can include any of these options: -- cgit v1.2.3 From e968acc21c7e60e09a8ef076a00ef22bce2813b9 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:25:46 +1100 Subject: Update ActionMailer documentation to not use deprecated template_root method as documentation, but rather raise_delivery_errors method --- actionmailer/lib/action_mailer/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 0bf60a2c24..d13ae3dd3a 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -273,7 +273,7 @@ module ActionMailer #:nodoc: # = Configuration options # # These options are specified on the class level, like - # ActionMailer::Base.template_root = "/my/templates" + # ActionMailer::Base.raise_delivery_errors = true # # * default - You can pass this in at a class level as well as within the class itself as # per the above section. -- cgit v1.2.3 From 926d64e8b3a0cfbc233a0c564b5dfb0aeacb9546 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:32:20 +1100 Subject: There is no config.action_mailer.default_charset method for Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 67028c683b..b5f32671a8 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -187,8 +187,6 @@ There are a number of settings available on +config.action_mailer+: * +config.action_mailer.perform_deliveries+ specifies whether mail will actually be delivered. By default this is +true+; it can be convenient to set it to +false+ for testing. -* +config.action_mailer.default_charset+ tells Action Mailer which character set to use for the body and for encoding the subject. It defaults to +utf-8+. - * +config.action_mailer.default_content_type+ specifies the default content type used for the main part of the message. It defaults to "text/plain" * +config.action_mailer.default_mime_version+ is the default MIME version for the message. It defaults to +1.0+. -- cgit v1.2.3 From 66aefed136ef0c087e9448d8098cea629e211144 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:32:36 +1100 Subject: There is no config.action_mailer.default_content_type method for Rails 3 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index b5f32671a8..df56768de9 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -187,8 +187,6 @@ There are a number of settings available on +config.action_mailer+: * +config.action_mailer.perform_deliveries+ specifies whether mail will actually be delivered. By default this is +true+; it can be convenient to set it to +false+ for testing. -* +config.action_mailer.default_content_type+ specifies the default content type used for the main part of the message. It defaults to "text/plain" - * +config.action_mailer.default_mime_version+ is the default MIME version for the message. It defaults to +1.0+. * +config.action_mailer.default_implicit_parts_order+ - When a message is built implicitly (i.e. multiple parts are assembled from templates -- cgit v1.2.3 From d0006121c581d0acadf7d46f955051be10128238 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:43:16 +1100 Subject: Mention the default config.action_mailer.default options in Configuration guide --- railties/guides/source/configuring.textile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index df56768de9..bb8561587c 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -187,7 +187,13 @@ There are a number of settings available on +config.action_mailer+: * +config.action_mailer.perform_deliveries+ specifies whether mail will actually be delivered. By default this is +true+; it can be convenient to set it to +false+ for testing. -* +config.action_mailer.default_mime_version+ is the default MIME version for the message. It defaults to +1.0+. +* +config.action_mailer.default+ configures Action Mailer defaults. These default to: + + :mime_version => "1.0", + :charset => "UTF-8", + :content_type => "text/plain", + :parts_order => [ "text/plain", "text/enriched", "text/html" ] + * +config.action_mailer.default_implicit_parts_order+ - When a message is built implicitly (i.e. multiple parts are assembled from templates which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to +["text/html", "text/enriched", "text/plain"]+. Items that appear first in the array have higher priority in the mail client -- cgit v1.2.3 From e96a64fb3910ad0ec2f3b7a970ae6047dfb89e01 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:53:12 +1100 Subject: There is no config.action_mailer.default_implicit_parts_order method for Rails 3 --- railties/guides/source/configuring.textile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index bb8561587c..a7244de19f 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -195,10 +195,6 @@ There are a number of settings available on +config.action_mailer+: :parts_order => [ "text/plain", "text/enriched", "text/html" ] -* +config.action_mailer.default_implicit_parts_order+ - When a message is built implicitly (i.e. multiple parts are assembled from templates -which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to +["text/html", "text/enriched", "text/plain"]+. Items that appear first in the array have higher priority in the mail client -and appear last in the mime encoded message. - h4. Configuring Active Resource There is a single configuration setting available on +config.active_resource+: -- cgit v1.2.3 From 1d5db09ca7cd2d8fa2d05c138e0211979fdbe966 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:55:56 +1100 Subject: There is no RAILS_GEM_VERSION environment variable or constant for Rails 3, since this is now managed by Bundler --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index a7244de19f..a4e85de590 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -247,8 +247,6 @@ Some parts of Rails can also be configured externally by supplying environment v * +ENV["RAILS_CACHE_ID"]+ and +ENV["RAILS_APP_VERSION"]+ are used to generate expanded cache keys in Rails' caching code. This allows you to have multiple separate caches from the same application. -* +ENV['RAILS_GEM_VERSION']+ defines the version of the Rails gems to use, if +RAILS_GEM_VERSION+ is not defined in your +environment.rb+ file. - h3. Changelog * August 13, 2009: Updated with config syntax and added general configuration options by "John Pignata" -- cgit v1.2.3 From 788fdb2ac1ae930660a135000abf92c36735815f Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 14:57:16 +1100 Subject: Update Changelog for configuring guide --- railties/guides/source/configuring.textile | 1 + 1 file changed, 1 insertion(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index a4e85de590..efb5663e33 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -249,6 +249,7 @@ Some parts of Rails can also be configured externally by supplying environment v h3. Changelog +* November 26, 2010: Removed all config settings not available in Rails 3 (Ryan Bigg) * August 13, 2009: Updated with config syntax and added general configuration options by "John Pignata" * January 3, 2009: First reasonably complete draft by "Mike Gunderloy":credits.html#mgunderloy * November 5, 2008: Rough outline by "Mike Gunderloy":credits.html#mgunderloy -- cgit v1.2.3 From ff04f6fee4a71da867398f49cc2d9e22760dd7e0 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 15:09:17 +1100 Subject: root_path is now simply root in Rails 3 --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index efb5663e33..30f8e31095 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -65,6 +65,8 @@ h4. Rails General Configuration * +config.reload_plugins+ enables or disables plugin reloading. +* +config.root+ configures the root path of the application. + * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. * +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_. -- cgit v1.2.3 From 6bb462861f7cca070f1d3933c19c10b18fa44c9e Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 16:46:06 +1100 Subject: There is no more load_application_initializers in Rails 3 --- railties/guides/source/configuring.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 30f8e31095..3986faa3cd 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -13,7 +13,7 @@ Rails offers (at least) four good spots to place initialization code: * application.rb * Environment-specific Configuration Files -* Initializers (load_application_initializers) +* Initializers * After-Initializers h3. Running Code Before Rails -- cgit v1.2.3 From 72e973ebbcddbeb172c2dd3a7ddc12957dc5bec0 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 17:03:14 +1100 Subject: Add mention of after_initialize to the config guide --- railties/guides/source/configuring.textile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 3986faa3cd..154eebc0c1 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -39,6 +39,14 @@ Rails will use that particular setting to configure Active Record. h4. Rails General Configuration +* +config.after_initialize+ takes a block which will be ran _after_ Rails has finished initializing. Useful for configuring values set up by other initializers: + + + config.after_initialize do + ActionView::Base.sanitized_allowed_tags.delete 'div' + end + + * +config.autoload_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +autoload_paths+. * +config.autoload_paths+ accepts an array of additional paths to prepend to the load path. By default, all app, lib, vendor and mock paths are included in this list. -- cgit v1.2.3 From 8d6ac59d58ff7fce851a894d714e0677c11d7eb6 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 18:06:14 +1100 Subject: Added documentation for config.generators to the config guide --- railties/guides/source/configuring.textile | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 154eebc0c1..c395e42c00 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -47,6 +47,8 @@ h4. Rails General Configuration end +* +config.app_generators+ alternate name for +config.generators+. See the "Configuring Generators" section below for how to use this. + * +config.autoload_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +autoload_paths+. * +config.autoload_paths+ accepts an array of additional paths to prepend to the load path. By default, all app, lib, vendor and mock paths are included in this list. @@ -79,6 +81,31 @@ h4. Rails General Configuration * +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_. +h4. Configuring Generators + +Rails 3 allows you to alter what generators are used with the +config.generators+ method. This method takes a block: + + + config.generators do |g| + g.orm :active_record + g.test_framework :test_unit + end + + +The full set of methods that can be used in this block are as follows: + +* +force_plural+ allows pluralized model names. Defaults to _false_. +* +helper+ defines whether or not to generate helpers. Defaults to _true_ +* +orm+ defines which orm to use. Defaults to _nil_, so will use Active Record by default. +* +integration_tool+ defines which integration tool to use. Defaults to _nil_ +* +performance_tool+ defines which performance tool to use. Defaults to _nil_ +* +resource_controller+ defines which generator to use for generating a controller when using +rails generate resource+. Defaults to +:controller+. +* +scaffold_controller+ different from +resource_controller+, defines which generator to use for generating a _scaffolded_ controller when using +rails generate scaffold+. Defaults to +:scaffold_controller+ +* +stylesheets+ turns on the hook for stylesheets in generators. Used in Rails for when the +scaffold+ generator is ran, but this hook can be used in other generates as well. +* +test_framework+ defines which test framework to use. Defaults to _nil_, so will use Test::Unit by default. +* +template_engine+ defines which template engine to use, such as ERB or Haml. Defaults to +:erb+. + + h4. Configuring i18n * +config.i18n.default_locale+ sets the default locale of an application used for i18n. Defaults to +:en+. -- cgit v1.2.3 From ce9456eca0c4ea77a42aaad5e8080842c1c01422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 26 Nov 2010 11:55:56 +0100 Subject: Only convert direct hash instances in hash with indifferent access. --- activesupport/lib/active_support/hash_with_indifferent_access.rb | 7 +++---- activesupport/test/core_ext/hash_ext_test.rb | 8 ++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 320f5c1c92..6a344867ee 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -140,11 +140,10 @@ module ActiveSupport end def convert_value(value) - case value - when Hash + if value.class == Hash self.class.new_from_hash_copying_default(value) - when Array - value.dup.replace(value.collect { |e| e.is_a?(Hash) ? self.class.new_from_hash_copying_default(e) : e }) + elsif value.is_a?(Array) + value.dup.replace(value.map { |e| convert_value(e) }) else value end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 370f26b0d7..74223dd7f2 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -12,6 +12,9 @@ class HashExtTest < Test::Unit::TestCase class SubclassingArray < Array end + class SubclassingHash < Hash + end + def setup @strings = { 'a' => 1, 'b' => 2 } @symbols = { :a => 1, :b => 2 } @@ -105,6 +108,11 @@ class HashExtTest < Test::Unit::TestCase assert_equal @strings, @mixed.with_indifferent_access.dup.stringify_keys! end + def test_hash_subclass + flash = { "foo" => SubclassingHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access + assert_kind_of SubclassingHash, flash["foo"] + end + def test_indifferent_assorted @strings = @strings.with_indifferent_access @symbols = @symbols.with_indifferent_access -- cgit v1.2.3 From 5b9f6a74db1a58c5557087cd924fd67dd9cf43e0 Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Fri, 26 Nov 2010 16:41:33 +0530 Subject: Resolving LH #5986, cookies doc updates --- actionpack/lib/action_dispatch/middleware/cookies.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index c4c2e1acd7..b0a4e3d949 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -16,17 +16,23 @@ module ActionDispatch # Examples for writing: # # # Sets a simple session cookie. + # # This cookie will be deleted when the user's browser is closed. # cookies[:user_name] = "david" # + # # Assign an array of values to a cookie. + # cookies[:lat_lon] = [47.68, -122.37] + # # # Sets a cookie that expires in 1 hour. # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now } # # # Sets a signed cookie, which prevents a user from tampering with its value. - # # You must specify a value in ActionController::Base.cookie_verifier_secret. - # cookies.signed[:remember_me] = [current_user.id, current_user.salt] + # # The cookie is signed by your app's config.secret_token value. + # # Rails generates this value by default when you create a new Rails app. + # cookies.signed[:user_id] = current_user.id # # # Sets a "permanent" cookie (which expires in 20 years from now). # cookies.permanent[:login] = "XJ-122" + # # # You can also chain these methods: # cookies.permanent.signed[:login] = "XJ-122" # @@ -34,6 +40,7 @@ module ActionDispatch # # cookies[:user_name] # => "david" # cookies.size # => 2 + # cookies[:lat_lon] # => [47.68, -122.37] # # Example for deleting: # @@ -275,7 +282,7 @@ module ActionDispatch "integrity hash for cookie session data. Use " + "config.secret_token = \"some secret phrase of at " + "least #{SECRET_MIN_LENGTH} characters\"" + - "in config/application.rb" + "in config/initializers/secret_token.rb" end if secret.length < SECRET_MIN_LENGTH -- cgit v1.2.3 From fe2103a1c8336e8ef164d696e8eaeab62158d84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Mej=C3=ADa?= Date: Fri, 26 Nov 2010 14:11:28 +0100 Subject: Added missing word. --- railties/guides/source/contributing_to_rails.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile index f501335958..721adc00d7 100644 --- a/railties/guides/source/contributing_to_rails.textile +++ b/railties/guides/source/contributing_to_rails.textile @@ -127,7 +127,7 @@ You can now run tests as you did for +sqlite3+: rake test_mysql -You can also +myqsl+ with +postgresql+, +jdbcmysql+, +jdbcsqlite3+ or +jdbcpostgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs. +You can also replace +myqsl+ with +postgresql+, +jdbcmysql+, +jdbcsqlite3+ or +jdbcpostgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs. NOTE: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite 3. Subtle differences between the various Active Record database adapters have been behind the rejection of many patches that looked OK when tested only against MySQL. -- cgit v1.2.3 From 3a50799ab8aede48767612ba1d4cc904f294cdb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Mej=C3=ADa?= Date: Fri, 26 Nov 2010 17:16:52 +0100 Subject: Mentioning catch with Bundler remembering options between different calls --- railties/guides/source/contributing_to_rails.textile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile index 721adc00d7..ccb9db5eee 100644 --- a/railties/guides/source/contributing_to_rails.textile +++ b/railties/guides/source/contributing_to_rails.textile @@ -105,12 +105,17 @@ mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost'; -Then ensure you run bundle install without the +--without db+ option: +Now you'll have to install Active Record dependencies. This step is a little tricky because just running +bundle install+ without the +--without db+ parameter won't get those dependencies installed. It turns out that bundler remembers the +--without db+ parameter between calls so you'll have to manually override this. (See the "+bundle_install+ man page":http://gembundler.com/man/bundle-install.1.html for details) + +The easiest way to do this is to remove bundler's config file and then run +install+ again: +rm .bundle/config bundle install +INFO: If you don't feel comfortable deleting bundler's config file, you can achieve the same effect by manually removing the "+BUNDLE_WITHOUT: db+" line on +.bundle/config+. + Finally, enter this from the +activerecord+ directory to create the test databases: -- cgit v1.2.3 From 6aa408e11cc99603778eb99e8cd2e585e0f97b50 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 26 Nov 2010 21:14:58 +1100 Subject: Add mention of config.serve_static_assets to config guide --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index c395e42c00..91c4c2b0f7 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -77,6 +77,8 @@ h4. Rails General Configuration * +config.root+ configures the root path of the application. +* +config.serve_static_assets+ configures Rails to serve static assets. Defaults to _true_, but in the production environment is turned off. The server software used to run the application should be used to serve the assets instead. + * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. * +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_. -- cgit v1.2.3 From 167964149e76d66742675a1505e1030e1c8f440b Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sat, 27 Nov 2010 07:56:08 +1100 Subject: Add explicit statement that verify_authenticity_token can be turned off for actions. --- .../lib/action_controller/metal/request_forgery_protection.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 02f577647e..148efbb081 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -53,9 +53,13 @@ module ActionController #:nodoc: # class FooController < ApplicationController # protect_from_forgery :except => :index # - # # you can disable csrf protection on controller-by-controller basis: - # skip_before_filter :verify_authenticity_token - # end + # You can disable csrf protection on controller-by-controller basis: + # + # skip_before_filter :verify_authenticity_token + # + # It can also be disabled for specific controller actions: + # + # skip_before_filter :verify_authenticity_token, :except => [:create] # # Valid Options: # -- cgit v1.2.3 From 3ee0f0379cc0ae3dd44a050e35d644f1ad7a920b Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sat, 27 Nov 2010 08:15:29 +1100 Subject: Add "Configuring Middleware" section to config guide, starting with mentioning what every bit of middleware does. --- railties/guides/source/configuring.textile | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 91c4c2b0f7..8187c647dd 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -67,6 +67,8 @@ h4. Rails General Configuration * +config.log_path+ overrides the path to the log file to use. Defaults to +log/#{environment}.log+ (e.g. log/development.log or log/production.log). +* +config.middleware+ allows you to configure the application's middleware. This is covered in depth in the "Configuring Middleware" section below. + * +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. * +config.plugins+ accepts the list of plugins to load. If this is set to nil, all plugins will be loaded. If this is set to [], no plugins will be loaded. Otherwise, plugins will be loaded in the order specified. @@ -107,6 +109,29 @@ The full set of methods that can be used in this block are as follows: * +test_framework+ defines which test framework to use. Defaults to _nil_, so will use Test::Unit by default. * +template_engine+ defines which template engine to use, such as ERB or Haml. Defaults to +:erb+. +h4. Configuring Middleware + +Every Rails application comes with a standard set of middleware which it uses in this order in the development environment: + +* +ActionDispatch::Static+ is used to serve static assets. Disabled if +config.serve_static_assets+ is _true_. +* +Rack::Lock+ Will wrap the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to _false_, which it is by default. +* +ActiveSupport::Cache::Strategy::LocalCache+ Serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. +* +Rack::Runtime+ Sets an +X-Runtime+ header, containing the time (in seconds) taken to execute the request. +* +Rails::Rack::Logger+ Will notify the logs that the request has began. After request is complete, flushes all the logs. +* +ActionDispatch::ShowExceptions+ rescues any exception returned by the application and renders nice exception pages if the request is local or if +config.consider_all_requests_local+ is set to _true_. If +config.action_dispatch.show_exceptions+ is set to _false_, exceptions will be raised regardless. +* +ActionDispatch::RemoteIp+ checks for IP spoofing attacks. Configurable with the +config.action_dispatch.ip_spoofing_check+ and +config.action_dispatch.trusted_proxies+ settings. +* +Rack::Sendfile+ The Sendfile middleware intercepts responses whose body is being served from a file and replaces it with a server specific X-Sendfile header. Configurable with +config.action_dispatch_ +* +ActionDispatch::Callbacks+ Runs the prepare callbacks before serving the request. +* +ActiveRecord::ConnectionAdapters::ConnectionManagement+ cleans active connections after each request, unless the +rack.test+ key in the request environment is set to _true_. +* +ActiveRecord::QueryCache+ caches all +SELECT+ queries generated in a request. If an +INSERT+ or +UPDATE+ takes place then the cache is cleaned. +* +ActionDispatch::Cookies+ sets cookies for the request. +* +ActionDispatch::Session::CookieStore+ is responsible for storing the session in cookies. An alternate middleware can be used for this by changing the +config.action_controller.session_store+ to an alternate value. Additionally, options passed to this can be configured by using +config.action_controller.session_options+. +* +ActionDispatch::Flash+ sets up the +flash+ keys. Only available if +config.action_controller.session_store+ is set to a value. +* +ActionDispatch::ParamsParser+ parses out parameters from the request into +params+ +* +Rack::MethodOverride+ allows the method to be overridden if +params[:_method]+ is set. This is the middleware which supports the PUT and DELETE HTTP method types. +* +ActionDispatch::Head+ converts HEAD requests to GET requests and serves them as so. +* +ActionDispatch::BestStandardsSupport+ enables "best standards support" so that IE8 renders some elements correctly. + h4. Configuring i18n -- cgit v1.2.3 From c8c95fc519f9d0e23e012d4434e6c3fe0f6e2a62 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sat, 27 Nov 2010 08:29:29 +1100 Subject: Add methods for configuring middleware to config guide --- railties/guides/source/configuring.textile | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 8187c647dd..ca78cc0e6d 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -132,6 +132,29 @@ Every Rails application comes with a standard set of middleware which it uses in * +ActionDispatch::Head+ converts HEAD requests to GET requests and serves them as so. * +ActionDispatch::BestStandardsSupport+ enables "best standards support" so that IE8 renders some elements correctly. +Besides these usual middleware, you can add your own by using the +config.middleware.use+ method: + + + config.middleware.use Magical::Unicorns + + +This will put the +Magical::Unicorns+ middleware on the end of the stack. If you wish to put this middleware before another use +insert_before+: + + + config.middleware.insert_before ActionDispatch::Head, Magical::Unicorns + + +There's also +insert_after+ which will insert a middleware _after_ another: + + + config.middleware.insert_after ActionDispatch::Head, Magical::Unicorns + + +Middlewares can also be completely swapped out and replaced with others: + + + config.middleware.swap ActionDispatch::BestStandardsSupport, Magical::Unicorns + h4. Configuring i18n -- cgit v1.2.3 From f28916194cc19759dd6b1ce4f22ef59b49f0c8f8 Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Sat, 27 Nov 2010 15:20:46 +0530 Subject: Resolves LH #6063, should be :expires_in not :expire_in --- activesupport/lib/active_support/cache.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 9098ffbfec..d31dad6bc7 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -210,8 +210,8 @@ module ActiveSupport # be specified as an option to the construction in which call all entries will be # affected. Or it can be supplied to the +fetch+ or +write+ method for just one entry. # - # cache = ActiveSupport::Cache::MemoryStore.new(:expire_in => 5.minutes) - # cache.write(key, value, :expire_in => 1.minute) # Set a lower value for one entry + # cache = ActiveSupport::Cache::MemoryStore.new(:expires_in => 5.minutes) + # cache.write(key, value, :expires_in => 1.minute) # Set a lower value for one entry # # Setting :race_condition_ttl is very useful in situations where a cache entry # is used very frequently unver heavy load. If a cache expires and due to heavy load -- cgit v1.2.3 From 667bcac439f2bf0bdd072cf9dc28658aedde5e93 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sat, 27 Nov 2010 21:40:42 +1100 Subject: Application templates should work with HTTPS too --- railties/lib/rails/generators/app_base.rb | 2 +- railties/test/generators/shared_generator_tests.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index f5c626553c..a560888edd 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -102,7 +102,7 @@ module Rails def set_default_accessors! self.rails_template = case options[:template] - when /^http:\/\// + when /^https:\/\// options[:template] when String File.expand_path(options[:template], Dir.pwd) diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index d117656fbd..9e6169721b 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -113,6 +113,15 @@ module SharedGeneratorTests assert_match /It works!/, silence(:stdout){ generator.invoke_all } end + def test_template_is_executed_when_supplied_an_https_path + path = "https://gist.github.com/103208.txt" + template = %{ say "It works!" } + template.instance_eval "def read; self; end" # Make the string respond to read + + generator([destination_root], :template => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template) + assert_match /It works!/, silence(:stdout){ generator.invoke_all } + end + def test_dev_option generator([destination_root], :dev => true).expects(:run).with("#{@bundle_command} install") silence(:stdout){ generator.invoke_all } -- cgit v1.2.3 From 04922bafda9456ca9b6b8c59f231c402cb7952b7 Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Sat, 27 Nov 2010 19:56:08 +0530 Subject: grammar fix --- activemodel/lib/active_model/attribute_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index c1c5640616..fc5f5c4c66 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -46,8 +46,8 @@ module ActiveModel # end # end # - # Notice that whenever you include ActiveModel::AttributeMethods in your class, - # it requires you to implement a attributes methods which returns a hash + # Note that whenever you include ActiveModel::AttributeMethods in your class, + # it requires you to implement an attributes method which returns a hash # with each attribute name in your model as hash key and the attribute value as # hash value. # -- cgit v1.2.3 From 927c582acfb9253be493e7ec584c0503555e00fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 27 Nov 2010 08:12:49 -0800 Subject: Update CHANGELOG. --- actionpack/CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 74d017cc3d..2ab2ca1542 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.1.0 (unreleased)* +* url_for and named url helpers now accept :subdomain and :domain as options [Josh Kalderimis] + * Added config.action_controller.include_all_helpers. By default 'helper :all' is done in ActionController::Base, which includes all the helpers by default. Setting include_all_helpers to false will result in including only application_helper and helper corresponding to controller (like foo_helper for foo_controller). [Piotr Sarnacki] * Added a convenience idiom to generate HTML5 data-* attributes in tag helpers from a :data hash of options: -- cgit v1.2.3 From 76053fe4d12bd2be38e16b3322f9de73e09b43bc Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sun, 28 Nov 2010 10:42:43 +1100 Subject: Allow http templates again, fix the broken test [#5926 state:committed] Signed-off-by: Santiago Pastorino --- railties/lib/rails/generators/app_base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index a560888edd..2d0c10efca 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -102,7 +102,7 @@ module Rails def set_default_accessors! self.rails_template = case options[:template] - when /^https:\/\// + when /^https?:\/\// options[:template] when String File.expand_path(options[:template], Dir.pwd) -- cgit v1.2.3 From 1662aa1a15ad3f2218f85e4bd58aea56708a87a1 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 11 Nov 2010 05:27:40 +0900 Subject: Added a space before "do" keyword --- actionpack/lib/action_view/helpers/form_tag_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 92645f5cf9..50f065f03d 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -39,7 +39,7 @@ module ActionView # form_tag('/upload', :multipart => true) # # =>
# - # <%= form_tag('/posts')do -%> + # <%= form_tag('/posts') do -%> #
<%= submit_tag 'Save' %>
# <% end -%> # # =>
-- cgit v1.2.3 From 2029187c5611e2da9995637d4f738ed3af834172 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sun, 28 Nov 2010 09:41:49 +0900 Subject: s/myqsl/mysql/ --- railties/guides/source/contributing_to_rails.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile index ccb9db5eee..1a1f4e9858 100644 --- a/railties/guides/source/contributing_to_rails.textile +++ b/railties/guides/source/contributing_to_rails.textile @@ -132,7 +132,7 @@ You can now run tests as you did for +sqlite3+: rake test_mysql
-You can also replace +myqsl+ with +postgresql+, +jdbcmysql+, +jdbcsqlite3+ or +jdbcpostgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs. +You can also replace +mysql+ with +postgresql+, +jdbcmysql+, +jdbcsqlite3+ or +jdbcpostgresql+. Check out the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/ci_build.rb+ to see the test suite that the Rails continuous integration server runs. NOTE: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite 3. Subtle differences between the various Active Record database adapters have been behind the rejection of many patches that looked OK when tested only against MySQL. -- cgit v1.2.3 From 9bd291407c70daac1b2777f552f599a6fff9f387 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 27 Nov 2010 22:16:51 -0500 Subject: make it clear that recompilation happens only in development mode --- actionpack/lib/action_view/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 1beae37af3..15944138f7 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -76,8 +76,8 @@ module ActionView #:nodoc: # # === Template caching # - # By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will - # check the file's modification time and recompile it. + # By default, Rails will compile each template to a method in order to render it. When you alter a template, + # Rails will check the file's modification time and recompile it in development mode. # # == Builder # -- cgit v1.2.3 From a4c360e8c7b7c2635de3c1172dafff7b7975ecc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 28 Nov 2010 11:48:39 +0100 Subject: trailing star mimes should respect the order in which mime types are defined. --- actionpack/lib/action_dispatch/http/mime_type.rb | 18 +++++++++++------- actionpack/test/dispatch/mime_type_test.rb | 19 ++++++------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 08eab5634a..3e3478a44e 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -115,15 +115,20 @@ module Mime end else # keep track of creation order to keep the subsequent sort stable - list = [] - accept_header.split(/,/).each_with_index do |header, index| + list, index = [], 0 + accept_header.split(/,/).each do |header| params, q = header.split(/;\s*q=/) - if params + if params.present? params.strip! + if params =~ TRAILING_STAR_REGEXP - parse_data_with_trailing_star($1).each { |m| list << AcceptItem.new(index, m.to_s, q) } + parse_data_with_trailing_star($1).each do |m| + list << AcceptItem.new(index, m.to_s, q) + index += 1 + end else - list << AcceptItem.new(index, params, q) unless params.empty? + list << AcceptItem.new(index, params, q) + index += 1 end end end @@ -178,8 +183,7 @@ module Mime # input: 'application' # returend value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM def parse_data_with_trailing_star(input) - keys = Mime::LOOKUP.keys.select{|k| k.include?(input)} - Mime::LOOKUP.values_at(*keys).uniq + Mime::SET.select { |m| m =~ input } end # This method is opposite of register method. diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 9424d88498..e80d339472 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -31,30 +31,23 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse text with trailing star at the beginning" do accept = "text/*, text/html, application/json, multipart/form-data" - expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::TEXT, Mime::YAML, Mime::JS, Mime::MULTIPART_FORM] + expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML, Mime::JSON, Mime::MULTIPART_FORM] parsed = Mime::Type.parse(accept) - assert_equal expect.size, parsed.size - Range.new(0,expect.size-1).to_a.each do |index| - assert_equal expect[index], parsed[index], "Failed for index number #{index}" - end + assert_equal expect, parsed end test "parse text with trailing star in the end" do accept = "text/html, application/json, multipart/form-data, text/*" - expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::XML, Mime::ICS, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT] + expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML] parsed = Mime::Type.parse(accept) - assert_equal 10, parsed.size - Range.new(0,expect.size-1).to_a.each do |index| - assert_equal expect[index], parsed[index], "Failed for index number #{index}" - end + assert_equal expect, parsed end test "parse text with trailing star" do accept = "text/*" - expect = [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT].sort_by(&:to_s) + expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML, Mime::JSON] parsed = Mime::Type.parse(accept) - assert_equal 9, parsed.size - assert_equal expect, parsed.sort_by(&:to_s) + assert_equal expect, parsed end test "parse application with trailing star" do -- cgit v1.2.3 From 31906eecdf7bffc2203379c5d40f1bb77fb35858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 28 Nov 2010 12:48:50 +0100 Subject: Fix tests on 1.9.2. --- actionpack/lib/action_dispatch/http/mime_type.rb | 15 +++++++++------ actionpack/test/controller/mime_responds_test.rb | 16 +++++++++------- actionpack/test/dispatch/mime_type_test.rb | 9 ++++----- actionpack/test/lib/controller/fake_models.rb | 2 +- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 3e3478a44e..c1503c7eeb 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -190,12 +190,15 @@ module Mime # # Usage: # - # Mime::Type.unregister("text/x-mobile", :mobile) - def unregister(string, symbol) - EXTENSION_LOOKUP.delete(symbol.to_s) - LOOKUP.delete(string) - symbol = symbol.to_s.upcase.intern - Mime.module_eval { remove_const(symbol) if const_defined?(symbol) } + # Mime::Type.unregister(:mobile) + def unregister(symbol) + symbol = symbol.to_s.upcase + mime = Mime.const_get(symbol) + Mime.instance_eval { remove_const(symbol) } + + SET.delete_if { |v| v.eql?(mime) } + LOOKUP.delete_if { |k,v| v.eql?(mime) } + EXTENSION_LOOKUP.delete_if { |k,v| v.eql?(mime) } end end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 82969b2979..98c9d43b93 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -201,8 +201,8 @@ class RespondToControllerTest < ActionController::TestCase def teardown super - Mime::Type.unregister('text/x-mobile', :iphone) - Mime::Type.unregister('text/iphone', :mobile) + Mime::Type.unregister(:iphone) + Mime::Type.unregister(:mobile) end def test_html @@ -622,12 +622,14 @@ class RespondWithControllerTest < ActionController::TestCase def setup super @request.host = "www.example.com" + Mime::Type.register_alias('text/html', :iphone) + Mime::Type.register('text/x-mobile', :mobile) end def teardown super - Mime::Type.unregister('text/x-mobile', :iphone) - Mime::Type.unregister('text/iphone', :mobile) + Mime::Type.unregister(:iphone) + Mime::Type.unregister(:mobile) end def test_using_resource @@ -929,7 +931,8 @@ class RespondWithControllerTest < ActionController::TestCase @controller = RenderJsonRespondWithController.new @request.accept = "application/json" get :index, :format => :json - assert_equal %Q{{"message":"boom","error":"RenderJsonTestException"}}, @response.body + assert_match(/"message":"boom"/, @response.body) + assert_match(/"error":"RenderJsonTestException"/, @response.body) end def test_no_double_render_is_raised @@ -1020,8 +1023,7 @@ class MimeControllerLayoutsTest < ActionController::TestCase def teardown super - Mime::Type.unregister('text/x-mobile', :iphone) - Mime::Type.unregister('text/iphone', :mobile) + Mime::Type.unregister(:iphone) end def test_missing_layout_renders_properly diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index e80d339472..9782f328fc 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -19,7 +19,7 @@ class MimeTypeTest < ActiveSupport::TestCase assert_equal Mime::MOBILE, Mime::LOOKUP['text/x-mobile'] assert_equal Mime::MOBILE, Mime::EXTENSION_LOOKUP['mobile'] - Mime::Type.unregister("text/x-mobile", :mobile) + Mime::Type.unregister(:mobile) assert !defined?(Mime::MOBILE), "Mime::MOBILE should not be defined" assert !Mime::LOOKUP.has_key?('text/x-mobile'), "Mime::LOOKUP should not have key ['text/x-mobile]" assert !Mime::EXTENSION_LOOKUP.has_key?('mobile'), "Mime::EXTENSION_LOOKUP should not have key ['mobile]" @@ -52,10 +52,9 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse application with trailing star" do accept = "application/*" - expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::PDF, Mime::URL_ENCODED_FORM].sort_by(&:to_s) + expect = [Mime::HTML, Mime::JS, Mime::XML, Mime::RSS, Mime::ATOM, Mime::YAML, Mime::URL_ENCODED_FORM, Mime::JSON, Mime::PDF] parsed = Mime::Type.parse(accept) - assert_equal 9, parsed.size - assert_equal expect, parsed.sort_by(&:to_s) + assert_equal expect, parsed end test "parse without q" do @@ -92,7 +91,7 @@ class MimeTypeTest < ActiveSupport::TestCase assert_equal Mime::GIF, Mime::SET.last end ensure - Mime::Type.unregister('image/gif', :gif) + Mime::Type.unregister(:gif) end end diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index bd18cdc1b8..67baf369e2 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -197,6 +197,6 @@ end class RenderJsonTestException < Exception def to_json(options = nil) - return { :error => self.class.name, :message => self.to_str }.to_json + return { :error => self.class.name, :message => self.to_s }.to_json end end -- cgit v1.2.3 From e444439fe28f873c783a4b99b1c9f29a7405dd0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 28 Nov 2010 16:55:48 +0100 Subject: Partialy revert f1c13b0dd7b22b5f6289ca1a09f1d7a8c7c8584b --- .../associations/association_collection.rb | 22 +++++++++++----------- .../associations/association_proxy.rb | 2 +- .../has_and_belongs_to_many_association.rb | 4 ++-- .../associations/has_many_through_association.rb | 4 ++-- .../associations/has_one_association.rb | 2 +- .../associations/has_one_through_association.rb | 2 +- .../lib/active_record/autosave_association.rb | 14 +++++++------- activerecord/lib/active_record/base.rb | 10 +++++----- activerecord/lib/active_record/persistence.rb | 10 +++++----- activerecord/lib/active_record/session_store.rb | 8 ++++---- activerecord/lib/active_record/transactions.rb | 15 ++++++++------- activerecord/lib/active_record/validations.rb | 2 +- 12 files changed, 48 insertions(+), 47 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index ba9373ba6a..abb17a11c6 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -121,13 +121,13 @@ module ActiveRecord # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically. def <<(*records) result = true - load_target unless @owner.persisted? + load_target if @owner.new_record? transaction do flatten_deeper(records).each do |record| raise_on_type_mismatch(record) add_record_to_target_with_callbacks(record) do |r| - result &&= insert_record(record) if @owner.persisted? + result &&= insert_record(record) unless @owner.new_record? end end end @@ -286,12 +286,12 @@ module ActiveRecord # This method is abstract in the sense that it relies on # +count_records+, which is a method descendants have to provide. def size - if !@owner.persisted? || (loaded? && !@reflection.options[:uniq]) + if @owner.new_record? || (loaded? && !@reflection.options[:uniq]) @target.size elsif !loaded? && @reflection.options[:group] load_target.size elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array) - unsaved_records = @target.reject { |r| r.persisted? } + unsaved_records = @target.select { |r| r.new_record? } unsaved_records.size + count_records else count_records @@ -355,7 +355,7 @@ module ActiveRecord def include?(record) return false unless record.is_a?(@reflection.klass) - return include_in_memory?(record) unless record.persisted? + return include_in_memory?(record) if record.new_record? load_target if @reflection.options[:finder_sql] && !loaded? loaded? ? @target.include?(record) : exists?(record) end @@ -369,7 +369,7 @@ module ActiveRecord end def load_target - if @owner.persisted? || foreign_key_present + if !@owner.new_record? || foreign_key_present begin unless loaded? if @target.is_a?(Array) && @target.any? @@ -478,7 +478,7 @@ module ActiveRecord private def create_record(attrs) attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash) - ensure_owner_is_not_new + ensure_owner_is_persisted! scoped_where = scoped.where_values_hash create_scope = scoped_where ? @scope[:create].merge(scoped_where) : @scope[:create] @@ -508,7 +508,7 @@ module ActiveRecord transaction do records.each { |record| callback(:before_remove, record) } - old_records = records.select { |r| r.persisted? } + old_records = records.reject { |r| r.new_record? } yield(records, old_records) records.each { |record| callback(:after_remove, record) } end @@ -532,15 +532,15 @@ module ActiveRecord @owner.class.send(full_callback_name.to_sym) || [] end - def ensure_owner_is_not_new + def ensure_owner_is_persisted! unless @owner.persisted? raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" end end def fetch_first_or_last_using_find?(args) - (args.first.kind_of?(Hash) && !args.first.empty?) || !(loaded? || !@owner.persisted? || @reflection.options[:finder_sql] || - !@target.all? { |record| record.persisted? } || args.first.kind_of?(Integer)) + (args.first.kind_of?(Hash) && !args.first.empty?) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] || + @target.any? { |record| record.new_record? } || args.first.kind_of?(Integer)) end def include_in_memory?(record) diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 7cd04a1ad5..252ff7e7ea 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -252,7 +252,7 @@ module ActiveRecord def load_target return nil unless defined?(@loaded) - if !loaded? and (@owner.persisted? || foreign_key_present) + if !loaded? && (!@owner.new_record? || foreign_key_present) @target = find_target end diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 2c72fd0004..f6f291a039 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -34,7 +34,7 @@ module ActiveRecord end def insert_record(record, force = true, validate = true) - unless record.persisted? + if record.new_record? if force record.save! else @@ -113,7 +113,7 @@ module ActiveRecord private def create_record(attributes, &block) # Can't use Base.create because the foreign key may be a protected attribute. - ensure_owner_is_not_new + ensure_owner_is_persisted! if attributes.is_a?(Array) attributes.collect { |attr| create(attr) } else diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 79c229d9c4..d0c8af1801 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -36,7 +36,7 @@ module ActiveRecord protected def create_record(attrs, force = true) - ensure_owner_is_not_new + ensure_owner_is_persisted! transaction do object = @reflection.klass.new(attrs) @@ -59,7 +59,7 @@ module ActiveRecord end def insert_record(record, force = true, validate = true) - unless record.persisted? + if record.new_record? if force record.save! else diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index e6e037441f..c49fd6e66a 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -56,7 +56,7 @@ module ActiveRecord set_inverse_instance(obj, @owner) @loaded = true - unless !@owner.persisted? or obj.nil? or dont_save + unless !@owner.persisted? || obj.nil? || dont_save return (obj.save ? self : false) else return (obj.nil? ? nil : self) diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb index 6e98f7dffb..7f28abf464 100644 --- a/activerecord/lib/active_record/associations/has_one_through_association.rb +++ b/activerecord/lib/active_record/associations/has_one_through_association.rb @@ -21,7 +21,7 @@ module ActiveRecord if current_object new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy elsif new_value - unless @owner.persisted? + if @owner.new_record? self.target = new_value through_association = @owner.send(:association_instance_get, @reflection.through_reflection.name) through_association.build(construct_join_attributes(new_value)) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 73ac8e82c6..c3dda29d03 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -217,7 +217,7 @@ module ActiveRecord # Returns whether or not this record has been changed in any way (including whether # any of its nested autosave associations are likewise changed) def changed_for_autosave? - !persisted? || changed? || marked_for_destruction? || nested_records_changed_for_autosave? + new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave? end private @@ -231,7 +231,7 @@ module ActiveRecord elsif autosave association.target.find_all { |record| record.changed_for_autosave? } else - association.target.find_all { |record| !record.persisted? } + association.target.find_all { |record| record.new_record? } end end @@ -257,7 +257,7 @@ module ActiveRecord # +reflection+. def validate_collection_association(reflection) if association = association_instance_get(reflection.name) - if records = associated_records_to_validate_or_save(association, !persisted?, reflection.options[:autosave]) + if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave]) records.each { |record| association_valid?(reflection, record) } end end @@ -286,7 +286,7 @@ module ActiveRecord # 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 = !persisted? + @new_record_before_save = new_record? true end @@ -308,7 +308,7 @@ module ActiveRecord if autosave && record.marked_for_destruction? association.destroy(record) - elsif autosave != false && (@new_record_before_save || !record.persisted?) + elsif autosave != false && (@new_record_before_save || record.new_record?) if autosave saved = association.send(:insert_record, record, false, false) else @@ -343,7 +343,7 @@ module ActiveRecord association.destroy else key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id - if autosave != false && (!persisted? || !association.persisted? || association[reflection.primary_key_name] != key || autosave) + if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave) association[reflection.primary_key_name] = key saved = association.save(:validate => !autosave) raise ActiveRecord::Rollback if !saved && autosave @@ -363,7 +363,7 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy elsif autosave != false - saved = association.save(:validate => !autosave) if !association.persisted? || autosave + saved = association.save(:validate => !autosave) if association.new_record? || autosave if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9b09b14c87..6076aaf4c3 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1365,7 +1365,7 @@ MSG def initialize(attributes = nil) @attributes = attributes_from_column_definition @attributes_cache = {} - @persisted = false + @new_record = true @readonly = false @destroyed = false @marked_for_destruction = false @@ -1396,7 +1396,7 @@ MSG @attributes = coder['attributes'] @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {} @readonly = @destroyed = @marked_for_destruction = false - @persisted = true + @new_record = false _run_find_callbacks _run_initialize_callbacks end @@ -1437,7 +1437,7 @@ MSG # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available) def cache_key case - when !persisted? + when new_record? "#{self.class.model_name.cache_key}/new" when timestamp = self[:updated_at] "#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}" @@ -1622,7 +1622,7 @@ MSG clear_aggregation_cache clear_association_cache @attributes_cache = {} - @persisted = false + @new_record = true ensure_proper_type populate_with_current_scope_attributes @@ -1643,7 +1643,7 @@ MSG # Returns the contents of the record as a nicely formatted string. def inspect attributes_as_nice_string = self.class.column_names.collect { |name| - if has_attribute?(name) || !persisted? + if has_attribute?(name) || new_record? "#{name}: #{attribute_for_inspect(name)}" end }.compact.join(", ") diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 594a2214bb..75dba0206d 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -4,7 +4,7 @@ module ActiveRecord # Returns true if this object hasn't been saved yet -- that is, a record # for the object doesn't exist in the data store yet; otherwise, returns false. def new_record? - !@persisted + @new_record end # Returns true if this object has been destroyed, otherwise returns false. @@ -15,7 +15,7 @@ module ActiveRecord # Returns if the record is persisted, i.e. it's not a new record and it was # not destroyed. def persisted? - @persisted && !destroyed? + !(new_record? || destroyed?) end # Saves the model. @@ -94,7 +94,7 @@ module ActiveRecord became = klass.new became.instance_variable_set("@attributes", @attributes) became.instance_variable_set("@attributes_cache", @attributes_cache) - became.instance_variable_set("@persisted", persisted?) + became.instance_variable_set("@new_record", new_record?) became.instance_variable_set("@destroyed", destroyed?) became.type = klass.name unless self.class.descends_from_active_record? became @@ -241,7 +241,7 @@ module ActiveRecord private def create_or_update raise ReadOnlyRecord if readonly? - result = persisted? ? update : create + result = new_record? ? create : update result != false end @@ -270,7 +270,7 @@ module ActiveRecord self.id ||= new_id - @persisted = true + @new_record = false id end diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index ba99800fb2..3400fd6ade 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -228,7 +228,7 @@ module ActiveRecord @session_id = attributes[:session_id] @data = attributes[:data] @marshaled_data = attributes[:marshaled_data] - @persisted = !@marshaled_data.nil? + @new_record = @marshaled_data.nil? end # Lazy-unmarshal session state. @@ -252,8 +252,8 @@ module ActiveRecord marshaled_data = self.class.marshal(data) connect = connection - unless @persisted - @persisted = true + if @new_record + @new_record = false connect.update <<-end_sql, 'Create session' INSERT INTO #{table_name} ( #{connect.quote_column_name(session_id_column)}, @@ -272,7 +272,7 @@ module ActiveRecord end def destroy - return unless @persisted + return if @new_record connect = connection connect.delete <<-end_sql, 'Destroy session' diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 8c94d1a2bc..654c475aed 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -11,6 +11,7 @@ module ActiveRecord included do define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name] end + # = Active Record Transactions # # Transactions are protective blocks where SQL statements are only permanent @@ -242,7 +243,7 @@ module ActiveRecord with_transaction_returning_status { super } end - # Reset id and @persisted if the transaction rolls back. + # Reset id and @new_record if the transaction rolls back. def rollback_active_record_state! remember_transaction_record_state yield @@ -297,9 +298,9 @@ module ActiveRecord # Save the new record state and id of a record so it can be restored later if a transaction fails. def remember_transaction_record_state #:nodoc @_start_transaction_state ||= {} - unless @_start_transaction_state.include?(:persisted) + unless @_start_transaction_state.include?(:new_record) @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key) - @_start_transaction_state[:persisted] = @persisted + @_start_transaction_state[:new_record] = @new_record end unless @_start_transaction_state.include?(:destroyed) @_start_transaction_state[:destroyed] = @destroyed @@ -323,8 +324,8 @@ module ActiveRecord restore_state = remove_instance_variable(:@_start_transaction_state) if restore_state @attributes = @attributes.dup if @attributes.frozen? - @persisted = restore_state[:persisted] - @destroyed = restore_state[:destroyed] + @new_record = restore_state[:new_record] + @destroyed = restore_state[:destroyed] if restore_state[:id] self.id = restore_state[:id] else @@ -345,11 +346,11 @@ module ActiveRecord def transaction_include_action?(action) #:nodoc case action when :create - transaction_record_state(:new_record) || !transaction_record_state(:persisted) + transaction_record_state(:new_record) when :destroy destroyed? when :update - !(transaction_record_state(:new_record) || !transaction_record_state(:persisted) || destroyed?) + !(transaction_record_state(:new_record) || destroyed?) end end end diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index ee45fcdf35..f367315b22 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -51,7 +51,7 @@ module ActiveRecord # Runs all the specified validations and returns true if no errors were added otherwise false. def valid?(context = nil) - context ||= (persisted? ? :update : :create) + context ||= (new_record? ? :create : :update) output = super(context) errors.empty? && output end -- cgit v1.2.3 From fbdd72ecae9192c461794e37c76a663112dcaa6e Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sat, 27 Nov 2010 20:47:35 +1100 Subject: Mention filter_parameters for config --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index ca78cc0e6d..0ea7a7bb47 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -63,6 +63,8 @@ h4. Rails General Configuration * +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+. +* +config.filter_parameters+ used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers. + * +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+. * +config.log_path+ overrides the path to the log file to use. Defaults to +log/#{environment}.log+ (e.g. log/development.log or log/production.log). -- cgit v1.2.3 From dc4dcb155589375a22c4fafea63f8bff85dd7044 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sat, 27 Nov 2010 20:52:28 +1100 Subject: Add documentation for secret_token to config guide --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 0ea7a7bb47..4677d2b983 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -81,6 +81,8 @@ h4. Rails General Configuration * +config.root+ configures the root path of the application. +* +config.secret_token+ used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. + * +config.serve_static_assets+ configures Rails to serve static assets. Defaults to _true_, but in the production environment is turned off. The server software used to run the application should be used to serve the assets instead. * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. -- cgit v1.2.3 From 70abf788001ed9fa83d3e3e3f2e8520df1ab8b46 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 11:07:36 +1100 Subject: allow_concurrency is a "global" configuration option in Rails 3 --- railties/guides/source/configuring.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 4677d2b983..6fd9cca68c 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -47,6 +47,8 @@ h4. Rails General Configuration end
+* +config.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Can also be enabled with +threadsafe!+. + * +config.app_generators+ alternate name for +config.generators+. See the "Configuring Generators" section below for how to use this. * +config.autoload_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +autoload_paths+. @@ -208,8 +210,6 @@ h4. Configuring Action Controller * +config.action_controller.consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ to specify which requests should provide debugging information on errors. -* +config.action_controller.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Instead, you should simply call +config.threadsafe!+ inside your +production.rb+ file, which makes all the necessary adjustments. - WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. * +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8". -- cgit v1.2.3 From d7ac3992d53e48c753b00c4e54ce24f890ca7974 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 11:07:55 +1100 Subject: Mention threadsafe! method in config guide --- railties/guides/source/configuring.textile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 6fd9cca68c..b87bd429de 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -55,13 +55,13 @@ h4. Rails General Configuration * +config.autoload_paths+ accepts an array of additional paths to prepend to the load path. By default, all app, lib, vendor and mock paths are included in this list. -* +config.cache_classes+ controls whether or not application classes should be reloaded on each request. Defaults to _true_ in development, _false_ in test and production. +* +config.cache_classes+ controls whether or not application classes should be reloaded on each request. Defaults to _true_ in development, _false_ in test and production. Can also be enabled with +threadsafe!+. * +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class. * +config.colorize_logging+ (true by default) specifies whether or not to use ANSI color codes when logging information. -* +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior. +* +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior. Can also be enabled with +threadsafe!+. * +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+. @@ -77,7 +77,7 @@ h4. Rails General Configuration * +config.plugins+ accepts the list of plugins to load. If this is set to nil, all plugins will be loaded. If this is set to [], no plugins will be loaded. Otherwise, plugins will be loaded in the order specified. -* +config.preload_frameworks+ enables or disables preloading all frameworks at startup. +* +config.preload_frameworks+ enables or disables preloading all frameworks at startup. Can also be enabled with +threadsafe!+. * +config.reload_plugins+ enables or disables plugin reloading. @@ -87,6 +87,8 @@ h4. Rails General Configuration * +config.serve_static_assets+ configures Rails to serve static assets. Defaults to _true_, but in the production environment is turned off. The server software used to run the application should be used to serve the assets instead. +* +config.threadsafe!+ enables +allow_concurrency+, +cache_classes+, +dependency_loading+ and +preload_frameworks+ to make the application threadsafe. + * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. * +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_. -- cgit v1.2.3 From f03aa05cee96f0a0e09af0e9244a9cf9fcd1cf6f Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 11:46:15 +1100 Subject: Change coloriz_logging description to follow the standard for this guide --- railties/guides/source/configuring.textile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index b87bd429de..214674ef36 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -59,7 +59,8 @@ h4. Rails General Configuration * +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class. -* +config.colorize_logging+ (true by default) specifies whether or not to use ANSI color codes when logging information. +* +config.colorize_logging+ specifies whether or not to use ANSI color codes when logging information. Defaults to _true_. + * +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior. Can also be enabled with +threadsafe!+. -- cgit v1.2.3 From b62d5d62e93ee0ad7b4500e4c351f98d9c685abd Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 11:47:06 +1100 Subject: Move consider_all_requests_local to global config methods in config guide --- railties/guides/source/configuring.textile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 214674ef36..e206d1d16b 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -61,6 +61,7 @@ h4. Rails General Configuration * +config.colorize_logging+ specifies whether or not to use ANSI color codes when logging information. Defaults to _true_. +* +config.consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ in controllers to specify which requests should provide debugging information on errors. * +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior. Can also be enabled with +threadsafe!+. @@ -211,8 +212,6 @@ h4. Configuring Action Controller * +config.action_controller.asset_path+ allows you to override the default asset path generation by providing your own instructions. -* +config.action_controller.consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ to specify which requests should provide debugging information on errors. - WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. * +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8". -- cgit v1.2.3 From 0d32572275f066099e472f37ca2e6262e8c07bf5 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 11:54:48 +1100 Subject: Move threadsafe warning to threadsafe's new location in config guide --- railties/guides/source/configuring.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index e206d1d16b..6272d81d7f 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -91,6 +91,8 @@ h4. Rails General Configuration * +config.threadsafe!+ enables +allow_concurrency+, +cache_classes+, +dependency_loading+ and +preload_frameworks+ to make the application threadsafe. +WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. + * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. * +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_. @@ -212,8 +214,6 @@ h4. Configuring Action Controller * +config.action_controller.asset_path+ allows you to override the default asset path generation by providing your own instructions. -WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. - * +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8". * +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -- cgit v1.2.3 From 945d714e75701890bf68b70c08d0e4836a1b3ffb Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 12:05:23 +1100 Subject: Mention controller_paths in the config guide --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 6272d81d7f..891c4f1c4d 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -63,6 +63,8 @@ h4. Rails General Configuration * +config.consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ in controllers to specify which requests should provide debugging information on errors. +* +config.controller_paths+ configures where Rails can find controllers for this application. + * +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior. Can also be enabled with +threadsafe!+. * +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+. -- cgit v1.2.3 From e15b8fa0901a5efb6ca1223d7331233b6e3af1f6 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 12:18:47 +1100 Subject: No more app_generators method in 3.1 --- railties/guides/source/configuring.textile | 2 -- 1 file changed, 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 891c4f1c4d..a610f49282 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -49,8 +49,6 @@ h4. Rails General Configuration * +config.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Can also be enabled with +threadsafe!+. -* +config.app_generators+ alternate name for +config.generators+. See the "Configuring Generators" section below for how to use this. - * +config.autoload_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +autoload_paths+. * +config.autoload_paths+ accepts an array of additional paths to prepend to the load path. By default, all app, lib, vendor and mock paths are included in this list. -- cgit v1.2.3 From 549c13466072d5c33fe808d25adfa06bd5feb6c8 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 12:24:41 +1100 Subject: Add asset_host and asset_path to the configuring guide --- railties/guides/source/configuring.textile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index a610f49282..5ae4a53973 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -49,6 +49,14 @@ h4. Rails General Configuration * +config.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Can also be enabled with +threadsafe!+. +* +config.asset_host+ sets the host for the assets. Useful when CDNs are used for hosting assets rather than the application server itself. + +* +config.asset_path+ takes a block which configures where assets can be found. + + + config.asset_path = proc { |asset_path| "assets/#{asset_path}" } + + * +config.autoload_once_paths+ accepts an array of paths from which Rails will automatically load from only once. All elements of this array must also be in +autoload_paths+. * +config.autoload_paths+ accepts an array of additional paths to prepend to the load path. By default, all app, lib, vendor and mock paths are included in this list. -- cgit v1.2.3 From b89ce3e67961e6e090403f99b0b696f252da14d4 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 13:10:51 +1100 Subject: Mention encoding in config guide --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 5ae4a53973..40d09f8941 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -75,6 +75,8 @@ h4. Rails General Configuration * +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+. +* +config.encoding+ sets up the application-wide encoding. Defaults to UTF-8. + * +config.filter_parameters+ used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers. * +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+. -- cgit v1.2.3 From 245ce774bf4e428bb608d0d880f711c3c4153821 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 13:11:27 +1100 Subject: Add mention of helper_paths to config guide --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 40d09f8941..05afc4f7ea 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -79,6 +79,8 @@ h4. Rails General Configuration * +config.filter_parameters+ used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers. +* +config.helper_paths+ configures where Rails can find helpers for this application. + * +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+. * +config.log_path+ overrides the path to the log file to use. Defaults to +log/#{environment}.log+ (e.g. log/development.log or log/production.log). -- cgit v1.2.3 From b9fde2fb757ffdc19bfdf170f20bdfbd10b487f9 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 13:11:53 +1100 Subject: Reorder logger and middleware global config options --- railties/guides/source/configuring.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 05afc4f7ea..9c3e0c38a8 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -85,10 +85,10 @@ h4. Rails General Configuration * +config.log_path+ overrides the path to the log file to use. Defaults to +log/#{environment}.log+ (e.g. log/development.log or log/production.log). -* +config.middleware+ allows you to configure the application's middleware. This is covered in depth in the "Configuring Middleware" section below. - * +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. +* +config.middleware+ allows you to configure the application's middleware. This is covered in depth in the "Configuring Middleware" section below. + * +config.plugins+ accepts the list of plugins to load. If this is set to nil, all plugins will be loaded. If this is set to [], no plugins will be loaded. Otherwise, plugins will be loaded in the order specified. * +config.preload_frameworks+ enables or disables preloading all frameworks at startup. Can also be enabled with +threadsafe!+. -- cgit v1.2.3 From 685353afc7ddfd58ce99249763a2c08d44d4df5c Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 13:30:11 +1100 Subject: asset_host and asset_path are now set at the global level --- railties/guides/source/configuring.textile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 9c3e0c38a8..38226e750a 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -222,10 +222,6 @@ h4. Configuring Action Controller config.action_controller includes a number of configuration settings: -* +config.action_controller.asset_host+ provides a string that is prepended to all of the URL-generating helpers in +AssetHelper+. This is designed to allow moving all javascript, CSS, and image files to a separate asset host. - -* +config.action_controller.asset_path+ allows you to override the default asset path generation by providing your own instructions. - * +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8". * +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -- cgit v1.2.3 From 390de62cacb6b3c89916b1146cdf204a5bab0703 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 14:10:36 +1100 Subject: Fix documentation regarding the initialization events of the Rails stack --- railties/guides/source/configuring.textile | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 38226e750a..3a49d42aaf 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -316,25 +316,22 @@ There are a few configuration options available in Active Support: * +ActiveSupport::Logger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+. -h3. Using Initializers +h3. Initialization events -After loading the framework and any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of Ruby code stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded. - -NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the +initializers+ folder on down. +Rails has 4 initialization events which can be hooked into (listed in order that they are ran): -TIP: If you have any ordering dependency in your initializers, you can control the load order by naming. For example, +01_critical.rb+ will be loaded before +02_normal.rb+. +* +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens. +* +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ enviroment. +* +before_initialize+ This is run directly before the initialization process of the application occurs. +* +after_initialize+ Run directly after the initialization of the application, but before the application initializers are run. -h3. Using an After-Initializer +WARNING: Some parts of your application, notably observers and routing, are not yet set up at the point where the +after_initialize+ block is called. -After-initializers are run (as you might guess) after any initializers are loaded. You can supply an +after_initialize+ block (or an array of such blocks) by setting up +config.after_initialize+ in any of the Rails configuration files: +After loading the framework and any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of Ruby code stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded. - -config.after_initialize do - SomeClass.init -end - +NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the +initializers+ folder on down. -WARNING: Some parts of your application, notably observers and routing, are not yet set up at the point where the +after_initialize+ block is called. +TIP: If you have any ordering dependency in your initializers, you can control the load order by naming. For example, +01_critical.rb+ will be loaded before +02_normal.rb+. h3. Rails Environment Settings -- cgit v1.2.3 From 964c2025ff7b4d22902ed8ee86c3e2858a4cf3ba Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 15:08:35 +1100 Subject: Use ARel for Range Conditions section, remove Date & Time section because users should *never* do that. --- .../guides/source/active_record_querying.textile | 60 +++------------------- 1 file changed, 7 insertions(+), 53 deletions(-) diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index 328439fdb8..0447013e24 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -65,6 +65,7 @@ The methods are: * +lock+ * +readonly+ * +from+ +* +having+ All of the above methods return an instance of ActiveRecord::Relation. @@ -103,7 +104,7 @@ h5. +first+ client = Client.first -=> # "Lifo"> +=> # SQL equivalent of the above is: @@ -120,7 +121,7 @@ h5. +last+ client = Client.last -=> # "Russel"> +=> # SQL equivalent of the above is: @@ -231,7 +232,7 @@ WARNING: Building your own conditions as pure strings can leave you vulnerable t h4. Array Conditions -Now what if that number could vary, say as an argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like: +Now what if that number could vary, say as an argument from somewhere? The find then becomes something like: Client.where("orders_count = ?", params[:orders]) @@ -279,62 +280,15 @@ h5(#array-range_conditions). Range Conditions If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the +IN+ SQL statement for this. If you had two dates coming in from a controller you could do something like this to look for a range: -Client.where("created_at IN (?)", - (params[:start_date].to_date)..(params[:end_date].to_date)) +Client.where(:created_at => (params[:start_date].to_date)..(params[:end_date].to_date)) -This would generate the proper query which is great for small ranges but not so good for larger ranges. For example if you pass in a range of date objects spanning a year that's 365 (or possibly 366, depending on the year) strings it will attempt to match your field against. +This query will generate something similar to the following SQL: -SELECT * FROM users WHERE (created_at IN - ('2007-12-31','2008-01-01','2008-01-02','2008-01-03','2008-01-04','2008-01-05', - '2008-01-06','2008-01-07','2008-01-08','2008-01-09','2008-01-10','2008-01-11', - '2008-01-12','2008-01-13','2008-01-14','2008-01-15','2008-01-16','2008-01-17', - '2008-01-18','2008-01-19','2008-01-20','2008-01-21','2008-01-22','2008-01-23',... - ‘2008-12-15','2008-12-16','2008-12-17','2008-12-18','2008-12-19','2008-12-20', - '2008-12-21','2008-12-22','2008-12-23','2008-12-24','2008-12-25','2008-12-26', - '2008-12-27','2008-12-28','2008-12-29','2008-12-30','2008-12-31')) + SELECT "clients".* FROM "clients" WHERE ("clients"."created_at" BETWEEN '2010-09-29' AND '2010-11-30') -h5. Time and Date Conditions - -Things can get *really* messy if you pass in Time objects as it will attempt to compare your field to *every second* in that range: - - -Client.where("created_at IN (?)", - (params[:start_date].to_date.to_time)..(params[:end_date].to_date.to_time)) - - - -SELECT * FROM users WHERE (created_at IN - ('2007-12-01 00:00:00', '2007-12-01 00:00:01' ... - '2007-12-01 23:59:59', '2007-12-02 00:00:00')) - - -This could possibly cause your database server to raise an unexpected error, for example MySQL will throw back this error: - - -Got a packet bigger than 'max_allowed_packet' bytes: _query_ - - -Where _query_ is the actual query used to get that error. - -In this example it would be better to use greater-than and less-than operators in SQL, like so: - - -Client.where( - "created_at > ? AND created_at < ?", params[:start_date], params[:end_date]) - - -You can also use the greater-than-or-equal-to and less-than-or-equal-to like this: - - -Client.where( - "created_at >= ? AND created_at <= ?", params[:start_date], params[:end_date]) - - -Just like in Ruby. If you want a shorter syntax be sure to check out the "Hash Conditions":#hash-conditions section later on in the guide. - h4. Hash Conditions Active Record also allows you to pass in hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them: -- cgit v1.2.3 From 9e655e8571345701b23a8cdd4d480ecf3a1623d3 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 15:12:39 +1100 Subject: Bring order description in line with actual text. --- railties/guides/source/active_record_querying.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index 0447013e24..e41b5fb606 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -339,7 +339,7 @@ SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5)) h4. Ordering -To retrieve records from the database in a specific order, you can specify the +:order+ option to the +find+ call. +To retrieve records from the database in a specific order, you can use the +order+ method. For example, if you're getting a set of records and want to order them in ascending order by the +created_at+ field in your table: -- cgit v1.2.3 From c4cbf733a1a3fe0ab46cc707d6793e3b2de56f60 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 17:55:12 +1100 Subject: re-add config.action_controller.asset_host and asset_path to config guide --- railties/guides/source/configuring.textile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 3a49d42aaf..47b6742f8b 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -49,9 +49,9 @@ h4. Rails General Configuration * +config.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Can also be enabled with +threadsafe!+. -* +config.asset_host+ sets the host for the assets. Useful when CDNs are used for hosting assets rather than the application server itself. +* +config.asset_host+ sets the host for the assets. Useful when CDNs are used for hosting assets rather than the application server itself. Shorter version of +config.action_controller.asset_host+. -* +config.asset_path+ takes a block which configures where assets can be found. +* +config.asset_path+ takes a block which configures where assets can be found. Shorter version of +config.action_controller.asset_path+. config.asset_path = proc { |asset_path| "assets/#{asset_path}" } @@ -222,6 +222,10 @@ h4. Configuring Action Controller config.action_controller includes a number of configuration settings: +* +config.action_controller.asset_host+ sets the host for the assets. Useful when CDNs are used for hosting assets rather than the application server itself. + +* +config.action_controller.asset_path+ takes a block which configures where assets can be found. Shorter version of +config.action_controller.asset_path+. + * +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8". * +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -- cgit v1.2.3 From 15cfaa0a3634b23477f74dbfd1fafd351933e17c Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 18:27:23 +1100 Subject: Add documentation for page_cache_directory and page_cache_extension to config guide --- railties/guides/source/configuring.textile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 47b6742f8b..ab9dd220bd 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -226,6 +226,10 @@ h4. Configuring Action Controller * +config.action_controller.asset_path+ takes a block which configures where assets can be found. Shorter version of +config.action_controller.asset_path+. +* +config.action_controller.page_cache_directory+ should be the document root for the web server and is set using Base.page_cache_directory = "/document/root". For Rails, this directory has already been set to Rails.public_path (which is usually set to Rails.root + "/public"). Changing this setting can be useful to avoid naming conflicts with files in public/, but doing so will likely require configuring your web server to look in the new location for cached files. + +* +config.action_controller.page_cache_extension+ configures the extension used for cached pages saved to +page_cache_directory+. Defaults to +.html+ + * +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8". * +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -- cgit v1.2.3 From 477d17b31682a7f7f7718ef9fd98e65888c990bc Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 29 Nov 2010 18:30:01 +1100 Subject: Add perform_caching to config guide --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index ab9dd220bd..fcf7ba0ae5 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -230,6 +230,8 @@ h4. Configuring Action Controller * +config.action_controller.page_cache_extension+ configures the extension used for cached pages saved to +page_cache_directory+. Defaults to +.html+ +* +config.action_controller.perform_caching+ configures whether the application should perform caching or not. Set to _false_ in development mode, _true_ in production. + * +config.action_controller.default_charset+ specifies the default character set for all renders. The default is "utf-8". * +config.action_controller.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then used to log information from Action Controller. Set to nil to disable logging. -- cgit v1.2.3 From 28896a9f4ae0830726619bc479f69263acb80e4b Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Sun, 28 Nov 2010 20:07:59 -0800 Subject: test case on has_one :through after a destroy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#6037 state:resolved] Signed-off-by: José Valim --- .../associations/has_one_through_associations_test.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 5d153147f5..ac43e571cb 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -206,6 +206,23 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase end end + def test_through_belongs_to_after_destroy + @member_detail = MemberDetail.new(:extra_data => 'Extra') + @member.member_detail = @member_detail + @member.save! + + assert_not_nil @member_detail.member_type + @member_detail.destroy + assert_queries(1) do + assert_not_nil @member_detail.member_type(true) + end + + @member_detail.member.destroy + assert_queries(1) do + assert_nil @member_detail.member_type(true) + end + end + def test_value_is_properly_quoted minivan = Minivan.find('m1') assert_nothing_raised do -- cgit v1.2.3 From 439a7dc0e62858a5ed1fc397e36626f9be1d7c87 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 29 Nov 2010 10:39:33 -0800 Subject: moving method to subclass that actually uses the method --- .../associations/class_methods/join_dependency/join_association.rb | 4 ++++ .../associations/class_methods/join_dependency/join_part.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 5e5c01c77a..e7d1ffa9f2 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -123,6 +123,10 @@ module ActiveRecord Arel.sql(interpolate_sql(sanitize_sql(conditions, table_name))) end + def sanitize_sql(condition, table_name) + active_record.send(:sanitize_sql, condition, table_name) + end + def join_target_table(relation, *conditions) relation = relation.join(target_table, join_type) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb index 0b093b65e9..cd16ae5a8b 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb @@ -14,7 +14,7 @@ module ActiveRecord # association. attr_reader :active_record - delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record + delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :active_record def initialize(active_record) @active_record = active_record -- cgit v1.2.3 From aae3c3538492f19f1c4e1fcfe6c942ff72a30ce1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 29 Nov 2010 10:58:21 -0800 Subject: removing interpolate_sql from join associations --- .../associations/class_methods/join_dependency/join_association.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index e7d1ffa9f2..3db80d341f 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -102,10 +102,6 @@ module ActiveRecord ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name end - def interpolate_sql(sql) - instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__) - end - private def allocate_aliases @@ -120,7 +116,7 @@ module ActiveRecord end def process_conditions(conditions, table_name) - Arel.sql(interpolate_sql(sanitize_sql(conditions, table_name))) + Arel.sql(sanitize_sql(conditions, table_name)) end def sanitize_sql(condition, table_name) -- cgit v1.2.3 From 9f2e885ce8d5cbf6999b22e274ec245d9007131d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 29 Nov 2010 13:32:02 -0800 Subject: testing attributes applied by default_scope --- activerecord/test/cases/relation_scoping_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index dae9721a63..c7f7738248 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -464,6 +464,11 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary end + def test_default_scope_attribute + jamis = PoorDeveloperCalledJamis.new(:name => 'David') + assert_equal 50000, jamis.salary + end + def test_scope_composed_by_limit_and_then_offset_is_equal_to_scope_composed_by_offset_and_then_limit posts_limit_offset = Post.limit(3).offset(2) posts_offset_limit = Post.offset(2).limit(3) -- cgit v1.2.3 From 76a15dd0599779f86bda063bb545bba68c6ff17b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 29 Nov 2010 16:41:02 -0800 Subject: adding more tests surrounding where values hash --- activerecord/test/cases/relation_scoping_test.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index c7f7738248..1678e631e5 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -469,6 +469,17 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal 50000, jamis.salary end + def test_where_attribute + aaron = PoorDeveloperCalledJamis.where(:salary => 20).new(:name => 'Aaron') + assert_equal 20, aaron.salary + assert_equal 'Aaron', aaron.name + end + + def test_where_attribute_merge + aaron = PoorDeveloperCalledJamis.where(:name => 'foo').new(:name => 'Aaron') + assert_equal 'Aaron', aaron.name + end + def test_scope_composed_by_limit_and_then_offset_is_equal_to_scope_composed_by_offset_and_then_limit posts_limit_offset = Post.limit(3).offset(2) posts_offset_limit = Post.offset(2).limit(3) -- cgit v1.2.3 From aa9a3227a0151af87e1eb5a6c4b7b0b095fc33c9 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 29 Nov 2010 21:38:37 -0500 Subject: Rewording existing comment --- actionpack/lib/abstract_controller/layouts.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index b68c7d9216..606f7eedec 100644 --- a/actionpack/lib/abstract_controller/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -235,13 +235,10 @@ module AbstractController controller_path end - # Takes the specified layout and creates a _layout method to be called - # by _default_layout + # Creates a _layout method to be called by _default_layout . # - # If there is no explicit layout specified: - # If a layout is found in the view paths with the controller's - # name, return that string. Otherwise, use the superclass' - # layout (which might also be implied) + # If a layout is not explicitly mentioned then look for a layout with the controller's name. + # if nothing is found then try same procedure to find super class's layout. def _write_layout_method remove_possible_method(:_layout) -- cgit v1.2.3 From aa1ac1ca8cf9557dae72604a48de1f0e58515b40 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 30 Nov 2010 09:44:12 -0500 Subject: Add comment for config_accessor method --- activesupport/lib/active_support/configurable.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb index 58ed37b018..644db0b205 100644 --- a/activesupport/lib/active_support/configurable.rb +++ b/activesupport/lib/active_support/configurable.rb @@ -38,6 +38,18 @@ module ActiveSupport yield config end + # Allows you to add shortcut so that you don't have to refer to attribute through config. + # Also look at the example for config to contrast. + # + # class User + # include ActiveSupport::Configurable + # config_accessor :allowed_access + # end + # + # user = User.new + # user.allowed_access = true + # user.allowed_access # => true + # def config_accessor(*names) names.each do |name| code, line = <<-RUBY, __LINE__ + 1 -- cgit v1.2.3 From 3c7a33c0510e30a3017b007d8ed8afcae02ef4a6 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 30 Nov 2010 10:00:47 -0500 Subject: Rewording existing comment --- actionpack/lib/abstract_controller/base.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index f83eaded88..c384fd0978 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -31,10 +31,9 @@ module AbstractController # A list of all internal methods for a controller. This finds the first # abstract superclass of a controller, and gets a list of all public # instance methods on that abstract class. Public instance methods of - # a controller would normally be considered action methods, so we - # are removing those methods on classes declared as abstract - # (ActionController::Metal and ActionController::Base are defined - # as abstract) + # a controller would normally be considered action methods, so methods + # declared on abstract classes are being removed. + # (ActionController::Metal and ActionController::Base are defined as abstract) def internal_methods controller = self controller = controller.superclass until controller.abstract? -- cgit v1.2.3 From 0bda6f1ec664fcfd1b312492a6419e3d76d5baa7 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Tue, 30 Nov 2010 16:36:01 +0100 Subject: The redirect routing method now allows for a hash of options which only changes the relevant parts of the url, or an object which responds to call can be supplied so common redirect rules can be easily reused. This commit includes a change where url generation from parts has been moved to AD::Http::URL as a class method. --- actionpack/lib/action_dispatch/http/url.rb | 52 ++ actionpack/lib/action_dispatch/routing/mapper.rb | 39 +- .../lib/action_dispatch/routing/redirection.rb | 81 +++ .../lib/action_dispatch/routing/route_set.rb | 62 +-- actionpack/test/controller/routing_test.rb | 581 +++++++++++---------- actionpack/test/dispatch/request_test.rb | 25 + actionpack/test/dispatch/routing_test.rb | 48 ++ 7 files changed, 525 insertions(+), 363 deletions(-) create mode 100644 actionpack/lib/action_dispatch/routing/redirection.rb diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 1e7054f381..51d4766282 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -24,6 +24,58 @@ module ActionDispatch !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host)) end + def self.url_for(options = {}) + unless options[:host].present? || options[:only_path].present? + raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true' + end + + rewritten_url = "" + + unless options[:only_path] + rewritten_url << (options[:protocol] || "http") + rewritten_url << "://" unless rewritten_url.match("://") + rewritten_url << rewrite_authentication(options) + rewritten_url << host_or_subdomain_and_domain(options) + rewritten_url << ":#{options.delete(:port)}" if options[:port] + end + + path = options.delete(:path) || '' + + params = options[:params] || {} + params.reject! {|k,v| !v } + + rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path) + rewritten_url << "?#{params.to_query}" unless params.empty? + rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor] + rewritten_url + end + + class << self + private + + def rewrite_authentication(options) + if options[:user] && options[:password] + "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@" + else + "" + end + end + + def host_or_subdomain_and_domain(options) + return options[:host] unless options[:subdomain] || options[:domain] + + tld_length = options[:tld_length] || @@tld_length + + host = "" + host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)) + host << "." + host << (options[:domain] || extract_domain(options[:host], tld_length)) + host + end + + end + + # Returns the complete URL used for this request. def url diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 880862c909..05b7147c70 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -2,6 +2,7 @@ require 'erb' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/object/blank' require 'active_support/inflector' +require 'action_dispatch/routing/redirection' module ActionDispatch module Routing @@ -383,39 +384,6 @@ module ActionDispatch map_method(:delete, *args, &block) end - # Redirect any path to another path: - # - # match "/stories" => redirect("/posts") - def redirect(*args) - options = args.last.is_a?(Hash) ? args.pop : {} - - path = args.shift || Proc.new - path_proc = path.is_a?(Proc) ? path : proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) } - status = options[:status] || 301 - - lambda do |env| - req = Request.new(env) - - params = [req.symbolized_path_parameters] - params << req if path_proc.arity > 1 - - uri = URI.parse(path_proc.call(*params)) - uri.scheme ||= req.scheme - uri.host ||= req.host - uri.port ||= req.port unless req.standard_port? - - body = %(You are being redirected.) - - headers = { - 'Location' => uri.to_s, - 'Content-Type' => 'text/html', - 'Content-Length' => body.length.to_s - } - - [ status, headers, [body] ] - end - end - private def map_method(method, *args, &block) options = args.extract_options! @@ -638,7 +606,7 @@ module ActionDispatch :shallow_path => path, :shallow_prefix => path }.merge!(options) scope(options) { yield } end - + # === Parameter Restriction # Allows you to constrain the nested routes based on a set of rules. # For instance, in order to change the routes to allow for a dot character in the +id+ parameter: @@ -649,7 +617,7 @@ module ActionDispatch # # Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be. # The +id+ parameter must match the constraint passed in for this example. - # + # # You may use this to also resrict other parameters: # # resources :posts do @@ -1340,6 +1308,7 @@ module ActionDispatch include Base include HttpHelpers + include Redirection include Scoping include Resources include Shorthand diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb new file mode 100644 index 0000000000..d9c9a400a7 --- /dev/null +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -0,0 +1,81 @@ +require 'action_dispatch/http/request' + +module ActionDispatch + module Routing + module Redirection + + # Redirect any path to another path: + # + # match "/stories" => redirect("/posts") + def redirect(*args, &block) + options = args.last.is_a?(Hash) ? args.pop : {} + status = options.delete(:status) || 301 + + path = args.shift + + path_proc = if path.is_a?(String) + proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) } + elsif options.any? + options_proc(options) + elsif path.respond_to?(:call) + proc { |params, request| path.call(params, request) } + elsif block + block + else + raise ArgumentError, "redirection argument not supported" + end + + redirection_proc(status, path_proc) + end + + private + + def options_proc(options) + proc do |params, request| + path = if options[:path].nil? + request.path + elsif params.empty? || !options[:path].match(/%\{\w*\}/) + options.delete(:path) + else + (options.delete(:path) % params) + end + + default_options = { + :protocol => request.protocol, + :host => request.host, + :port => request.optional_port, + :path => path, + :params => request.query_parameters + } + + ActionDispatch::Http::URL.url_for(options.reverse_merge(default_options)) + end + end + + def redirection_proc(status, path_proc) + lambda do |env| + req = Request.new(env) + + params = [req.symbolized_path_parameters] + params << req if path_proc.arity > 1 + + uri = URI.parse(path_proc.call(*params)) + uri.scheme ||= req.scheme + uri.host ||= req.host + uri.port ||= req.port unless req.standard_port? + + body = %(You are being redirected.) + + headers = { + 'Location' => uri.to_s, + 'Content-Type' => 'text/html', + 'Content-Length' => body.length.to_s + } + + [ status, headers, [body] ] + end + end + + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index ebced9cabe..03bfe178e5 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -442,12 +442,9 @@ module ActionDispatch raise_routing_error unless path - params.reject! {|k,v| !v } - return [path, params.keys] if @extras - path << "?#{params.to_query}" unless params.empty? - path + [path, params] rescue Rack::Mount::RoutingError raise_routing_error end @@ -486,7 +483,7 @@ module ActionDispatch end RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length, - :trailing_slash, :script_name, :anchor, :params, :only_path ] + :trailing_slash, :anchor, :params, :only_path, :script_name] def _generate_prefix(options = {}) nil @@ -498,29 +495,24 @@ module ActionDispatch handle_positional_args(options) - rewritten_url = "" - - path_segments = options.delete(:_path_segments) - unless options[:only_path] - rewritten_url << (options[:protocol] || "http") - rewritten_url << "://" unless rewritten_url.match("://") - rewritten_url << rewrite_authentication(options) - rewritten_url << host_from_options(options) - rewritten_url << ":#{options.delete(:port)}" if options[:port] - end + user, password = extract_authentication(options) + path_segments = options.delete(:_path_segments) + script_name = options.delete(:script_name) - script_name = options.delete(:script_name) path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s path_options = options.except(*RESERVED_OPTIONS) path_options = yield(path_options) if block_given? - path << generate(path_options, path_segments || {}) - # ROUTES TODO: This can be called directly, so script_name should probably be set in the routes - rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path) - rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor] + path_addition, params = generate(path_options, path_segments || {}) + path << path_addition - rewritten_url + ActionDispatch::Http::URL.url_for(options.merge({ + :path => path, + :params => params, + :user => user, + :password => password + })) end def call(env) @@ -561,23 +553,12 @@ module ActionDispatch private - def host_from_options(options) - computed_host = subdomain_and_domain(options) || options[:host] - unless computed_host - raise ArgumentError, "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" + def extract_authentication(options) + if options[:user] && options[:password] + [options.delete(:user), options.delete(:password)] + else + nil end - computed_host - end - - def subdomain_and_domain(options) - return nil unless options[:subdomain] || options[:domain] - tld_length = options[:tld_length] || ActionDispatch::Http::URL.tld_length - - host = "" - host << (options[:subdomain] || ActionDispatch::Http::URL.extract_subdomain(options[:host], tld_length)) - host << "." - host << (options[:domain] || ActionDispatch::Http::URL.extract_domain(options[:host], tld_length)) - host end def handle_positional_args(options) @@ -590,13 +571,6 @@ module ActionDispatch options.merge!(Hash[args.zip(keys).map { |v, k| [k, v] }]) end - def rewrite_authentication(options) - if options[:user] && options[:password] - "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@" - else - "" - end - end end end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index cd067b7d18..89f0d03c56 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -12,8 +12,16 @@ end ROUTING = ActionDispatch::Routing +module RoutingTestHelpers + def url_for(set, options, recall = nil) + set.send(:url_for, options.merge(:only_path => true, :_path_segments => recall)) + end +end + # See RFC 3986, section 3.3 for allowed path characters. class UriReservedCharactersRoutingTest < Test::Unit::TestCase + include RoutingTestHelpers + def setup @set = ActionDispatch::Routing::RouteSet.new @set.draw do @@ -28,12 +36,13 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase end def test_route_generation_escapes_unsafe_path_characters - @set.generate(:controller => "content", :action => "act#{@segment}ion", :variable => "variable", :additional => "foo") assert_equal "/content/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2", - @set.generate(:controller => "content", - :action => "act#{@segment}ion", - :variable => "var#{@segment}iable", - :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"]) + url_for(@set, { + :controller => "content", + :action => "act#{@segment}ion", + :variable => "var#{@segment}iable", + :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"] + }) end def test_route_recognition_unescapes_path_components @@ -45,10 +54,13 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase end def test_route_generation_allows_passing_non_string_values_to_generated_helper - assert_equal "/content/action/variable/1/2", @set.generate(:controller => "content", - :action => "action", - :variable => "variable", - :additional => [1, 2]) + assert_equal "/content/action/variable/1/2", + url_for(@set, { + :controller => "content", + :action => "action", + :variable => "variable", + :additional => [1, 2] + }) end end @@ -68,6 +80,8 @@ class MockController end class LegacyRouteSetTests < Test::Unit::TestCase + include RoutingTestHelpers + attr_reader :rs def setup @@ -81,18 +95,18 @@ class LegacyRouteSetTests < Test::Unit::TestCase def test_default_setup @rs.draw { match '/:controller(/:action(/:id))' } assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/content")) - assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/content/list")) + assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/content/list")) assert_equal({:controller => "content", :action => 'show', :id => '10'}, rs.recognize_path("/content/show/10")) assert_equal({:controller => "admin/user", :action => 'show', :id => '10'}, rs.recognize_path("/admin/user/show/10")) - assert_equal '/admin/user/show/10', rs.generate(:controller => 'admin/user', :action => 'show', :id => 10) + assert_equal '/admin/user/show/10', url_for(rs, { :controller => 'admin/user', :action => 'show', :id => 10 }) - assert_equal '/admin/user/show', rs.generate({:action => 'show'}, {:controller => 'admin/user', :action => 'list', :id => '10'}) - assert_equal '/admin/user/list/10', rs.generate({}, {:controller => 'admin/user', :action => 'list', :id => '10'}) + assert_equal '/admin/user/show', url_for(rs, { :action => 'show' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) + assert_equal '/admin/user/list/10', url_for(rs, {}, { :controller => 'admin/user', :action => 'list', :id => '10' }) - assert_equal '/admin/stuff', rs.generate({:controller => 'stuff'}, {:controller => 'admin/user', :action => 'list', :id => '10'}) - assert_equal '/stuff', rs.generate({:controller => '/stuff'}, {:controller => 'admin/user', :action => 'list', :id => '10'}) + assert_equal '/admin/stuff', url_for(rs, { :controller => 'stuff' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) + assert_equal '/stuff', url_for(rs, { :controller => '/stuff' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) end def test_ignores_leading_slash @@ -143,11 +157,14 @@ class LegacyRouteSetTests < Test::Unit::TestCase match ':controller/:admintoken(/:action(/:id))', :controller => /admin\/.+/ match '/:controller(/:action(/:id))' end + assert_equal({:controller => "admin/user", :admintoken => "foo", :action => "index"}, rs.recognize_path("/admin/user/foo")) - assert_equal({:controller => "content", :action => "foo"}, rs.recognize_path("/content/foo")) - assert_equal '/admin/user/foo', rs.generate(:controller => "admin/user", :admintoken => "foo", :action => "index") - assert_equal '/content/foo', rs.generate(:controller => "content", :action => "foo") + assert_equal({:controller => "content", :action => "foo"}, + rs.recognize_path("/content/foo")) + + assert_equal '/admin/user/foo', url_for(rs, { :controller => "admin/user", :admintoken => "foo", :action => "index" }) + assert_equal '/content/foo', url_for(rs, { :controller => "content", :action => "foo" }) end def test_route_with_regexp_and_captures_for_controller @@ -169,17 +186,16 @@ class LegacyRouteSetTests < Test::Unit::TestCase end # Without a file extension assert_equal '/user/download/file', - rs.generate(:controller => "user", :action => "download", :file => "file") - assert_equal( - {:controller => "user", :action => "download", :file => "file"}, + url_for(rs, { :controller => "user", :action => "download", :file => "file" }) + + assert_equal({:controller => "user", :action => "download", :file => "file"}, rs.recognize_path("/user/download/file")) # Now, let's try a file with an extension, really a dot (.) assert_equal '/user/download/file.jpg', - rs.generate( - :controller => "user", :action => "download", :file => "file.jpg") - assert_equal( - {:controller => "user", :action => "download", :file => "file.jpg"}, + url_for(rs, { :controller => "user", :action => "download", :file => "file.jpg" }) + + assert_equal({:controller => "user", :action => "download", :file => "file.jpg"}, rs.recognize_path("/user/download/file.jpg")) end @@ -187,28 +203,25 @@ class LegacyRouteSetTests < Test::Unit::TestCase rs.draw do root :to => 'content#list', :as => 'home' end - x = setup_for_named_route - assert_equal("http://test.host/", - x.send(:home_url)) + assert_equal("http://test.host/", setup_for_named_route.send(:home_url)) end def test_named_route_with_option rs.draw do match 'page/:title' => 'content#show_page', :as => 'page' end - x = setup_for_named_route + assert_equal("http://test.host/page/new%20stuff", - x.send(:page_url, :title => 'new stuff')) + setup_for_named_route.send(:page_url, :title => 'new stuff')) end def test_named_route_with_default rs.draw do match 'page/:title' => 'content#show_page', :title => 'AboutPage', :as => 'page' end - x = setup_for_named_route - assert_equal("http://test.host/page/AboutRails", - x.send(:page_url, :title => "AboutRails")) + assert_equal("http://test.host/page/AboutRails", + setup_for_named_route.send(:page_url, :title => "AboutRails")) end def test_named_route_with_path_prefix @@ -217,9 +230,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase match 'page' => 'content#show_page', :as => 'page' end end - x = setup_for_named_route + assert_equal("http://test.host/my/page", - x.send(:page_url)) + setup_for_named_route.send(:page_url)) end def test_named_route_with_blank_path_prefix @@ -228,27 +241,33 @@ class LegacyRouteSetTests < Test::Unit::TestCase match 'page' => 'content#show_page', :as => 'page' end end - x = setup_for_named_route + assert_equal("http://test.host/page", - x.send(:page_url)) + setup_for_named_route.send(:page_url)) end def test_named_route_with_nested_controller rs.draw do match 'admin/user' => 'admin/user#index', :as => "users" end - x = setup_for_named_route + assert_equal("http://test.host/admin/user", - x.send(:users_url)) + setup_for_named_route.send(:users_url)) end def test_optimised_named_route_with_host rs.draw do match 'page' => 'content#show_page', :as => 'pages', :host => 'foo.com' end - x = setup_for_named_route - x.expects(:url_for).with(:host => 'foo.com', :only_path => false, :controller => 'content', :action => 'show_page', :use_route => 'pages').once - x.send(:pages_url) + routes = setup_for_named_route + routes.expects(:url_for).with({ + :host => 'foo.com', + :only_path => false, + :controller => 'content', + :action => 'show_page', + :use_route => 'pages' + }).once + routes.send(:pages_url) end def setup_for_named_route @@ -265,9 +284,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase rs.draw do root :to => "hello#index" end - x = setup_for_named_route - assert_equal("http://test.host/", x.send(:root_url)) - assert_equal("/", x.send(:root_path)) + routes = setup_for_named_route + assert_equal("http://test.host/", routes.send(:root_url)) + assert_equal("/", routes.send(:root_path)) end def test_named_route_with_regexps @@ -276,24 +295,19 @@ class LegacyRouteSetTests < Test::Unit::TestCase :year => /\d+/, :month => /\d+/, :day => /\d+/ match ':controller/:action/:id' end - x = setup_for_named_route - # assert_equal( - # {:controller => 'page', :action => 'show', :title => 'hi', :use_route => :article, :only_path => false}, - # x.send(:article_url, :title => 'hi') - # ) - assert_equal( - "http://test.host/page/2005/6/10/hi", - x.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6) - ) + + routes = setup_for_named_route + + assert_equal "http://test.host/page/2005/6/10/hi", + routes.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6) end def test_changing_controller @rs.draw { match ':controller/:action/:id' } - assert_equal '/admin/stuff/show/10', rs.generate( - {:controller => 'stuff', :action => 'show', :id => 10}, - {:controller => 'admin/user', :action => 'index'} - ) + assert_equal '/admin/stuff/show/10', + url_for(rs, {:controller => 'stuff', :action => 'show', :id => 10}, + {:controller => 'admin/user', :action => 'index'}) end def test_paths_escaped @@ -319,8 +333,7 @@ class LegacyRouteSetTests < Test::Unit::TestCase end # No / to %2F in URI, only for query params. - x = setup_for_named_route - assert_equal("/file/hello/world", x.send(:path_path, ['hello', 'world'])) + assert_equal("/file/hello/world", setup_for_named_route.send(:path_path, ['hello', 'world'])) end def test_non_controllers_cannot_be_matched @@ -334,7 +347,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase rs.draw do match 'post/:id' => 'post#show', :constraints => { :id => /\d+/ }, :as => 'post' end - assert_raise(ActionController::RoutingError) { rs.generate(:controller => 'post', :action => 'show', :bad_param => "foo", :use_route => "post") } + assert_raise(ActionController::RoutingError) do + url_for(rs, { :controller => 'post', :action => 'show', :bad_param => "foo", :use_route => "post" }) + end end def test_dynamic_path_allowed @@ -342,7 +357,8 @@ class LegacyRouteSetTests < Test::Unit::TestCase match '*path' => 'content#show_file' end - assert_equal '/pages/boo', rs.generate(:controller => 'content', :action => 'show_file', :path => %w(pages boo)) + assert_equal '/pages/boo', + url_for(rs, { :controller => 'content', :action => 'show_file', :path => %w(pages boo) }) end def test_dynamic_recall_paths_allowed @@ -350,7 +366,8 @@ class LegacyRouteSetTests < Test::Unit::TestCase match '*path' => 'content#show_file' end - assert_equal '/pages/boo', rs.generate({}, :controller => 'content', :action => 'show_file', :path => %w(pages boo)) + assert_equal '/pages/boo', + url_for(rs, {}, { :controller => 'content', :action => 'show_file', :path => %w(pages boo) }) end def test_backwards @@ -359,9 +376,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase match ':controller(/:action(/:id))' end - assert_equal '/page/20', rs.generate({:id => 20}, {:controller => 'pages', :action => 'show'}) - assert_equal '/page/20', rs.generate(:controller => 'pages', :id => 20, :action => 'show') - assert_equal '/pages/boo', rs.generate(:controller => 'pages', :action => 'boo') + assert_equal '/page/20', url_for(rs, { :id => 20 }, { :controller => 'pages', :action => 'show' }) + assert_equal '/page/20', url_for(rs, { :controller => 'pages', :id => 20, :action => 'show' }) + assert_equal '/pages/boo', url_for(rs, { :controller => 'pages', :action => 'boo' }) end def test_route_with_fixnum_default @@ -370,10 +387,10 @@ class LegacyRouteSetTests < Test::Unit::TestCase match ':controller/:action/:id' end - assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page') - assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page', :id => 1) - assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page', :id => '1') - assert_equal '/page/10', rs.generate(:controller => 'content', :action => 'show_page', :id => 10) + assert_equal '/page', url_for(rs, { :controller => 'content', :action => 'show_page' }) + assert_equal '/page', url_for(rs, { :controller => 'content', :action => 'show_page', :id => 1 }) + assert_equal '/page', url_for(rs, { :controller => 'content', :action => 'show_page', :id => '1' }) + assert_equal '/page/10', url_for(rs, { :controller => 'content', :action => 'show_page', :id => 10 }) assert_equal({:controller => "content", :action => 'show_page', :id => 1 }, rs.recognize_path("/page")) assert_equal({:controller => "content", :action => 'show_page', :id => '1'}, rs.recognize_path("/page/1")) @@ -387,20 +404,20 @@ class LegacyRouteSetTests < Test::Unit::TestCase match ':controller/:action/:id' end - assert_equal '/page/foo', rs.generate(:controller => 'content', :action => 'show_page', :id => 'foo') - assert_equal({:controller => "content", :action => 'show_page', :id => 'foo'}, rs.recognize_path("/page/foo")) + assert_equal '/page/foo', url_for(rs, { :controller => 'content', :action => 'show_page', :id => 'foo' }) + assert_equal({ :controller => "content", :action => 'show_page', :id => 'foo' }, rs.recognize_path("/page/foo")) token = "\321\202\320\265\320\272\321\201\321\202" # 'text' in russian token.force_encoding(Encoding::BINARY) if token.respond_to?(:force_encoding) escaped_token = CGI::escape(token) - assert_equal '/page/' + escaped_token, rs.generate(:controller => 'content', :action => 'show_page', :id => token) - assert_equal({:controller => "content", :action => 'show_page', :id => token}, rs.recognize_path("/page/#{escaped_token}")) + assert_equal '/page/' + escaped_token, url_for(rs, { :controller => 'content', :action => 'show_page', :id => token }) + assert_equal({ :controller => "content", :action => 'show_page', :id => token }, rs.recognize_path("/page/#{escaped_token}")) end def test_action_expiry @rs.draw { match ':controller(/:action(/:id))' } - assert_equal '/content', rs.generate({:controller => 'content'}, {:controller => 'content', :action => 'show'}) + assert_equal '/content', url_for(rs, { :controller => 'content' }, { :controller => 'content', :action => 'show' }) end def test_requirement_should_prevent_optional_id @@ -408,10 +425,10 @@ class LegacyRouteSetTests < Test::Unit::TestCase match 'post/:id' => 'post#show', :constraints => {:id => /\d+/}, :as => 'post' end - assert_equal '/post/10', rs.generate(:controller => 'post', :action => 'show', :id => 10) + assert_equal '/post/10', url_for(rs, { :controller => 'post', :action => 'show', :id => 10 }) assert_raise ActionController::RoutingError do - rs.generate(:controller => 'post', :action => 'show') + url_for(rs, { :controller => 'post', :action => 'show' }) end end @@ -424,12 +441,10 @@ class LegacyRouteSetTests < Test::Unit::TestCase match ':controller/:action/:id' end - assert_equal '/test', rs.generate(:controller => 'post', :action => 'show') - assert_equal '/test', rs.generate(:controller => 'post', :action => 'show', :year => nil) + assert_equal '/test', url_for(rs, { :controller => 'post', :action => 'show' }) + assert_equal '/test', url_for(rs, { :controller => 'post', :action => 'show', :year => nil }) - x = setup_for_named_route - assert_equal("http://test.host/test", - x.send(:blog_url)) + assert_equal("http://test.host/test", setup_for_named_route.send(:blog_url)) end def test_set_to_nil_forgets @@ -439,20 +454,20 @@ class LegacyRouteSetTests < Test::Unit::TestCase end assert_equal '/pages/2005', - rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005) + url_for(rs, { :controller => 'content', :action => 'list_pages', :year => 2005 }) assert_equal '/pages/2005/6', - rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005, :month => 6) + url_for(rs, { :controller => 'content', :action => 'list_pages', :year => 2005, :month => 6 }) assert_equal '/pages/2005/6/12', - rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005, :month => 6, :day => 12) + url_for(rs, { :controller => 'content', :action => 'list_pages', :year => 2005, :month => 6, :day => 12 }) assert_equal '/pages/2005/6/4', - rs.generate({:day => 4}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'}) + url_for(rs, { :day => 4 }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) assert_equal '/pages/2005/6', - rs.generate({:day => nil}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'}) + url_for(rs, { :day => nil }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) assert_equal '/pages/2005', - rs.generate({:day => nil, :month => nil}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'}) + url_for(rs, { :day => nil, :month => nil }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) end def test_root_url_generation_with_controller_and_action @@ -460,8 +475,8 @@ class LegacyRouteSetTests < Test::Unit::TestCase root :to => "content#index" end - assert_equal '/', rs.generate(:controller => 'content', :action => 'index') - assert_equal '/', rs.generate(:controller => 'content') + assert_equal '/', url_for(rs, { :controller => 'content', :action => 'index' }) + assert_equal '/', url_for(rs, { :controller => 'content' }) end def test_named_root_url_generation_with_controller_and_action @@ -469,12 +484,10 @@ class LegacyRouteSetTests < Test::Unit::TestCase root :to => "content#index", :as => 'home' end - assert_equal '/', rs.generate(:controller => 'content', :action => 'index') - assert_equal '/', rs.generate(:controller => 'content') + assert_equal '/', url_for(rs, { :controller => 'content', :action => 'index' }) + assert_equal '/', url_for(rs, { :controller => 'content' }) - x = setup_for_named_route - assert_equal("http://test.host/", - x.send(:home_url)) + assert_equal("http://test.host/", setup_for_named_route.send(:home_url)) end def test_named_route_method @@ -483,8 +496,8 @@ class LegacyRouteSetTests < Test::Unit::TestCase match ':controller(/:action(/:id))' end - assert_equal '/categories', rs.generate(:controller => 'content', :action => 'categories') - assert_equal '/content/hi', rs.generate({:controller => 'content', :action => 'hi'}) + assert_equal '/categories', url_for(rs, { :controller => 'content', :action => 'categories' }) + assert_equal '/content/hi', url_for(rs, { :controller => 'content', :action => 'hi' }) end def test_named_routes_array @@ -499,7 +512,12 @@ class LegacyRouteSetTests < Test::Unit::TestCase match ':controller/:action/:id' end - assert_equal '/journal', rs.generate(:controller => 'content', :action => 'list_journal', :date => nil, :user_id => nil) + assert_equal '/journal', url_for(rs, { + :controller => 'content', + :action => 'list_journal', + :date => nil, + :user_id => nil + }) end def setup_request_method_routes_for(method) @@ -564,9 +582,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase match '/posts/new/:action' => 'subpath_books' end - assert_equal "/books/7/edit", rs.generate(:controller => "subpath_books", :id => 7, :action => "edit") - assert_equal "/items/15/complete", rs.generate(:controller => "subpath_books", :id => 15, :action => "complete") - assert_equal "/posts/new/preview", rs.generate(:controller => "subpath_books", :action => "preview") + assert_equal "/books/7/edit", url_for(rs, { :controller => "subpath_books", :id => 7, :action => "edit" }) + assert_equal "/items/15/complete", url_for(rs, { :controller => "subpath_books", :id => 15, :action => "complete" }) + assert_equal "/posts/new/preview", url_for(rs, { :controller => "subpath_books", :action => "preview" }) end def test_failed_constraints_raises_exception_with_violated_constraints @@ -574,9 +592,8 @@ class LegacyRouteSetTests < Test::Unit::TestCase match 'foos/:id' => 'foos#show', :as => 'foo_with_requirement', :constraints => { :id => /\d+/ } end - x = setup_for_named_route assert_raise(ActionController::RoutingError) do - x.send(:foo_with_requirement_url, "I am Against the constraints") + setup_for_named_route.send(:foo_with_requirement_url, "I am Against the constraints") end end @@ -606,11 +623,12 @@ class LegacyRouteSetTests < Test::Unit::TestCase assert_not_nil hash assert_equal %w(cc ac), [hash[:controller], hash[:action]] - end end class RouteSetTest < ActiveSupport::TestCase + include RoutingTestHelpers + def set @set ||= ROUTING::RouteSet.new end @@ -657,7 +675,8 @@ class RouteSetTest < ActiveSupport::TestCase match ':controller/:action/:id.:format' match ':controller/:action/:id' end - assert_equal "/foo/bar/15?this=hello", set.generate(:controller => "foo", :action => "bar", :id => 15, :this => "hello") + assert_equal "/foo/bar/15?this=hello", + url_for(set, { :controller => "foo", :action => "bar", :id => 15, :this => "hello" }) end def test_extra_keys_not_first @@ -742,7 +761,7 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal "http://test.host/admin/users", controller.send(:users_url) assert_equal '/admin/users', controller.send(:users_path) - assert_equal '/admin/users', set.generate(controller.send(:hash_for_users_url), {:controller => 'users', :action => 'index'}) + assert_equal '/admin/users', url_for(set, controller.send(:hash_for_users_url), { :controller => 'users', :action => 'index' }) end def test_named_route_url_method_with_anchor @@ -813,8 +832,8 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal 1, set.routes.size - assert_equal '/users/show/10', set.generate(:controller => 'users', :action => 'show', :id => 10) - assert_equal '/users/index/10', set.generate(:controller => 'users', :id => 10) + assert_equal '/users/show/10', url_for(set, { :controller => 'users', :action => 'show', :id => 10 }) + assert_equal '/users/index/10', url_for(set, { :controller => 'users', :id => 10 }) assert_equal({:controller => 'users', :action => 'index', :id => '10'}, set.recognize_path('/users/index/10')) assert_equal({:controller => 'users', :action => 'index', :id => '10'}, set.recognize_path('/users/index/10/')) @@ -992,7 +1011,7 @@ class RouteSetTest < ActiveSupport::TestCase match "/people/list", :controller => "people", :action => "list" end - url = set.generate(:controller => "people", :action => "list") + url = url_for(set, { :controller => "people", :action => "list" }) assert_equal "/people/list", url end @@ -1057,8 +1076,9 @@ class RouteSetTest < ActiveSupport::TestCase def test_generate_changes_controller_module set.draw { match ':controller/:action/:id' } current = { :controller => "bling/bloop", :action => "bap", :id => 9 } - url = set.generate({:controller => "foo/bar", :action => "baz", :id => 7}, current) - assert_equal "/foo/bar/baz/7", url + + assert_equal "/foo/bar/baz/7", + url_for(set, { :controller => "foo/bar", :action => "baz", :id => 7 }, current) end def test_id_is_sticky_when_it_ought_to_be @@ -1066,7 +1086,7 @@ class RouteSetTest < ActiveSupport::TestCase match ':controller/:id/:action' end - url = set.generate({:action => "destroy"}, {:controller => "people", :action => "show", :id => "7"}) + url = url_for(set, { :action => "destroy" }, { :controller => "people", :action => "show", :id => "7" }) assert_equal "/people/7/destroy", url end @@ -1076,8 +1096,9 @@ class RouteSetTest < ActiveSupport::TestCase match ':controller/:action/:id' end - url = set.generate({:controller => "welcome", :action => "about"}, - {:controller => "welcome", :action => "get", :id => "7"}) + url = url_for(set, { :controller => "welcome", :action => "about" }, + { :controller => "welcome", :action => "get", :id => "7" }) + assert_equal "/about", url end @@ -1085,7 +1106,7 @@ class RouteSetTest < ActiveSupport::TestCase set.draw { match ':controller/:action/:id' } args = { :controller => "foo", :action => "bar", :id => "7", :x => "y" } - assert_equal "/foo/bar/7?x=y", set.generate(args) + assert_equal "/foo/bar/7?x=y", url_for(set, args) assert_equal ["/foo/bar/7", [:x]], set.generate_extras(args) assert_equal [:x], set.extra_keys(args) end @@ -1098,7 +1119,7 @@ class RouteSetTest < ActiveSupport::TestCase end args = { :controller => "foo", :action => "bar", :id => "7", :x => "y" } - assert_equal "/my/foo/bar/7?x=y", set.generate(args) + assert_equal "/my/foo/bar/7?x=y", url_for(set, args) end def test_generate_with_blank_path_prefix @@ -1109,7 +1130,7 @@ class RouteSetTest < ActiveSupport::TestCase end args = { :controller => "foo", :action => "bar", :id => "7", :x => "y" } - assert_equal "/foo/bar/7?x=y", set.generate(args) + assert_equal "/foo/bar/7?x=y", url_for(set, args) end def test_named_routes_are_never_relative_to_modules @@ -1119,10 +1140,10 @@ class RouteSetTest < ActiveSupport::TestCase match '/connection' => 'connection#index', :as => 'family_connection' end - url = set.generate({:controller => "connection"}, {:controller => 'connection/manage'}) + url = url_for(set, { :controller => "connection" }, { :controller => 'connection/manage' }) assert_equal "/connection/connection", url - url = set.generate({:use_route => :family_connection, :controller => "connection"}, {:controller => 'connection/manage'}) + url = url_for(set, { :use_route => :family_connection, :controller => "connection" }, { :controller => 'connection/manage' }) assert_equal "/connection", url end @@ -1130,7 +1151,7 @@ class RouteSetTest < ActiveSupport::TestCase set.draw do match ':controller(/:action(/:id))' end - assert_equal '/books', set.generate( + assert_equal '/books', url_for(set, {:controller => 'books', :action => 'index'}, {:controller => 'books', :action => 'show', :id => '10'} ) @@ -1141,7 +1162,7 @@ class RouteSetTest < ActiveSupport::TestCase match 'show_weblog/:parameter' => 'weblog#show' match ':controller(/:action(/:id))' end - assert_equal '/weblog/edit?parameter=1', set.generate( + assert_equal '/weblog/edit?parameter=1', url_for(set, {:action => 'edit', :parameter => 1}, {:controller => 'weblog', :action => 'show', :parameter => 1} ) @@ -1152,12 +1173,12 @@ class RouteSetTest < ActiveSupport::TestCase match '/posts(.:format)' => 'posts#index' end - assert_equal '/posts', set.generate( + assert_equal '/posts', url_for(set, {:controller => 'posts'}, {:controller => 'posts', :action => 'index', :format => 'xml'} ) - assert_equal '/posts.xml', set.generate( + assert_equal '/posts.xml', url_for(set, {:controller => 'posts', :format => 'xml'}, {:controller => 'posts', :action => 'index', :format => 'xml'} ) @@ -1165,9 +1186,9 @@ class RouteSetTest < ActiveSupport::TestCase def test_expiry_determination_should_consider_values_with_to_param set.draw { match 'projects/:project_id/:controller/:action' } - assert_equal '/projects/1/weblog/show', set.generate( - {:action => 'show', :project_id => 1}, - {:controller => 'weblog', :action => 'show', :project_id => '1'}) + assert_equal '/projects/1/weblog/show', url_for(set, + { :action => 'show', :project_id => 1 }, + { :controller => 'weblog', :action => 'show', :project_id => '1' }) end def test_named_route_in_nested_resource @@ -1208,7 +1229,7 @@ class RouteSetTest < ActiveSupport::TestCase assert_raise ArgumentError do set.draw do match 'page/:name' => 'pages#show', - :constraints => {:name => /(david|jamis)/m} + :constraints => { :name => /(david|jamis)/m } end end end @@ -1217,18 +1238,18 @@ class RouteSetTest < ActiveSupport::TestCase assert_nothing_raised do set.draw do match 'page/:name' => 'pages#show', - :constraints => {:name => /(david|jamis)/i} + :constraints => { :name => /(david|jamis)/i } end end assert_nothing_raised do set.draw do match 'page/:name' => 'pages#show', - :constraints => {:name => / # Desperately overcommented regexp + :constraints => { :name => / # Desperately overcommented regexp ( #Either david #The Creator | #Or jamis #The Deployer - )/x} + )/x } end end end @@ -1251,12 +1272,12 @@ class RouteSetTest < ActiveSupport::TestCase :constraints => {:name => /(david|jamis)/i} end - url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'}) + url = url_for(set, { :controller => 'pages', :action => 'show', :name => 'david' }) assert_equal "/page/david", url assert_raise ActionController::RoutingError do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'}) + url_for(set, { :controller => 'pages', :action => 'show', :name => 'davidjamis' }) end - url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) + url = url_for(set, { :controller => 'pages', :action => 'show', :name => 'JAMIS' }) assert_equal "/page/JAMIS", url end @@ -1280,7 +1301,7 @@ class RouteSetTest < ActiveSupport::TestCase end end - def test_route_requirement_generate_with_xi_modifiers + def test_route_requirement_with_xi_modifiers set.draw do match 'page/:name' => 'pages#show', :constraints => {:name => / # Desperately overcommented regexp @@ -1291,21 +1312,11 @@ class RouteSetTest < ActiveSupport::TestCase )/xi} end - url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) - assert_equal "/page/JAMIS", url - end + assert_equal({:controller => 'pages', :action => 'show', :name => 'JAMIS'}, + set.recognize_path('/page/JAMIS')) - def test_route_requirement_recognize_with_xi_modifiers - set.draw do - match 'page/:name' => 'pages#show', - :constraints => {:name => / # Desperately overcommented regexp - ( #Either - david #The Creator - | #Or - jamis #The Deployer - )/xi} - end - assert_equal({:controller => 'pages', :action => 'show', :name => 'JAMIS'}, set.recognize_path('/page/JAMIS')) + assert_equal "/page/JAMIS", + url_for(set, { :controller => 'pages', :action => 'show', :name => 'JAMIS' }) end def test_routes_with_symbols @@ -1323,8 +1334,8 @@ class RouteSetTest < ActiveSupport::TestCase match '/hello' => 'bar#index' end - assert_equal '/', set.generate(:controller => 'foo') - assert_equal '/hello', set.generate(:controller => 'bar') + assert_equal '/', url_for(set, { :controller => 'foo' }) + assert_equal '/hello', url_for(set, { :controller => 'bar' }) assert_equal({:controller => "foo", :action => "index"}, set.recognize_path('/')) assert_equal({:controller => "bar", :action => "index"}, set.recognize_path('/hello')) @@ -1335,7 +1346,7 @@ class RouteSetTest < ActiveSupport::TestCase match '/cars/:action/:person/:car/', :controller => 'cars' end - assert_equal '/cars/buy/1/2', set.generate(:controller => 'cars', :action => 'buy', :person => '1', :car => '2') + assert_equal '/cars/buy/1/2', url_for(set, { :controller => 'cars', :action => 'buy', :person => '1', :car => '2' }) assert_equal({:controller => "cars", :action => "buy", :person => "1", :car => "2"}, set.recognize_path('/cars/buy/1/2')) end @@ -1345,7 +1356,7 @@ class RouteSetTest < ActiveSupport::TestCase match '/books/:action.rss', :controller => 'books' end - assert_equal '/books/list.rss', set.generate(:controller => 'books', :action => 'list') + assert_equal '/books/list.rss', url_for(set, { :controller => 'books', :action => 'list' }) assert_equal({:controller => "books", :action => "list"}, set.recognize_path('/books/list.rss')) end @@ -1355,14 +1366,14 @@ class RouteSetTest < ActiveSupport::TestCase match '/books(/:action(.:format))', :controller => 'books' end - assert_equal '/books/list.rss', set.generate(:controller => 'books', :action => 'list', :format => 'rss') - assert_equal '/books/list.xml', set.generate(:controller => 'books', :action => 'list', :format => 'xml') - assert_equal '/books/list', set.generate(:controller => 'books', :action => 'list') - assert_equal '/books', set.generate(:controller => 'books', :action => 'index') + assert_equal '/books/list.rss', url_for(set, { :controller => 'books', :action => 'list', :format => 'rss' }) + assert_equal '/books/list.xml', url_for(set, { :controller => 'books', :action => 'list', :format => 'xml' }) + assert_equal '/books/list', url_for(set, { :controller => 'books', :action => 'list' }) + assert_equal '/books', url_for(set, { :controller => 'books', :action => 'index' }) assert_equal({:controller => "books", :action => "list", :format => "rss"}, set.recognize_path('/books/list.rss')) assert_equal({:controller => "books", :action => "list", :format => "xml"}, set.recognize_path('/books/list.xml')) - assert_equal({:controller => "books", :action => "list"}, set.recognize_path('/books/list')) + assert_equal({:controller => "books", :action => "list"}, set.recognize_path('/books/list')) assert_equal({:controller => "books", :action => "index"}, set.recognize_path('/books')) end @@ -1370,13 +1381,13 @@ class RouteSetTest < ActiveSupport::TestCase @set = nil set.draw { match("/:controller(/:action(/:id))") } - assert_equal '/content', set.generate(:controller => 'content', :action => 'index') - assert_equal '/content/list', set.generate(:controller => 'content', :action => 'list') - assert_equal '/content/show/1', set.generate(:controller => 'content', :action => 'show', :id => '1') + assert_equal '/content', url_for(set, { :controller => 'content', :action => 'index' }) + assert_equal '/content/list', url_for(set, { :controller => 'content', :action => 'list' }) + assert_equal '/content/show/1', url_for(set, { :controller => 'content', :action => 'show', :id => '1' }) assert_equal({:controller => "content", :action => "index"}, set.recognize_path('/content')) assert_equal({:controller => "content", :action => "index"}, set.recognize_path('/content/index')) - assert_equal({:controller => "content", :action => "list"}, set.recognize_path('/content/list')) + assert_equal({:controller => "content", :action => "list"}, set.recognize_path('/content/list')) assert_equal({:controller => "content", :action => "show", :id => "1"}, set.recognize_path('/content/show/1')) end @@ -1401,54 +1412,54 @@ class RouteSetTest < ActiveSupport::TestCase end def test_default_route_should_omit_default_action - assert_equal '/accounts', default_route_set.generate({:controller => 'accounts', :action => 'index'}) + assert_equal '/accounts', url_for(default_route_set, { :controller => 'accounts', :action => 'index' }) end def test_default_route_should_include_default_action_when_id_present - assert_equal '/accounts/index/20', default_route_set.generate({:controller => 'accounts', :action => 'index', :id => '20'}) + assert_equal '/accounts/index/20', url_for(default_route_set, { :controller => 'accounts', :action => 'index', :id => '20' }) end def test_default_route_should_work_with_action_but_no_id - assert_equal '/accounts/list_all', default_route_set.generate({:controller => 'accounts', :action => 'list_all'}) + assert_equal '/accounts/list_all', url_for(default_route_set, { :controller => 'accounts', :action => 'list_all' }) end def test_default_route_should_uri_escape_pluses expected = { :controller => 'pages', :action => 'show', :id => 'hello world' } assert_equal expected, default_route_set.recognize_path('/pages/show/hello%20world') - assert_equal '/pages/show/hello%20world', default_route_set.generate(expected, expected) + assert_equal '/pages/show/hello%20world', url_for(default_route_set, expected) expected[:id] = 'hello+world' assert_equal expected, default_route_set.recognize_path('/pages/show/hello+world') assert_equal expected, default_route_set.recognize_path('/pages/show/hello%2Bworld') - assert_equal '/pages/show/hello+world', default_route_set.generate(expected, expected) + assert_equal '/pages/show/hello+world', url_for(default_route_set, expected) end def test_build_empty_query_string - assert_uri_equal '/foo', default_route_set.generate({:controller => 'foo'}) + assert_uri_equal '/foo', url_for(default_route_set, { :controller => 'foo' }) end def test_build_query_string_with_nil_value - assert_uri_equal '/foo', default_route_set.generate({:controller => 'foo', :x => nil}) + assert_uri_equal '/foo', url_for(default_route_set, { :controller => 'foo', :x => nil }) end def test_simple_build_query_string - assert_uri_equal '/foo?x=1&y=2', default_route_set.generate({:controller => 'foo', :x => '1', :y => '2'}) + assert_uri_equal '/foo?x=1&y=2', url_for(default_route_set, { :controller => 'foo', :x => '1', :y => '2' }) end def test_convert_ints_build_query_string - assert_uri_equal '/foo?x=1&y=2', default_route_set.generate({:controller => 'foo', :x => 1, :y => 2}) + assert_uri_equal '/foo?x=1&y=2', url_for(default_route_set, { :controller => 'foo', :x => 1, :y => 2 }) end def test_escape_spaces_build_query_string - assert_uri_equal '/foo?x=hello+world&y=goodbye+world', default_route_set.generate({:controller => 'foo', :x => 'hello world', :y => 'goodbye world'}) + assert_uri_equal '/foo?x=hello+world&y=goodbye+world', url_for(default_route_set, { :controller => 'foo', :x => 'hello world', :y => 'goodbye world' }) end def test_expand_array_build_query_string - assert_uri_equal '/foo?x%5B%5D=1&x%5B%5D=2', default_route_set.generate({:controller => 'foo', :x => [1, 2]}) + assert_uri_equal '/foo?x%5B%5D=1&x%5B%5D=2', url_for(default_route_set, { :controller => 'foo', :x => [1, 2] }) end def test_escape_spaces_build_query_string_selected_keys - assert_uri_equal '/foo?x=hello+world', default_route_set.generate({:controller => 'foo', :x => 'hello world'}) + assert_uri_equal '/foo?x=hello+world', url_for(default_route_set, { :controller => 'foo', :x => 'hello world' }) end def test_generate_with_default_params @@ -1462,7 +1473,7 @@ class RouteSetTest < ActiveSupport::TestCase match ':controller/:action/:id' end - assert_equal '/ibocorp', set.generate({:controller => 'ibocorp', :action => "show", :page => 1}) + assert_equal '/ibocorp', url_for(set, { :controller => 'ibocorp', :action => "show", :page => 1 }) end def test_generate_with_optional_params_recalls_last_request @@ -1492,11 +1503,11 @@ class RouteSetTest < ActiveSupport::TestCase last_request = set.recognize_path("/blog/2006/07/28").freeze assert_equal({:controller => "blog", :action => "show_date", :year => "2006", :month => "07", :day => "28"}, last_request) - assert_equal("/blog/2006/07/25", set.generate({:day => 25}, last_request)) - assert_equal("/blog/2005", set.generate({:year => 2005}, last_request)) - assert_equal("/blog/show/123", set.generate({:action => "show" , :id => 123}, last_request)) - assert_equal("/blog/2006", set.generate({:year => 2006}, last_request)) - assert_equal("/blog/2006", set.generate({:year => 2006, :month => nil}, last_request)) + assert_equal("/blog/2006/07/25", url_for(set, { :day => 25 }, last_request)) + assert_equal("/blog/2005", url_for(set, { :year => 2005 }, last_request)) + assert_equal("/blog/show/123", url_for(set, { :action => "show" , :id => 123 }, last_request)) + assert_equal("/blog/2006", url_for(set, { :year => 2006 }, last_request)) + assert_equal("/blog/2006", url_for(set, { :year => 2006, :month => nil }, last_request)) end private @@ -1512,6 +1523,8 @@ class RouteSetTest < ActiveSupport::TestCase end class RackMountIntegrationTests < ActiveSupport::TestCase + include RoutingTestHelpers + Model = Struct.new(:to_param) Mapping = lambda { @@ -1647,111 +1660,111 @@ class RackMountIntegrationTests < ActiveSupport::TestCase end def test_generate - assert_equal '/admin/users', @routes.generate(:use_route => 'admin_users') - assert_equal '/admin/users', @routes.generate(:controller => 'admin/users') - assert_equal '/admin/users', @routes.generate(:controller => 'admin/users', :action => 'index') - assert_equal '/admin/users', @routes.generate({:action => 'index'}, {:controller => 'admin/users'}) - assert_equal '/admin/users', @routes.generate({:controller => 'users', :action => 'index'}, {:controller => 'admin/accounts'}) - assert_equal '/people', @routes.generate({:controller => '/people', :action => 'index'}, {:controller => 'admin/accounts'}) - - assert_equal '/admin/posts', @routes.generate({:controller => 'admin/posts'}) - assert_equal '/admin/posts/new', @routes.generate({:controller => 'admin/posts', :action => 'new'}) - - assert_equal '/blog/2009', @routes.generate(:controller => 'posts', :action => 'show_date', :year => 2009) - assert_equal '/blog/2009/1', @routes.generate(:controller => 'posts', :action => 'show_date', :year => 2009, :month => 1) - assert_equal '/blog/2009/1/1', @routes.generate(:controller => 'posts', :action => 'show_date', :year => 2009, :month => 1, :day => 1) - - assert_equal '/archive/2010', @routes.generate(:controller => 'archive', :action => 'index', :year => '2010') - assert_equal '/archive', @routes.generate(:controller => 'archive', :action => 'index') - assert_equal '/archive?year=january', @routes.generate(:controller => 'archive', :action => 'index', :year => 'january') - - assert_equal '/people', @routes.generate(:controller => 'people', :action => 'index') - assert_equal '/people', @routes.generate({:action => 'index'}, {:controller => 'people'}) - assert_equal '/people', @routes.generate({:action => 'index'}, {:controller => 'people', :action => 'show', :id => '1'}) - assert_equal '/people', @routes.generate({:controller => 'people', :action => 'index'}, {:controller => 'people', :action => 'show', :id => '1'}) - assert_equal '/people', @routes.generate({}, {:controller => 'people', :action => 'index'}) - assert_equal '/people/1', @routes.generate({:controller => 'people', :action => 'show'}, {:controller => 'people', :action => 'show', :id => '1'}) - assert_equal '/people/new', @routes.generate(:use_route => 'new_person') - assert_equal '/people/new', @routes.generate(:controller => 'people', :action => 'new') - assert_equal '/people/1', @routes.generate(:use_route => 'person', :id => '1') - assert_equal '/people/1', @routes.generate(:controller => 'people', :action => 'show', :id => '1') - assert_equal '/people/1.xml', @routes.generate(:controller => 'people', :action => 'show', :id => '1', :format => 'xml') - assert_equal '/people/1', @routes.generate(:controller => 'people', :action => 'show', :id => 1) - assert_equal '/people/1', @routes.generate(:controller => 'people', :action => 'show', :id => Model.new('1')) - assert_equal '/people/1', @routes.generate({:action => 'show', :id => '1'}, {:controller => 'people', :action => 'index'}) - assert_equal '/people/1', @routes.generate({:action => 'show', :id => 1}, {:controller => 'people', :action => 'show', :id => '1'}) - assert_equal '/people', @routes.generate({:controller => 'people', :action => 'index'}, {:controller => 'people', :action => 'show', :id => '1'}) - assert_equal '/people/1', @routes.generate({}, {:controller => 'people', :action => 'show', :id => '1'}) - assert_equal '/people/1', @routes.generate({:controller => 'people', :action => 'show'}, {:controller => 'people', :action => 'index', :id => '1'}) - assert_equal '/people/1/edit', @routes.generate(:controller => 'people', :action => 'edit', :id => '1') - assert_equal '/people/1/edit.xml', @routes.generate(:controller => 'people', :action => 'edit', :id => '1', :format => 'xml') - assert_equal '/people/1/edit', @routes.generate(:use_route => 'edit_person', :id => '1') - assert_equal '/people/1?legacy=true', @routes.generate(:controller => 'people', :action => 'show', :id => '1', :legacy => 'true') - assert_equal '/people?legacy=true', @routes.generate(:controller => 'people', :action => 'index', :legacy => 'true') - - assert_equal '/id_default/2', @routes.generate(:controller => 'foo', :action => 'id_default', :id => '2') - assert_equal '/id_default', @routes.generate(:controller => 'foo', :action => 'id_default', :id => '1') - assert_equal '/id_default', @routes.generate(:controller => 'foo', :action => 'id_default', :id => 1) - assert_equal '/id_default', @routes.generate(:controller => 'foo', :action => 'id_default') - assert_equal '/optional/bar', @routes.generate(:controller => 'posts', :action => 'index', :optional => 'bar') - assert_equal '/posts', @routes.generate(:controller => 'posts', :action => 'index') - - assert_equal '/project', @routes.generate({:controller => 'project', :action => 'index'}) - assert_equal '/projects/1', @routes.generate({:controller => 'project', :action => 'index', :project_id => '1'}) - assert_equal '/projects/1', @routes.generate({:controller => 'project', :action => 'index'}, {:project_id => '1'}) - assert_raise(ActionController::RoutingError) { @routes.generate({:use_route => 'project', :controller => 'project', :action => 'index'}) } - assert_equal '/projects/1', @routes.generate({:use_route => 'project', :controller => 'project', :action => 'index', :project_id => '1'}) - assert_equal '/projects/1', @routes.generate({:use_route => 'project', :controller => 'project', :action => 'index'}, {:project_id => '1'}) - - assert_equal '/clients', @routes.generate(:controller => 'projects', :action => 'index') - assert_equal '/clients?project_id=1', @routes.generate(:controller => 'projects', :action => 'index', :project_id => '1') - assert_equal '/clients', @routes.generate({:controller => 'projects', :action => 'index'}, {:project_id => '1'}) - assert_equal '/clients', @routes.generate({:action => 'index'}, {:controller => 'projects', :action => 'index', :project_id => '1'}) - - assert_equal '/comment/20', @routes.generate({:id => 20}, {:controller => 'comments', :action => 'show'}) - assert_equal '/comment/20', @routes.generate(:controller => 'comments', :id => 20, :action => 'show') - assert_equal '/comments/boo', @routes.generate(:controller => 'comments', :action => 'boo') - - assert_equal '/ws/posts/show/1', @routes.generate(:controller => 'posts', :action => 'show', :id => '1', :ws => true) - assert_equal '/ws/posts', @routes.generate(:controller => 'posts', :action => 'index', :ws => true) - - assert_equal '/account', @routes.generate(:controller => 'account', :action => 'subscription') - assert_equal '/account/billing', @routes.generate(:controller => 'account', :action => 'billing') - - assert_equal '/pages/1/notes/show/1', @routes.generate(:page_id => '1', :controller => 'notes', :action => 'show', :id => '1') - assert_equal '/pages/1/notes/list', @routes.generate(:page_id => '1', :controller => 'notes', :action => 'list') - assert_equal '/pages/1/notes', @routes.generate(:page_id => '1', :controller => 'notes', :action => 'index') - assert_equal '/pages/1/notes', @routes.generate(:page_id => '1', :controller => 'notes') - assert_equal '/notes', @routes.generate(:page_id => nil, :controller => 'notes') - assert_equal '/notes', @routes.generate(:controller => 'notes') - assert_equal '/notes/print', @routes.generate(:controller => 'notes', :action => 'print') - assert_equal '/notes/print', @routes.generate({}, {:controller => 'notes', :action => 'print'}) - - assert_equal '/notes/index/1', @routes.generate({:controller => 'notes'}, {:controller => 'notes', :id => '1'}) - assert_equal '/notes/index/1', @routes.generate({:controller => 'notes'}, {:controller => 'notes', :id => '1', :foo => 'bar'}) - assert_equal '/notes/index/1', @routes.generate({:controller => 'notes'}, {:controller => 'notes', :id => '1'}) - assert_equal '/notes/index/1', @routes.generate({:action => 'index'}, {:controller => 'notes', :id => '1'}) - assert_equal '/notes/index/1', @routes.generate({}, {:controller => 'notes', :id => '1'}) - assert_equal '/notes/show/1', @routes.generate({}, {:controller => 'notes', :action => 'show', :id => '1'}) - assert_equal '/notes/index/1', @routes.generate({:controller => 'notes', :id => '1'}, {:foo => 'bar'}) - assert_equal '/posts', @routes.generate({:controller => 'posts'}, {:controller => 'notes', :action => 'show', :id => '1'}) - assert_equal '/notes/list', @routes.generate({:action => 'list'}, {:controller => 'notes', :action => 'show', :id => '1'}) - - assert_equal '/posts/ping', @routes.generate(:controller => 'posts', :action => 'ping') - assert_equal '/posts/show/1', @routes.generate(:controller => 'posts', :action => 'show', :id => '1') - assert_equal '/posts', @routes.generate(:controller => 'posts') - assert_equal '/posts', @routes.generate(:controller => 'posts', :action => 'index') - assert_equal '/posts', @routes.generate({:controller => 'posts'}, {:controller => 'posts', :action => 'index'}) - assert_equal '/posts/create', @routes.generate({:action => 'create'}, {:controller => 'posts'}) - assert_equal '/posts?foo=bar', @routes.generate(:controller => 'posts', :foo => 'bar') - assert_equal '/posts?foo%5B%5D=bar&foo%5B%5D=baz', @routes.generate(:controller => 'posts', :foo => ['bar', 'baz']) - assert_equal '/posts?page=2', @routes.generate(:controller => 'posts', :page => 2) - assert_equal '/posts?q%5Bfoo%5D%5Ba%5D=b', @routes.generate(:controller => 'posts', :q => { :foo => { :a => 'b'}}) - - assert_equal '/news.rss', @routes.generate(:controller => 'news', :action => 'index', :format => 'rss') - - - assert_raise(ActionController::RoutingError) { @routes.generate({:action => 'index'}) } + assert_equal '/admin/users', url_for(@routes, { :use_route => 'admin_users' }) + assert_equal '/admin/users', url_for(@routes, { :controller => 'admin/users' }) + assert_equal '/admin/users', url_for(@routes, { :controller => 'admin/users', :action => 'index' }) + assert_equal '/admin/users', url_for(@routes, { :action => 'index' }, { :controller => 'admin/users' }) + assert_equal '/admin/users', url_for(@routes, { :controller => 'users', :action => 'index' }, { :controller => 'admin/accounts' }) + assert_equal '/people', url_for(@routes, { :controller => '/people', :action => 'index' }, { :controller => 'admin/accounts' }) + + assert_equal '/admin/posts', url_for(@routes, { :controller => 'admin/posts' }) + assert_equal '/admin/posts/new', url_for(@routes, { :controller => 'admin/posts', :action => 'new' }) + + assert_equal '/blog/2009', url_for(@routes, { :controller => 'posts', :action => 'show_date', :year => 2009 }) + assert_equal '/blog/2009/1', url_for(@routes, { :controller => 'posts', :action => 'show_date', :year => 2009, :month => 1 }) + assert_equal '/blog/2009/1/1', url_for(@routes, { :controller => 'posts', :action => 'show_date', :year => 2009, :month => 1, :day => 1 }) + + assert_equal '/archive/2010', url_for(@routes, { :controller => 'archive', :action => 'index', :year => '2010' }) + assert_equal '/archive', url_for(@routes, { :controller => 'archive', :action => 'index' }) + assert_equal '/archive?year=january', url_for(@routes, { :controller => 'archive', :action => 'index', :year => 'january' }) + + assert_equal '/people', url_for(@routes, { :controller => 'people', :action => 'index' }) + assert_equal '/people', url_for(@routes, { :action => 'index' }, { :controller => 'people' }) + assert_equal '/people', url_for(@routes, { :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }) + assert_equal '/people', url_for(@routes, { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }) + assert_equal '/people', url_for(@routes, {}, { :controller => 'people', :action => 'index' }) + assert_equal '/people/1', url_for(@routes, { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'show', :id => '1' }) + assert_equal '/people/new', url_for(@routes, { :use_route => 'new_person' }) + assert_equal '/people/new', url_for(@routes, { :controller => 'people', :action => 'new' }) + assert_equal '/people/1', url_for(@routes, { :use_route => 'person', :id => '1' }) + assert_equal '/people/1', url_for(@routes, { :controller => 'people', :action => 'show', :id => '1' }) + assert_equal '/people/1.xml', url_for(@routes, { :controller => 'people', :action => 'show', :id => '1', :format => 'xml' }) + assert_equal '/people/1', url_for(@routes, { :controller => 'people', :action => 'show', :id => 1 }) + assert_equal '/people/1', url_for(@routes, { :controller => 'people', :action => 'show', :id => Model.new('1') }) + assert_equal '/people/1', url_for(@routes, { :action => 'show', :id => '1' }, { :controller => 'people', :action => 'index' }) + assert_equal '/people/1', url_for(@routes, { :action => 'show', :id => 1 }, { :controller => 'people', :action => 'show', :id => '1' }) + assert_equal '/people', url_for(@routes, { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }) + assert_equal '/people/1', url_for(@routes, {}, { :controller => 'people', :action => 'show', :id => '1' }) + assert_equal '/people/1', url_for(@routes, { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'index', :id => '1' }) + assert_equal '/people/1/edit', url_for(@routes, { :controller => 'people', :action => 'edit', :id => '1' }) + assert_equal '/people/1/edit.xml', url_for(@routes, { :controller => 'people', :action => 'edit', :id => '1', :format => 'xml' }) + assert_equal '/people/1/edit', url_for(@routes, { :use_route => 'edit_person', :id => '1' }) + assert_equal '/people/1?legacy=true', url_for(@routes, { :controller => 'people', :action => 'show', :id => '1', :legacy => 'true' }) + assert_equal '/people?legacy=true', url_for(@routes, { :controller => 'people', :action => 'index', :legacy => 'true' }) + + assert_equal '/id_default/2', url_for(@routes, { :controller => 'foo', :action => 'id_default', :id => '2' }) + assert_equal '/id_default', url_for(@routes, { :controller => 'foo', :action => 'id_default', :id => '1' }) + assert_equal '/id_default', url_for(@routes, { :controller => 'foo', :action => 'id_default', :id => 1 }) + assert_equal '/id_default', url_for(@routes, { :controller => 'foo', :action => 'id_default' }) + assert_equal '/optional/bar', url_for(@routes, { :controller => 'posts', :action => 'index', :optional => 'bar' }) + assert_equal '/posts', url_for(@routes, { :controller => 'posts', :action => 'index' }) + + assert_equal '/project', url_for(@routes, { :controller => 'project', :action => 'index' }) + assert_equal '/projects/1', url_for(@routes, { :controller => 'project', :action => 'index', :project_id => '1' }) + assert_equal '/projects/1', url_for(@routes, { :controller => 'project', :action => 'index'}, {:project_id => '1' }) + assert_raise(ActionController::RoutingError) { url_for(@routes, { :use_route => 'project', :controller => 'project', :action => 'index' }) } + assert_equal '/projects/1', url_for(@routes, { :use_route => 'project', :controller => 'project', :action => 'index', :project_id => '1' }) + assert_equal '/projects/1', url_for(@routes, { :use_route => 'project', :controller => 'project', :action => 'index' }, { :project_id => '1' }) + + assert_equal '/clients', url_for(@routes, { :controller => 'projects', :action => 'index' }) + assert_equal '/clients?project_id=1', url_for(@routes, { :controller => 'projects', :action => 'index', :project_id => '1' }) + assert_equal '/clients', url_for(@routes, { :controller => 'projects', :action => 'index' }, { :project_id => '1' }) + assert_equal '/clients', url_for(@routes, { :action => 'index' }, { :controller => 'projects', :action => 'index', :project_id => '1' }) + + assert_equal '/comment/20', url_for(@routes, { :id => 20 }, { :controller => 'comments', :action => 'show' }) + assert_equal '/comment/20', url_for(@routes, { :controller => 'comments', :id => 20, :action => 'show' }) + assert_equal '/comments/boo', url_for(@routes, { :controller => 'comments', :action => 'boo' }) + + assert_equal '/ws/posts/show/1', url_for(@routes, { :controller => 'posts', :action => 'show', :id => '1', :ws => true }) + assert_equal '/ws/posts', url_for(@routes, { :controller => 'posts', :action => 'index', :ws => true }) + + assert_equal '/account', url_for(@routes, { :controller => 'account', :action => 'subscription' }) + assert_equal '/account/billing', url_for(@routes, { :controller => 'account', :action => 'billing' }) + + assert_equal '/pages/1/notes/show/1', url_for(@routes, { :page_id => '1', :controller => 'notes', :action => 'show', :id => '1' }) + assert_equal '/pages/1/notes/list', url_for(@routes, { :page_id => '1', :controller => 'notes', :action => 'list' }) + assert_equal '/pages/1/notes', url_for(@routes, { :page_id => '1', :controller => 'notes', :action => 'index' }) + assert_equal '/pages/1/notes', url_for(@routes, { :page_id => '1', :controller => 'notes' }) + assert_equal '/notes', url_for(@routes, { :page_id => nil, :controller => 'notes' }) + assert_equal '/notes', url_for(@routes, { :controller => 'notes' }) + assert_equal '/notes/print', url_for(@routes, { :controller => 'notes', :action => 'print' }) + assert_equal '/notes/print', url_for(@routes, {}, { :controller => 'notes', :action => 'print' }) + + assert_equal '/notes/index/1', url_for(@routes, { :controller => 'notes' }, { :controller => 'notes', :id => '1' }) + assert_equal '/notes/index/1', url_for(@routes, { :controller => 'notes' }, { :controller => 'notes', :id => '1', :foo => 'bar' }) + assert_equal '/notes/index/1', url_for(@routes, { :controller => 'notes' }, { :controller => 'notes', :id => '1' }) + assert_equal '/notes/index/1', url_for(@routes, { :action => 'index' }, { :controller => 'notes', :id => '1' }) + assert_equal '/notes/index/1', url_for(@routes, {}, { :controller => 'notes', :id => '1' }) + assert_equal '/notes/show/1', url_for(@routes, {}, { :controller => 'notes', :action => 'show', :id => '1' }) + assert_equal '/notes/index/1', url_for(@routes, { :controller => 'notes', :id => '1' }, { :foo => 'bar' }) + assert_equal '/posts', url_for(@routes, { :controller => 'posts' }, { :controller => 'notes', :action => 'show', :id => '1' }) + assert_equal '/notes/list', url_for(@routes, { :action => 'list' }, { :controller => 'notes', :action => 'show', :id => '1' }) + + assert_equal '/posts/ping', url_for(@routes, { :controller => 'posts', :action => 'ping' }) + assert_equal '/posts/show/1', url_for(@routes, { :controller => 'posts', :action => 'show', :id => '1' }) + assert_equal '/posts', url_for(@routes, { :controller => 'posts' }) + assert_equal '/posts', url_for(@routes, { :controller => 'posts', :action => 'index' }) + assert_equal '/posts', url_for(@routes, { :controller => 'posts' }, { :controller => 'posts', :action => 'index' }) + assert_equal '/posts/create', url_for(@routes, { :action => 'create' }, { :controller => 'posts' }) + assert_equal '/posts?foo=bar', url_for(@routes, { :controller => 'posts', :foo => 'bar' }) + assert_equal '/posts?foo%5B%5D=bar&foo%5B%5D=baz', url_for(@routes, { :controller => 'posts', :foo => ['bar', 'baz'] }) + assert_equal '/posts?page=2', url_for(@routes, { :controller => 'posts', :page => 2 }) + assert_equal '/posts?q%5Bfoo%5D%5Ba%5D=b', url_for(@routes, { :controller => 'posts', :q => { :foo => { :a => 'b'}} }) + + assert_equal '/news.rss', url_for(@routes, { :controller => 'news', :action => 'index', :format => 'rss' }) + + + assert_raise(ActionController::RoutingError) { url_for(@routes, { :action => 'index' }) } end def test_generate_extras diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 8f672c1149..75b674ec1a 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -1,6 +1,31 @@ require 'abstract_unit' class RequestTest < ActiveSupport::TestCase + + def url_for(options = {}) + options.reverse_merge!(:host => 'www.example.com') + ActionDispatch::Http::URL.url_for(options) + end + + test "url_for class method" do + e = assert_raise(ArgumentError) { url_for(:host => nil) } + assert_match(/Please provide the :host parameter/, e.message) + + assert_equal '/books', url_for(:only_path => true, :path => '/books') + + assert_equal 'http://www.example.com', url_for + assert_equal 'http://api.example.com', url_for(:subdomain => 'api') + assert_equal 'http://www.ror.com', url_for(:domain => 'ror.com') + assert_equal 'http://api.ror.co.uk', url_for(:host => 'www.ror.co.uk', :subdomain => 'api', :tld_length => 2) + assert_equal 'http://www.example.com:8080', url_for(:port => 8080) + assert_equal 'https://www.example.com', url_for(:protocol => 'https') + assert_equal 'http://www.example.com/docs', url_for(:path => '/docs') + assert_equal 'http://www.example.com#signup', url_for(:anchor => 'signup') + assert_equal 'http://www.example.com/', url_for(:trailing_slash => true) + assert_equal 'http://dhh:supersecret@www.example.com', url_for(:user => 'dhh', :password => 'supersecret') + assert_equal 'http://www.example.com?search=books', url_for(:params => { :search => 'books' }) + end + test "remote ip" do request = stub_request 'REMOTE_ADDR' => '1.2.3.4' assert_equal '1.2.3.4', request.remote_ip diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index bbd010ea6d..74420317c7 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -13,6 +13,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + class YoutubeFavoritesRedirector + def self.call(params, request) + "http://www.youtube.com/watch?v=#{params[:youtube_id]}" + end + end + stub_controllers do |routes| Routes = routes Routes.draw do @@ -54,6 +60,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match 'account/login', :to => redirect("/login") match 'secure', :to => redirect("/secure/login") + match 'mobile', :to => redirect(:subdomain => 'mobile') + match 'documentation', :to => redirect(:domain => 'example-documentation.com', :path => '') + match 'new_documentation', :to => redirect(:path => '/documentation/new') + match 'super_new_documentation', :to => redirect(:host => 'super-docs.com') + + match 'youtube_favorites/:youtube_id/:name', :to => redirect(YoutubeFavoritesRedirector) + constraints(lambda { |req| true }) do match 'account/overview' end @@ -667,6 +680,41 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_redirect_hash_with_subdomain + with_test_routes do + get '/mobile' + verify_redirect 'http://mobile.example.com/mobile' + end + end + + def test_redirect_hash_with_domain_and_path + with_test_routes do + get '/documentation' + verify_redirect 'http://www.example-documentation.com' + end + end + + def test_redirect_hash_with_path + with_test_routes do + get '/new_documentation' + verify_redirect 'http://www.example.com/documentation/new' + end + end + + def test_redirect_hash_with_host + with_test_routes do + get '/super_new_documentation?section=top' + verify_redirect 'http://super-docs.com/super_new_documentation?section=top' + end + end + + def test_redirect_class + with_test_routes do + get '/youtube_favorites/oHg5SJYRHA0/rick-rolld' + verify_redirect 'http://www.youtube.com/watch?v=oHg5SJYRHA0' + end + end + def test_openid with_test_routes do get '/openid/login' -- cgit v1.2.3 From a9b666b51d28b2e74da630c31327dee7cbe96d37 Mon Sep 17 00:00:00 2001 From: Krekoten' Marjan Date: Tue, 30 Nov 2010 22:48:19 +0800 Subject: Fix generation of wrong json string when field has multiple errors --- activemodel/lib/active_model/errors.rb | 10 ++++++++++ activemodel/test/cases/errors_test.rb | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 99f47f2cbe..01ca540d28 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -167,6 +167,16 @@ module ActiveModel def as_json(options=nil) self end + + def encode_json(encoder) + errors = [] + each_pair do |key, value| + value = value.first if value.size == 1 + errors << "#{encoder.encode(key.to_s)}:#{encoder.encode(value, false)}" + end + + "{#{errors * ','}}" + end # Adds +message+ to the error messages on +attribute+, which will be returned on a call to # on(attribute) for the same attribute. More than one error can be added to the same diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 79b45bb298..1958ed112c 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -61,5 +61,15 @@ class ErrorsTest < ActiveModel::TestCase assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a end + + test 'to_json should return valid json string' do + person = Person.new + person.errors.add(:name, "can not be blank") + person.errors.add(:name, "can not be nil") + + hash = ActiveSupport::OrderedHash[:name, ["can not be blank", "can not be nil"]] + + assert_equal person.errors.to_json, hash.to_json + end end -- cgit v1.2.3 From 1e26bda0959c313ce5c1816bf4958b542050e5e2 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Tue, 30 Nov 2010 17:55:33 +0100 Subject: Added documentation explaining the new additional supported syntaxes for the routing redirect method, a small changelog note, and two extra tests for path interpolation when using the hash option syntax. --- actionpack/CHANGELOG | 2 ++ .../lib/action_dispatch/routing/redirection.rb | 29 ++++++++++++++++++++++ actionpack/test/dispatch/routing_test.rb | 17 +++++++++++++ 3 files changed, 48 insertions(+) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 74d017cc3d..9b292cca69 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.1.0 (unreleased)* +* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused (check the documentation for examples). [Josh Kalderimis] + * Added config.action_controller.include_all_helpers. By default 'helper :all' is done in ActionController::Base, which includes all the helpers by default. Setting include_all_helpers to false will result in including only application_helper and helper corresponding to controller (like foo_helper for foo_controller). [Piotr Sarnacki] * Added a convenience idiom to generate HTML5 data-* attributes in tag helpers from a :data hash of options: diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index d9c9a400a7..804991ad5f 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -7,6 +7,35 @@ module ActionDispatch # Redirect any path to another path: # # match "/stories" => redirect("/posts") + # + # You can also use interpolation in the supplied redirect argument: + # + # match 'docs/:article', :to => redirect('/wiki/%{article}') + # + # Alternatively you can use one of the other syntaxes: + # + # The block version of redirect allows for the easy encapsulation of any logic associated with + # the redirect in question. Either the params and request are supplied as arguments, or just + # params, depending of how many arguments your block accepts. A string is required as a + # return value. + # + # match 'jokes/:number', :to => redirect do |params, request| + # path = (params[:number].to_i.even? ? "/wheres-the-beef" : "/i-love-lamp") + # "http://#{request.host_with_port}/#{path}" + # end + # + # The options version of redirect allows you to supply only the parts of the url which need + # to change, it also supports interpolation of the path similar to the first example. + # + # match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}') + # match 'stores/:name(*all)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{all}') + # + # Finally, an object which responds to call can be supplied to redirect, allowing you to reuse + # common redirect routes. The call method must accept two arguments, params and request, and return + # a string. + # + # match 'accounts/:name' => redirect(SubdomainRedirector.new('api')) + # def redirect(*args, &block) options = args.last.is_a?(Hash) ? args.pop : {} status = options.delete(:status) || 301 diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 74420317c7..4bf7880294 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -65,6 +65,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match 'new_documentation', :to => redirect(:path => '/documentation/new') match 'super_new_documentation', :to => redirect(:host => 'super-docs.com') + match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}') + match 'stores/:name(*rest)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{rest}') + match 'youtube_favorites/:youtube_id/:name', :to => redirect(YoutubeFavoritesRedirector) constraints(lambda { |req| true }) do @@ -708,6 +711,20 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_redirect_hash_path_substitution + with_test_routes do + get '/stores/iernest' + verify_redirect 'http://stores.example.com/iernest' + end + end + + def test_redirect_hash_path_substitution_with_catch_all + with_test_routes do + get '/stores/iernest/products' + verify_redirect 'http://stores.example.com/iernest/products' + end + end + def test_redirect_class with_test_routes do get '/youtube_favorites/oHg5SJYRHA0/rick-rolld' -- cgit v1.2.3 From b6848b6491b75d69c0e1ceaa6a66d86305124d24 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 12:05:02 -0800 Subject: cleaning up where_values_hash --- activerecord/lib/active_record/relation.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3b22be78cb..1ed42ffb95 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -319,13 +319,13 @@ module ActiveRecord end def where_values_hash - Hash[@where_values.find_all { |w| - w.respond_to?(:operator) && w.operator == :== && w.left.relation.name == table_name - }.map { |where| - [ - where.left.name, - where.right.respond_to?(:value) ? where.right.value : where.right - ] + equalities = @where_values.grep(Arel::Nodes::Equality).find_all { |node| + node.left.relation.name == table_name + } + + Hash[equalities.map { |where| + left, right = where.left, where.right + [ left.name, right.respond_to?(:value) ? right.value : right ] }] end -- cgit v1.2.3 From 8998aea4b751ba8d9a6362f8cc11db5cf46a71ee Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 13:31:00 -0800 Subject: making relationship merge cheaper --- activerecord/lib/active_record/relation/spawn_methods.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index a61a3bd41c..5acf3ec83a 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -3,10 +3,11 @@ require 'active_support/core_ext/object/blank' module ActiveRecord module SpawnMethods def merge(r) - merged_relation = clone - return merged_relation unless r + return self unless r return to_a & r if r.is_a?(Array) + merged_relation = clone + Relation::ASSOCIATION_METHODS.each do |method| value = r.send(:"#{method}_values") @@ -24,7 +25,7 @@ module ActiveRecord merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present? end - merged_relation = merged_relation.joins(r.joins_values) + merged_relation.joins_values += r.joins_values merged_wheres = @where_values + r.where_values -- cgit v1.2.3 From b7a9890d7765a0a6edc1161f58304386266bdf2e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 13:38:48 -0800 Subject: fix whitespace errors --- activerecord/test/cases/connection_pool_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index f0ec5c751c..2e18117895 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -38,9 +38,9 @@ module ActiveRecord assert_not_nil connection end end - + threads.each {|t| t.join} - + Thread.new do threads.each do |t| thread_ids = pool.instance_variable_get(:@reserved_connections).keys -- cgit v1.2.3 From bfc398cb7083c41becf6e4938c0a80a6659a1caa Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 14:10:55 -0800 Subject: adding a test for ActiveRecord::Relation --- activerecord/test/cases/relation_test.rb | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 activerecord/test/cases/relation_test.rb diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb new file mode 100644 index 0000000000..96856ac22b --- /dev/null +++ b/activerecord/test/cases/relation_test.rb @@ -0,0 +1,56 @@ +require "cases/helper" + +module ActiveRecord + class RelationTest < ActiveRecord::TestCase + def test_construction + relation = nil + assert_nothing_raised do + relation = Relation.new :a, :b + end + assert_equal :a, relation.klass + assert_equal :b, relation.table + assert !relation.loaded, 'relation is not loaded' + end + + def test_single_values + assert_equal [:limit, :offset, :lock, :readonly, :create_with, :from].sort, + Relation::SINGLE_VALUE_METHODS.sort + end + + def test_initialize_single_values + relation = Relation.new :a, :b + Relation::SINGLE_VALUE_METHODS.each do |method| + assert_nil relation.send("#{method}_value"), method.to_s + end + end + + def test_association_methods + assert_equal [:includes, :eager_load, :preload].sort, + Relation::ASSOCIATION_METHODS.sort + end + + def test_initialize_association_methods + relation = Relation.new :a, :b + Relation::ASSOCIATION_METHODS.each do |method| + assert_equal [], relation.send("#{method}_values"), method.to_s + end + end + + def test_multi_value_methods + assert_equal [:select, :group, :order, :joins, :where, :having, :bind].sort, + Relation::MULTI_VALUE_METHODS.sort + end + + def test_multi_value_initialize + relation = Relation.new :a, :b + Relation::MULTI_VALUE_METHODS.each do |method| + assert_equal [], relation.send("#{method}_values"), method.to_s + end + end + + def test_extensions + relation = Relation.new :a, :b + assert_equal [], relation.extensions + end + end +end -- cgit v1.2.3 From 6c32290bac460ce53ea2d29b50047248f9f0de92 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 14:29:35 -0800 Subject: testing Relation#table_name --- activerecord/lib/active_record/relation.rb | 4 ++++ activerecord/test/cases/relation_test.rb | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 1ed42ffb95..098a03b39b 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -356,6 +356,10 @@ module ActiveRecord to_a.inspect end + def table_name + @klass.table_name + end + protected def method_missing(method, *args, &block) diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 96856ac22b..3629f6f806 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -2,6 +2,9 @@ require "cases/helper" module ActiveRecord class RelationTest < ActiveRecord::TestCase + class FakeTable < Struct.new(:table_name) + end + def test_construction relation = nil assert_nothing_raised do @@ -52,5 +55,23 @@ module ActiveRecord relation = Relation.new :a, :b assert_equal [], relation.extensions end + + def test_where_values_hash + relation = Relation.new :a, :b + assert_equal({}, relation.where_values_hash) + + relation.where_values << :hello + assert_equal({}, relation.where_values_hash) + end + + def test_table_name_delegates_to_klass + relation = Relation.new FakeTable.new('foo'), :b + assert_equal 'foo', relation.table_name + end + + def test_scope_for_create + relation = Relation.new :a, :b + assert_equal({}, relation.scope_for_create) + end end end -- cgit v1.2.3 From 795dc3d15fee02c9ccc9da08c8fcee0830a4962a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 14:47:55 -0800 Subject: adding more tests surrounding where_values_hash --- activerecord/test/cases/relation_test.rb | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 3629f6f806..cc2c2c1446 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -1,8 +1,12 @@ require "cases/helper" +require 'models/post' +require 'models/comment' module ActiveRecord class RelationTest < ActiveRecord::TestCase - class FakeTable < Struct.new(:table_name) + fixtures :posts, :comments + + class FakeKlass < Struct.new(:table_name) end def test_construction @@ -56,7 +60,7 @@ module ActiveRecord assert_equal [], relation.extensions end - def test_where_values_hash + def test_empty_where_values_hash relation = Relation.new :a, :b assert_equal({}, relation.where_values_hash) @@ -64,8 +68,20 @@ module ActiveRecord assert_equal({}, relation.where_values_hash) end + def test_has_values + relation = Relation.new Post, Post.arel_table + relation.where_values << relation.table[:id].eq(10) + assert_equal({:id => 10}, relation.where_values_hash) + end + + def test_values_wrong_table + relation = Relation.new Post, Post.arel_table + relation.where_values << Comment.arel_table[:id].eq(10) + assert_equal({}, relation.where_values_hash) + end + def test_table_name_delegates_to_klass - relation = Relation.new FakeTable.new('foo'), :b + relation = Relation.new FakeKlass.new('foo'), :b assert_equal 'foo', relation.table_name end -- cgit v1.2.3 From 3ad0779e566067f26422a74f108d81ac458e4333 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 14:56:32 -0800 Subject: testing combined nodes are not traversed --- activerecord/test/cases/relation_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index cc2c2c1446..a9244ae65e 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -80,6 +80,15 @@ module ActiveRecord assert_equal({}, relation.where_values_hash) end + def test_tree_is_not_traversed + relation = Relation.new Post, Post.arel_table + left = relation.table[:id].eq(10) + right = relation.table[:id].eq(10) + combine = left.and right + relation.where_values << combine + assert_equal({}, relation.where_values_hash) + end + def test_table_name_delegates_to_klass relation = Relation.new FakeKlass.new('foo'), :b assert_equal 'foo', relation.table_name -- cgit v1.2.3 From c856e25b32f3c2ea198d6f4ac46276a15fd20d80 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 14:56:53 -0800 Subject: removing arel 1.0 compatibility --- activerecord/lib/active_record/relation.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 098a03b39b..c31a6e8feb 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -323,10 +323,7 @@ module ActiveRecord node.left.relation.name == table_name } - Hash[equalities.map { |where| - left, right = where.left, where.right - [ left.name, right.respond_to?(:value) ? right.value : right ] - }] + Hash[equalities.map { |where| [where.left.name, where.right] }] end def scope_for_create -- cgit v1.2.3 From dccf624b643398afdf1b0e048e57f04cb182c55e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 15:20:27 -0800 Subject: Ruby 1.8, how does it work? --- activerecord/lib/active_record/relation.rb | 2 +- activerecord/test/cases/relation_test.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index c31a6e8feb..4bc7f73013 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -329,7 +329,7 @@ module ActiveRecord def scope_for_create @scope_for_create ||= begin if @create_with_value - @create_with_value.reverse_merge(where_values_hash) + where_values_hash.merge @create_with_value else where_values_hash end diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index a9244ae65e..02de0398aa 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -20,8 +20,8 @@ module ActiveRecord end def test_single_values - assert_equal [:limit, :offset, :lock, :readonly, :create_with, :from].sort, - Relation::SINGLE_VALUE_METHODS.sort + assert_equal [:limit, :offset, :lock, :readonly, :create_with, :from].map(&:to_s).sort, + Relation::SINGLE_VALUE_METHODS.map(&:to_s).sort end def test_initialize_single_values @@ -32,8 +32,8 @@ module ActiveRecord end def test_association_methods - assert_equal [:includes, :eager_load, :preload].sort, - Relation::ASSOCIATION_METHODS.sort + assert_equal [:includes, :eager_load, :preload].map(&:to_s).sort, + Relation::ASSOCIATION_METHODS.map(&:to_s).sort end def test_initialize_association_methods @@ -44,8 +44,8 @@ module ActiveRecord end def test_multi_value_methods - assert_equal [:select, :group, :order, :joins, :where, :having, :bind].sort, - Relation::MULTI_VALUE_METHODS.sort + assert_equal [:select, :group, :order, :joins, :where, :having, :bind].map(&:to_s).sort, + Relation::MULTI_VALUE_METHODS.map(&:to_s).sort end def test_multi_value_initialize -- cgit v1.2.3 From 7ff980031bc49fe332d3b9ea8fbc6c2aae935db5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 15:39:49 -0800 Subject: scope_for_create always returns something --- activerecord/lib/active_record/base.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 6076aaf4c3..b972b193e4 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1830,7 +1830,9 @@ MSG def populate_with_current_scope_attributes if scope = self.class.send(:current_scoped_methods) create_with = scope.scope_for_create - create_with.each { |att,value| self.respond_to?(:"#{att}=") && self.send("#{att}=", value) } if create_with + create_with.each { |att,value| + respond_to?(:"#{att}=") && send("#{att}=", value) + } end end -- cgit v1.2.3 From b2e7d1e41ec1d40c35aeb201720dfec413c8f95a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 15:44:30 -0800 Subject: surrounding scope_for_create behavior --- activerecord/test/cases/relation_test.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 02de0398aa..31d2020acd 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -98,5 +98,19 @@ module ActiveRecord relation = Relation.new :a, :b assert_equal({}, relation.scope_for_create) end + + def test_create_with_value + relation = Relation.new Post, Post.arel_table + hash = { :hello => 'world' } + relation.create_with_value = hash + assert_equal hash, relation.scope_for_create + end + + def test_create_with_value_with_wheres + relation = Relation.new Post, Post.arel_table + relation.where_values << relation.table[:id].eq(10) + relation.create_with_value = {:hello => 'world'} + assert_equal({:hello => 'world', :id => 10}, relation.scope_for_create) + end end end -- cgit v1.2.3 From b293ab7c618f65e06371e9ae7b00fe2a65082209 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 15:45:32 -0800 Subject: making sure scope_for_create value is cached --- activerecord/test/cases/relation_test.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 31d2020acd..3cc31b3db0 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -112,5 +112,17 @@ module ActiveRecord relation.create_with_value = {:hello => 'world'} assert_equal({:hello => 'world', :id => 10}, relation.scope_for_create) end + + # FIXME: is this really wanted or expected behavior? + def test_scope_for_create_is_cached + relation = Relation.new Post, Post.arel_table + assert_equal({}, relation.scope_for_create) + + relation.where_values << relation.table[:id].eq(10) + assert_equal({}, relation.scope_for_create) + + relation.create_with_value = {:hello => 'world'} + assert_equal({}, relation.scope_for_create) + end end end -- cgit v1.2.3 From ae6033331fe78cf5b3af04e1f6f930b98ddab66f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 15:48:15 -0800 Subject: shorten up scope_for_create --- activerecord/lib/active_record/relation.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 4bc7f73013..8d0d99a125 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -327,13 +327,7 @@ module ActiveRecord end def scope_for_create - @scope_for_create ||= begin - if @create_with_value - where_values_hash.merge @create_with_value - else - where_values_hash - end - end + @scope_for_create ||= where_values_hash.merge(@create_with_value || {}) end def eager_loading? -- cgit v1.2.3 From fe522f79c772c915efc88a8476fa69a503955967 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 15:53:04 -0800 Subject: we already know it is already an array, so we do not need to make really really sure it is an array --- activerecord/lib/active_record/relation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 8d0d99a125..45fc3c20c2 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -339,7 +339,7 @@ module ActiveRecord when Relation other.to_sql == to_sql when Array - to_a == other.to_a + to_a == other end end -- cgit v1.2.3 From 22b01c52c7cccdaa7f4fbed2a66c38a4c5416c6e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 16:10:49 -0800 Subject: isolating eager_loading? method --- activerecord/test/cases/relation_test.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 3cc31b3db0..7bdbd773b6 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -124,5 +124,16 @@ module ActiveRecord relation.create_with_value = {:hello => 'world'} assert_equal({}, relation.scope_for_create) end + + def test_empty_eager_loading? + relation = Relation.new :a, :b + assert !relation.eager_loading? + end + + def test_eager_load_values + relation = Relation.new :a, :b + relation.eager_load_values << :b + assert relation.eager_loading? + end end end -- cgit v1.2.3 From 39f5fc06cb5661b93275e3f7bf98e63b98b57550 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 16:42:21 -0800 Subject: just call join_sql against the manager object --- activerecord/lib/active_record/relation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 45fc3c20c2..bebdd1292c 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -371,7 +371,7 @@ module ActiveRecord def references_eager_loaded_tables? # always convert table names to downcase as in Oracle quoted table names are in uppercase - joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq + joined_tables = (tables_in_string(arel.join_sql) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq (tables_in_string(to_sql) - joined_tables).any? end -- cgit v1.2.3 From f1758525f940eb6c4dd4178784db2ba66316083d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 16:51:03 -0800 Subject: reduce method calls on arel --- activerecord/lib/active_record/relation/calculations.rb | 2 +- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index c8adaddfca..013b878293 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -166,7 +166,7 @@ module ActiveRecord if operation == "count" column_name ||= (select_for_count || :all) - if arel.joins(arel) =~ /LEFT OUTER/i + if arel.join_sql =~ /LEFT OUTER/i distinct = true column_name = @klass.primary_key if column_name == :all end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 23ae0b4325..1da7411589 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -196,7 +196,7 @@ module ActiveRecord def construct_relation_for_association_calculations including = (@eager_load_values + @includes_values).uniq - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.joins(arel)) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.join_sql) relation = except(:includes, :eager_load, :preload) apply_join_dependency(relation, join_dependency) end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0a4c119849..1eb7f04bdf 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -180,7 +180,7 @@ module ActiveRecord arel.join(join) end - arel.joins(arel) + arel.join_sql end def build_arel -- cgit v1.2.3 From 7408b6ec842c87644460430827b8ce27e43ccfc7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 17:29:19 -0800 Subject: just grep the AST for OuterJoin nodes rather than converting the tree to SQL --- activerecord/lib/active_record/relation/calculations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 013b878293..fd45bb24dd 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -166,7 +166,7 @@ module ActiveRecord if operation == "count" column_name ||= (select_for_count || :all) - if arel.join_sql =~ /LEFT OUTER/i + unless arel.ast.grep(Arel::Nodes::OuterJoin).empty? distinct = true column_name = @klass.primary_key if column_name == :all end -- cgit v1.2.3 From d51854a6be66ea39973e8880726be3a17dfbd7b8 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 17:42:17 -0800 Subject: stop using splat args when we do not need them --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1eb7f04bdf..30cd4f4477 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -162,7 +162,7 @@ module ActiveRecord @arel ||= build_arel end - def custom_join_sql(*joins) + def custom_join_sql(joins) arel = table.select_manager joins.each do |join| @@ -254,7 +254,7 @@ module ActiveRecord stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) non_association_joins = (joins - association_joins - stashed_association_joins) - custom_joins = custom_join_sql(*non_association_joins) + custom_joins = custom_join_sql(non_association_joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins) -- cgit v1.2.3 From 10b1887a719588c1252906169f9d3d12ee1387c6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 17:48:10 -0800 Subject: JoinBase initialize can be deleted --- .../active_record/associations/class_methods/join_dependency.rb | 5 +++-- .../associations/class_methods/join_dependency/join_base.rb | 8 -------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 6ab7bd0b06..c578845878 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -9,7 +9,8 @@ module ActiveRecord attr_reader :join_parts, :reflections, :table_aliases def initialize(base, associations, joins) - @join_parts = [JoinBase.new(base, joins)] + @table_joins = joins || '' + @join_parts = [JoinBase.new(base)] @associations = {} @reflections = [] @table_aliases = Hash.new(0) @@ -45,7 +46,7 @@ module ActiveRecord def count_aliases_from_table_joins(name) # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase - join_sql = join_base.table_joins.to_s.downcase + join_sql = @table_joins.downcase join_sql.blank? ? 0 : # Table names join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb index ed05003f66..97003c1457 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb @@ -3,14 +3,6 @@ module ActiveRecord module ClassMethods class JoinDependency # :nodoc: class JoinBase < JoinPart # :nodoc: - # Extra joins provided when the JoinDependency was created - attr_reader :table_joins - - def initialize(active_record, joins = nil) - super(active_record) - @table_joins = joins - end - def ==(other) other.class == self.class && other.active_record == active_record -- cgit v1.2.3 From 542cb5c327f92d3f6ae6159a54e86949441f095e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 18:46:56 -0800 Subject: fix warnings, stop using global variables --- activerecord/lib/active_record/relation/query_methods.rb | 6 ++---- activerecord/test/cases/nested_attributes_test.rb | 3 +-- activerecord/test/models/pet.rb | 6 +++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 30cd4f4477..0483950db7 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -243,12 +243,10 @@ module ActiveRecord end def build_joins(relation, joins) - association_joins = [] - joins = joins.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq - joins.each do |join| - association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join) + association_joins = joins.find_all do |join| + [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join) end stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index fb6a239545..ffcc7a081a 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -848,13 +848,12 @@ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase def test_attr_accessor_of_child_should_be_value_provided_during_update_attributes @owner = owners(:ashley) @pet1 = pets(:chew) - assert_equal nil, $current_user attributes = {:pets_attributes => { "1"=> { :id => @pet1.id, :name => "Foo2", :current_user => "John", :_destroy=>true }}} @owner.update_attributes(attributes) - assert_equal 'John', $after_destroy_callback_output + assert_equal 'John', Pet.after_destroy_output end end diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb index 570db4c8d5..113826756a 100644 --- a/activerecord/test/models/pet.rb +++ b/activerecord/test/models/pet.rb @@ -6,8 +6,12 @@ class Pet < ActiveRecord::Base belongs_to :owner, :touch => true has_many :toys + class << self + attr_accessor :after_destroy_output + end + after_destroy do |record| - $after_destroy_callback_output = record.current_user + Pet.after_destroy_output = record.current_user end end -- cgit v1.2.3 From 831a2342c69f1d602aca9e894bffa4f6dbf614ed Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 30 Nov 2010 20:04:31 -0800 Subject: just use alias --- actionpack/lib/action_dispatch/testing/integration.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 8fe74c3c80..5c6416a19e 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -235,9 +235,7 @@ module ActionDispatch # Set the host name to use in the next request. # # session.host! "www.example.com" - def host!(name) - @host = name - end + alias :host! :host= private def _mock_session -- cgit v1.2.3 From b640e3f5107b68e62967cba20e5f627170d98460 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 1 Dec 2010 13:48:33 +1100 Subject: Mention that the documentation of create_file can be found on rdoc.info ino the generators guide --- railties/guides/source/generators.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index 99c34ed30f..909c79f3cb 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -45,6 +45,8 @@ class InitializerGenerator < Rails::Generators::Base end +NOTE: +create_file+ is a method provided by +Thor::Actions+ and the documentation for it and its brethren can be found at "rdoc.info":http://rdoc.info/github/wycats/thor/master/Thor/Actions. + Our new generator is quite simple: it inherits from +Rails::Generators::Base+ and has one method definition. Each public method in the generator is executed when a generator is invoked. Finally, we invoke the +create_file+ method that will create a file at the given destination with the given content. If you are familiar with the Rails Application Templates API, you'll feel right at home with the new generators API. To invoke our new generator, we just need to do: -- cgit v1.2.3 From 04ec2c6d6c9fe92655bb171521f2167d28fd66fb Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 1 Dec 2010 15:22:42 +1100 Subject: Begin covering application templates in the generators guide --- railties/guides/source/generators.textile | 42 ++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index 909c79f3cb..ea45f7c31c 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -1,4 +1,4 @@ -h2. Creating and Customizing Rails Generators +h2. Creating and Customizing Rails Generators & Templates Rails generators are an essential tool if you plan to improve your workflow. With this guide you will learn how to create generators and customize existing ones. @@ -10,6 +10,7 @@ In this guide you will: * Customize your scaffold by creating new generators * Customize your scaffold by changing generator templates * Learn how to use fallbacks to avoid overwriting a huge set of generators +* Learn how to create an application template endprologue. @@ -363,8 +364,47 @@ $ rails generate scaffold Comment body:text Fallbacks allow your generators to have a single responsibility, increasing code reuse and reducing the amount of duplication. +h3. Application templates + +Now that you've seen how generators can be used _inside_ an application, did you know they can also be used to _generate_ applications too? This kind of generator is referred as a "template". + + + gem("rspec-rails", :group => "test") + gem("cucumber-rails", :group => "test") + + if yes?("Would you like to install Devise?") + gem("devise") + generate("devise:install") + model_name = ask("What would you like the user model to be called? [user]") + model_name = "user" if model_name.blank? + generate("devise", model_name) + end + + +In the above template we specify that the application relies on the +rspec-rails+ and +cucumber-rails+ gem so these two will be added to the +test+ group in the +Gemfile+. Then we pose a question to the user about whether or not they would like to install Devise. If the user replies "y" or "yes" to this question, then the template will add Devise to the +Gemfile+ outside of any group and then runs the +devise:install+ generator. This template then takes the users input and runs the +devise+ generator, with the user's answer from the last question being passed to this generator. + +Imagine that this template was in a file called +template.rb+. We can use it to modify the outcome of the +rails new+ command by using the +-m+ option and passing in the filename: + + + rails new thud -m template.rb + + +This command will generate the +Thud+ application, and then apply the template to the generated output. + +Templates don't have to be stored on the local system, the +-m+ option also supports online templates: + + + rails new thud -m https://gist.github.com/722911 + + +Whilst the remainder of this guide doesn't cover how to generate the most awesome template known to man, it will take you through the methods available at your disposal so that you can develop it yourself. + + + h3. Changelog +* December 1, 2010: Addition of Rails application templates by "Ryan Bigg" + * August 23, 2010: Edit pass by "Xavier Noria":credits.html#fxn * April 30, 2010: Reviewed by José Valim -- cgit v1.2.3 From f3bf63d6111e9b305819cb9d19720971b3b91e6d Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 1 Dec 2010 15:53:36 +1100 Subject: Covering generator methods provided by Rails in the generators guide --- railties/guides/source/generators.textile | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index ea45f7c31c..c57171f189 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -134,6 +134,8 @@ $ rails generate initializer core_extensions We can see that now a initializer named core_extensions was created at +config/initializers/core_extensions.rb+ with the contents of our template. That means that +copy_file+ copied a file in our source root to the destination path we gave. The method +file_name+ is automatically created when we inherit from +Rails::Generators::NamedBase+. +The methods that are available for generators are covered in the "final section":#generator_methods of this guide. + h3. Generators Lookup When you run +rails generate initializer core_extensions+ Rails requires these files in turn until one is found: @@ -397,13 +399,35 @@ Templates don't have to be stored on the local system, the +-m+ option also supp rails new thud -m https://gist.github.com/722911 -Whilst the remainder of this guide doesn't cover how to generate the most awesome template known to man, it will take you through the methods available at your disposal so that you can develop it yourself. +Whilst the final section of this guide doesn't cover how to generate the most awesome template known to man, it will take you through the methods available at your disposal so that you can develop it yourself. These same methods are also available for generators. + +h3. Generator methods + +The following are methods available for both generators and templates for Rails. + +NOTE: Methods provided by Thor are not covered this guide and can be found in "Thor's documentation":http://rdoc.info/github/wycats/thor/master/Thor/Actions.html + +h4. +plugin+ + ++plugin+ will install a plugin into the current application. + + + plugin("dynamic-form", :git => "git://github.com/rails/dynamic-form.git") + + +Available options are: +* +:git+ - Takes the path to the git repository where this plugin can be found. +* +:branch+ - The name of the branch of the git repository where the plugin is found. +* +:submodule+ - Set to +true+ for the plugin to be installed as a submodule. Defaults to +false+. +* +:svn+ - Takes the path to the svn repository where this plugin can be found. +* +:revision+ - The revision of the plugin in an SVN repository. h3. Changelog -* December 1, 2010: Addition of Rails application templates by "Ryan Bigg" +* December 1, 2010: Documenting the available methods and options for generators and templates by "Ryan Bigg":http://ryanbigg.com +* December 1, 2010: Addition of Rails application templates by "Ryan Bigg":http://ryanbigg.com * August 23, 2010: Edit pass by "Xavier Noria":credits.html#fxn -- cgit v1.2.3 From 74e178880c56bae86958e6e39be1c35532399bb3 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 1 Dec 2010 16:15:49 +1100 Subject: For templates' gem method, :env is deprecated, use :group instead --- railties/lib/rails/generators/actions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 378c07cb0e..d7a86a5c40 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -44,7 +44,7 @@ module Rails # # ==== Example # - # gem "rspec", :env => :test + # gem "rspec", :group => :test # gem "technoweenie-restful-authentication", :lib => "restful-authentication", :source => "http://gems.github.com/" # gem "rails", "3.0", :git => "git://github.com/rails/rails" # -- cgit v1.2.3 From 77a228785ccc6832d8e30438380aea7ad9706a70 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 1 Dec 2010 17:18:41 +1100 Subject: Add gem method documentation to the generators guide --- railties/guides/source/generators.textile | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index c57171f189..915270ff46 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -423,6 +423,34 @@ Available options are: * +:svn+ - Takes the path to the svn repository where this plugin can be found. * +:revision+ - The revision of the plugin in an SVN repository. +h4. +gem+ + +Specifies a gem dependency of the application. + + + gem("rspec", :group => "test", :version => "2.1.0") + gem("devise", "1.1.5") + + +Available options are: + +* +:group+ - The group in the +Gemfile+ where this gem should go. +* +:version+ - The version string of the gem you want to use. Can also be specified as the second argument to the method. +* +:git+ - The URL to the git repository for this gem. + +Any additional options passed to this method are put on the end of the line: + + + gem("devise", :git => "git://github.com/plataformatec/devise", :branch => "master") + + +The above code will put the following line into +Gemfile+: + + + gem "devise", :git => "git://github.com/plataformatec/devise", :branch => "master" + + + h3. Changelog -- cgit v1.2.3 From 3a3c812c282b2972632ac7699ff7ed58de3782ec Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 1 Dec 2010 20:25:39 +1100 Subject: Finish documenting generator / template methods for the generators guide --- railties/guides/source/generators.textile | 161 ++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index 915270ff46..a0d744333f 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -451,6 +451,167 @@ The above code will put the following line into +Gemfile+: +h4. +add_source+ + +Adds a specified source to +Gemfile+: + + + add_source "http://gems.github.com" + + +h4. +application+ + +Adds a line to +config/application.rb+ directly after the application class definition. + + + application "config.asset_host = 'http://example.com'" + + +This method can also take a block: + + + application do + "config.asset_host = 'http://example.com'" + end + end + + +Available options are: + +* +:env+ - Specify an environment for this configuration option. If you wish to use this option with the block syntax the recommended syntax is as follows: + + + application(nil, :env => "development") do + "config.asset_host = 'http://localhost:3000'" + end + + +h4. +git+ + +Runs the specified git command: + + + git :init + git :add => "." + git :commit => "-m First commit!" + git :add => "onefile.rb", :rm => "badfile.cxx" + + +The values of the hash here being the arguments or options passed to the specific git command. As per the final example shown here, multiple git commands can be specified at a time, but the order of their running is not guaranteed to be the same as the order that they were specified in. + +h4. +vendor+ + +Places a file into +vendor+ which contains the specified code. + + + vendor("sekrit.rb", '#top secret stuff') + + +This method also takes a block: + + + vendor("seeds.rb") do + "puts 'in ur app, seeding ur database'" + end + + +h4. +lib+ + +Places a file into +lib+ which contains the specified code. + + + lib("special.rb", 'p Rails.root') + + +This method also takes a block: + + + lib("super_special.rb") do + puts "Super special!" + end + + +h4. +rakefile+ + +Creates a Rake file in the +lib/tasks+ directory of the application. + + + rakefile("test.rake", 'hello there') + + +This method also takes a block: + + + rakefile("test.rake") do + %Q{ + task :rock => :environment do + puts "Rockin'" + end + } + end + + +h4. +initializer+ + +Creates an initializer in the +config/initializers+ directory of the application: + + + initializer("begin.rb", "puts 'this is the beginning'") + + +This method also takes a block: + + + initializer("begin.rb") do + puts "Almost done!" + end + + +h4. +generate+ + +Runs the specified generator where the first argument is the generator name and the remaining arguments are passed directly to the generator. + + + generate("scaffold", "forums title:string description:text") + + + +h4. +rake+ + +Runs the specified Rake task. + + + rake("db:migrate") + + +Available options are: + +* +:env+ - Specifies the environment in which to run this rake task. +* +:sudo+ - Whether or not to run this task using +sudo+. Defaults to +false+. + +h4. +capify!+ + +Runs the +capify+ command from Capistrano at the root of the application which generates Capistrano configuration. + + + capify! + + +h4. +route+ + +Adds text to the +config/routes.rb+ file: + + + route("resources :people") + + +h4. +readme+ + +Output the contents of a file in the template's +source_path+, usually a README. + + + readme("README") + h3. Changelog -- cgit v1.2.3 From ba63c0a9b58691adeca645977828ee918107208d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 1 Dec 2010 11:22:48 +0100 Subject: Deprecate handler_class_for_extension as template handlers can be any Ruby object, not necessarily a class. --- actionpack/lib/action_view/renderer/template_renderer.rb | 2 +- actionpack/lib/action_view/template/handlers.rb | 8 +++++++- actionpack/lib/action_view/template/resolver.rb | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index 6912acee31..ece3f621b6 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -45,7 +45,7 @@ module ActionView elsif options.key?(:file) with_fallbacks { find_template(options[:file], options[:prefix], false, keys) } elsif options.key?(:inline) - handler = Template.handler_class_for_extension(options[:type] || "erb") + handler = Template.handler_for_extension(options[:type] || "erb") Template.new(options[:inline], "inline template", handler, :locals => keys) elsif options.key?(:template) options[:template].respond_to?(:render) ? diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb index 60347e2a95..4438199497 100644 --- a/actionpack/lib/action_view/template/handlers.rb +++ b/actionpack/lib/action_view/template/handlers.rb @@ -44,7 +44,13 @@ module ActionView #:nodoc: end def handler_class_for_extension(extension) - (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers + ActiveSupport::Deprecation.warn "handler_class_for_extension is deprecated. " << + "Please use handler_for_extension instead", caller + handler_for_extension(extension) + end + + def handler_for_extension(extension) + registered_template_handler(extension) || @@default_template_handlers end end end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 7707dbcf98..9f15661669 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -129,7 +129,7 @@ module ActionView def extract_handler_and_format(path, default_formats) pieces = File.basename(path).split(".") pieces.shift - handler = Template.handler_class_for_extension(pieces.pop) + handler = Template.handler_for_extension(pieces.pop) format = pieces.last && Mime[pieces.last] [handler, format] end -- cgit v1.2.3 From 1ec126dd37b52ecf7c0c24a842fc87836d8f2e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 1 Dec 2010 11:42:30 +0100 Subject: Revert "Fix generation of wrong json string when field has multiple errors" This reverts commit a9b666b51d28b2e74da630c31327dee7cbe96d37. --- activemodel/lib/active_model/errors.rb | 10 ---------- activemodel/test/cases/errors_test.rb | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 01ca540d28..99f47f2cbe 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -167,16 +167,6 @@ module ActiveModel def as_json(options=nil) self end - - def encode_json(encoder) - errors = [] - each_pair do |key, value| - value = value.first if value.size == 1 - errors << "#{encoder.encode(key.to_s)}:#{encoder.encode(value, false)}" - end - - "{#{errors * ','}}" - end # Adds +message+ to the error messages on +attribute+, which will be returned on a call to # on(attribute) for the same attribute. More than one error can be added to the same diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 1958ed112c..79b45bb298 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -61,15 +61,5 @@ class ErrorsTest < ActiveModel::TestCase assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a end - - test 'to_json should return valid json string' do - person = Person.new - person.errors.add(:name, "can not be blank") - person.errors.add(:name, "can not be nil") - - hash = ActiveSupport::OrderedHash[:name, ["can not be blank", "can not be nil"]] - - assert_equal person.errors.to_json, hash.to_json - end end -- cgit v1.2.3 From 7148b933c4865a5140187d7ed792fd6df9b860a4 Mon Sep 17 00:00:00 2001 From: Thilo Utke Date: Sun, 28 Nov 2010 14:36:40 +0100 Subject: ActiveModel::Errors.to_hash returns plain OrderedHash and used in to_json serialization to properly handle multiple errors per attribute [#5615 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activemodel/lib/active_model/errors.rb | 8 +++++++- activemodel/test/cases/errors_test.rb | 5 +++++ .../test/cases/serializeration/json_serialization_test.rb | 15 ++++++++------- activemodel/test/cases/validations_test.rb | 4 ++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 99f47f2cbe..fdca852c7a 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -165,7 +165,13 @@ module ActiveModel # Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object. def as_json(options=nil) - self + to_hash + end + + def to_hash + hash = ActiveSupport::OrderedHash.new + each { |k, v| (hash[k] ||= []) << v } + hash end # Adds +message+ to the error messages on +attribute+, which will be returned on a call to diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 79b45bb298..27821c333b 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -62,4 +62,9 @@ class ErrorsTest < ActiveModel::TestCase end + test 'to_hash should return an ordered hash' do + person = Person.new + person.errors.add(:name, "can not be blank") + assert_instance_of ActiveSupport::OrderedHash, person.errors.to_hash + end end diff --git a/activemodel/test/cases/serializeration/json_serialization_test.rb b/activemodel/test/cases/serializeration/json_serialization_test.rb index 20d123ef0b..500a5c575f 100644 --- a/activemodel/test/cases/serializeration/json_serialization_test.rb +++ b/activemodel/test/cases/serializeration/json_serialization_test.rb @@ -6,6 +6,7 @@ require 'active_support/core_ext/object/instance_variables' class Contact extend ActiveModel::Naming include ActiveModel::Serializers::JSON + include ActiveModel::Validations def attributes instance_values @@ -105,15 +106,15 @@ class JsonSerializationTest < ActiveModel::TestCase end test "should return OrderedHash for errors" do - car = Automobile.new - - # run the validation - car.valid? + contact = Contact.new + contact.errors.add :name, "can't be blank" + contact.errors.add :name, "is too short (minimum is 2 characters)" + contact.errors.add :age, "must be 16 or over" hash = ActiveSupport::OrderedHash.new - hash[:make] = "can't be blank" - hash[:model] = "is too short (minimum is 2 characters)" - assert_equal hash.to_json, car.errors.to_json + hash[:name] = ["can't be blank", "is too short (minimum is 2 characters)"] + hash[:age] = ["must be 16 or over"] + assert_equal hash.to_json, contact.errors.to_json end test "serializable_hash should not modify options passed in argument" do diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 4024002aaa..55b477dd10 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -174,8 +174,8 @@ class ValidationsTest < ActiveModel::TestCase assert_match %r{Content can't be blank}, xml hash = ActiveSupport::OrderedHash.new - hash[:title] = "can't be blank" - hash[:content] = "can't be blank" + hash[:title] = ["can't be blank"] + hash[:content] = ["can't be blank"] assert_equal t.errors.to_json, hash.to_json end -- cgit v1.2.3 From c283cdd63cafdb04784cfcc5094da41c9268c20c Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Mon, 15 Mar 2010 00:53:57 -0700 Subject: Add migrated_at column to schema_migrations table. --- .../abstract/schema_statements.rb | 10 +++++- activerecord/test/cases/migration_test.rb | 36 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 4e770c37da..730b1c7425 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -422,9 +422,17 @@ module ActiveRecord def initialize_schema_migrations_table sm_table = ActiveRecord::Migrator.schema_migrations_table_name - unless table_exists?(sm_table) + if table_exists?(sm_table) + cols = columns(sm_table).collect { |col| col.name } + unless cols.include?("migrated_at") + add_column sm_table, :migrated_at, :datetime + update "UPDATE #{quote_table_name(sm_table)} SET migrated_at = '#{quoted_date(Time.now)}' WHERE migrated_at IS NULL" + change_column sm_table, :migrated_at, :datetime, :null => false + end + else create_table(sm_table, :id => false) do |schema_migrations_table| schema_migrations_table.column :version, :string, :null => false + schema_migrations_table.column :migrated_at, :datetime, :null => false end add_index sm_table, :version, :unique => true, :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}" diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 3037d73a1b..55a24a94f3 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -27,22 +27,46 @@ if ActiveRecord::Base.connection.supports_migrations? end class MigrationTableAndIndexTest < ActiveRecord::TestCase - def test_add_schema_info_respects_prefix_and_suffix - conn = ActiveRecord::Base.connection + def setup + @conn = ActiveRecord::Base.connection + @conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if @conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) + end - conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) + def test_add_schema_migrations_respects_prefix_and_suffix # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters ActiveRecord::Base.table_name_prefix = 'p_' ActiveRecord::Base.table_name_suffix = '_s' - conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) + @conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if @conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) - conn.initialize_schema_migrations_table + @conn.initialize_schema_migrations_table - assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name] + assert_equal "p_unique_schema_migrations_s", @conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name] ensure ActiveRecord::Base.table_name_prefix = "" ActiveRecord::Base.table_name_suffix = "" end + + def test_schema_migrations_columns + @conn.initialize_schema_migrations_table + + columns = @conn.columns(ActiveRecord::Migrator.schema_migrations_table_name).collect(&:name) + %w[version migrated_at].each { |col| assert columns.include?(col) } + end + + def test_add_migrated_at_to_exisiting_schema_migrations + sm_table = ActiveRecord::Migrator.schema_migrations_table_name + @conn.create_table(sm_table, :id => false) do |schema_migrations_table| + schema_migrations_table.column :version, :string, :null => false + end + @conn.insert "INSERT INTO #{@conn.quote_table_name(sm_table)} (version) VALUES (100)" + @conn.insert "INSERT INTO #{@conn.quote_table_name(sm_table)} (version) VALUES (200)" + + @conn.initialize_schema_migrations_table + + m_ats = @conn.select_values("SELECT migrated_at FROM #{@conn.quote_table_name(sm_table)}") + assert_equal 2, m_ats.length + assert_equal 2, m_ats.compact.length + end end class MigrationTest < ActiveRecord::TestCase -- cgit v1.2.3 From 4e4e9ad48a222ac94a904c62ae684c609bd8e177 Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Tue, 16 Mar 2010 23:02:03 -0700 Subject: record migration timestamp when migrations run --- activerecord/lib/active_record/migration.rb | 17 +++++----- activerecord/test/cases/migration_test.rb | 49 ++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index f6321f1499..2cf2f4acd0 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -628,7 +628,7 @@ module ActiveRecord raise UnknownMigrationVersionError.new(@target_version) if target.nil? unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i)) target.migrate(@direction) - record_version_state_after_migrating(target.version) + record_version_state_after_migrating(target) end end @@ -664,7 +664,7 @@ module ActiveRecord begin ddl_transaction do migration.migrate(@direction) - record_version_state_after_migrating(migration.version) + record_version_state_after_migrating(migration) end rescue => e canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : "" @@ -690,16 +690,19 @@ module ActiveRecord end private - def record_version_state_after_migrating(version) + def record_version_state_after_migrating(target) table = Arel::Table.new(self.class.schema_migrations_table_name) @migrated_versions ||= [] if down? - @migrated_versions.delete(version) - table.where(table["version"].eq(version.to_s)).delete + @migrated_versions.delete(target.version) + table.where(table["version"].eq(target.version.to_s)).delete else - @migrated_versions.push(version).sort! - table.insert table["version"] => version.to_s + @migrated_versions.push(target.version).sort! + table.insert( + table["version"] => target.version.to_s, + table["migrated_at"] => Time.now + ) end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 55a24a94f3..e76097d42d 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -445,6 +445,43 @@ if ActiveRecord::Base.connection.supports_migrations? Person.connection.drop_table table_name rescue nil end + def test_create_table_with_custom_sequence_name + return unless current_adapter? :OracleAdapter + + # table name is 29 chars, the standard sequence name will + # be 33 chars and fail + assert_raise(ActiveRecord::StatementInvalid) do + begin + Person.connection.create_table :table_with_name_thats_just_ok do |t| + t.column :foo, :string, :null => false + end + ensure + Person.connection.drop_table :table_with_name_thats_just_ok rescue nil + end + end + + # should be all good w/ a custom sequence name + assert_nothing_raised do + begin + Person.connection.create_table :table_with_name_thats_just_ok, + :sequence_name => 'suitably_short_seq' do |t| + t.column :foo, :string, :null => false + end + + Person.connection.execute("select suitably_short_seq.nextval from dual") + + ensure + Person.connection.drop_table :table_with_name_thats_just_ok, + :sequence_name => 'suitably_short_seq' rescue nil + end + end + + # confirm the custom sequence got dropped + assert_raise(ActiveRecord::StatementInvalid) do + Person.connection.execute("select suitably_short_seq.nextval from dual") + end + end + # Sybase, and SQLite3 will not allow you to add a NOT NULL # column to a table without a default value. unless current_adapter?(:SybaseAdapter, :SQLite3Adapter) @@ -1462,6 +1499,17 @@ if ActiveRecord::Base.connection.supports_migrations? ActiveRecord::Base.table_name_suffix = "" end + def test_migration_row_includes_timestamp + conn = ActiveRecord::Base.connection + sm_table = ActiveRecord::Migrator.schema_migrations_table_name + + ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid") + + conn.select_all("SELECT * FROM #{conn.quote_table_name(sm_table)}").each do |row| + assert_match /^2\d\d\d-/, row["migrated_at"], "missing migrated_at" + end + end + def test_proper_table_name assert_equal "table", ActiveRecord::Migrator.proper_table_name('table') assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table) @@ -2117,4 +2165,3 @@ if ActiveRecord::Base.connection.supports_migrations? end end end - -- cgit v1.2.3 From 7139aa878ceea8dd17a60955cd4d07f5f68980d9 Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Sat, 20 Mar 2010 18:26:12 -0700 Subject: name in schema_migrations, migrations in schema dump --- .../abstract/schema_statements.rb | 4 ++++ activerecord/lib/active_record/migration.rb | 1 + activerecord/lib/active_record/schema.rb | 17 ++++++++++++---- activerecord/lib/active_record/schema_dumper.rb | 10 ++++++++++ activerecord/test/cases/ar_schema_test.rb | 23 ++++++++++++++++++++++ activerecord/test/cases/migration_test.rb | 20 +++++++++++-------- activerecord/test/cases/schema_dumper_test.rb | 15 ++++++++++++++ 7 files changed, 78 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 730b1c7425..ccb6fe3be2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -429,9 +429,13 @@ module ActiveRecord update "UPDATE #{quote_table_name(sm_table)} SET migrated_at = '#{quoted_date(Time.now)}' WHERE migrated_at IS NULL" change_column sm_table, :migrated_at, :datetime, :null => false end + unless cols.include?("name") + add_column sm_table, :name, :string, :null => false, :default => "" + end else create_table(sm_table, :id => false) do |schema_migrations_table| schema_migrations_table.column :version, :string, :null => false + schema_migrations_table.column :name, :string, :null => false, :default => "" schema_migrations_table.column :migrated_at, :datetime, :null => false end add_index sm_table, :version, :unique => true, diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 2cf2f4acd0..d2ebf656cb 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -701,6 +701,7 @@ module ActiveRecord @migrated_versions.push(target.version).sort! table.insert( table["version"] => target.version.to_s, + table["name"] => File.basename(target.filename,'.rb').gsub(/^\d+_/,''), table["migrated_at"] => Time.now ) end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index c6bb5c1961..c9b5539e4f 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -49,10 +49,19 @@ module ActiveRecord schema = new schema.instance_eval(&block) - unless info[:version].blank? - initialize_schema_migrations_table - assume_migrated_upto_version(info[:version], schema.migrations_path) - end + initialize_schema_migrations_table + assume_migrated_upto_version(info[:version], schema.migrations_path) unless info[:version].blank? + end + + def self.migration(version, name="", options={}) + name, options = "", name if name.is_a?(Hash) + + table = Arel::Table.new(ActiveRecord::Migrator.schema_migrations_table_name) + table.insert( + table["version"] => version, + table["name"] => name, + table["migrated_at"] => Time.now + ) end end end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index e30b481fe1..a53dce0cf4 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -24,6 +24,7 @@ module ActiveRecord def dump(stream) header(stream) + migrations(stream) tables(stream) trailer(stream) stream @@ -62,6 +63,15 @@ HEADER stream.puts "end" end + def migrations(stream) + rows = @connection.select_all("SELECT * FROM #{@connection.quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)}") + rows.each do |migration| + line = %Q(migration "#{migration['version']}") + line << %Q(, "#{migration['name']}") unless migration['name'].blank? + stream.puts line + end + end + def tables(stream) @connection.tables.sort.each do |tbl| next if ['schema_migrations', ignore_tables].flatten.any? do |ignored| diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 588adc38e3..e7f3907ff5 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -39,4 +39,27 @@ if ActiveRecord::Base.connection.supports_migrations? end end + class ActiveRecordSchemaMigrationsTest < ActiveRecordSchemaTest + def setup + super + @sm_table = ActiveRecord::Migrator.schema_migrations_table_name + @connection.execute "DELETE FROM #{@connection.quote_table_name(@sm_table)}" + end + + def test_migration_adds_row_to_migrations_table + ActiveRecord::Schema.migration("123001") + ActiveRecord::Schema.migration("123002", "add_magic_power_to_unicorns") + rows = @connection.select_all("SELECT * FROM #{@connection.quote_table_name(@sm_table)}") + assert_equal 2, rows.length + + assert_equal "123001", rows[0]["version"] + assert_equal "", rows[0]["name"] + assert_match /^2\d\d\d-/, rows[0]["migrated_at"] + + assert_equal "123002", rows[1]["version"] + assert_equal "add_magic_power_to_unicorns", rows[1]["name"] + assert_match /^2\d\d\d-/, rows[1]["migrated_at"] + end + end + end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index e76097d42d..497391e713 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -50,10 +50,10 @@ if ActiveRecord::Base.connection.supports_migrations? @conn.initialize_schema_migrations_table columns = @conn.columns(ActiveRecord::Migrator.schema_migrations_table_name).collect(&:name) - %w[version migrated_at].each { |col| assert columns.include?(col) } + %w[version name migrated_at].each { |col| assert columns.include?(col) } end - def test_add_migrated_at_to_exisiting_schema_migrations + def test_add_name_and_migrated_at_to_exisiting_schema_migrations sm_table = ActiveRecord::Migrator.schema_migrations_table_name @conn.create_table(sm_table, :id => false) do |schema_migrations_table| schema_migrations_table.column :version, :string, :null => false @@ -63,9 +63,9 @@ if ActiveRecord::Base.connection.supports_migrations? @conn.initialize_schema_migrations_table - m_ats = @conn.select_values("SELECT migrated_at FROM #{@conn.quote_table_name(sm_table)}") - assert_equal 2, m_ats.length - assert_equal 2, m_ats.compact.length + rows = @conn.select_all("SELECT * FROM #{@conn.quote_table_name(sm_table)}") + assert rows[0].has_key?("name") + assert rows[0].has_key?("migrated_at") end end @@ -1499,15 +1499,19 @@ if ActiveRecord::Base.connection.supports_migrations? ActiveRecord::Base.table_name_suffix = "" end - def test_migration_row_includes_timestamp + def test_migration_row_includes_name_and_timestamp conn = ActiveRecord::Base.connection sm_table = ActiveRecord::Migrator.schema_migrations_table_name ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid") - conn.select_all("SELECT * FROM #{conn.quote_table_name(sm_table)}").each do |row| - assert_match /^2\d\d\d-/, row["migrated_at"], "missing migrated_at" + rows = conn.select_all("SELECT * FROM #{conn.quote_table_name(sm_table)}") + rows.each do |row| + assert_match( /^2\d\d\d-/, row["migrated_at"], "missing migrated_at" ) end + assert_equal "people_have_last_names", rows[0]["name"] + assert_equal "we_need_reminders", rows[1]["name"] + assert_equal "innocent_jointable", rows[2]["name"] end def test_proper_table_name diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 9b2c7c00df..fdcf1b5cd4 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -17,6 +17,21 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_no_match %r{create_table "schema_migrations"}, output end + def test_schema_dump_includes_migrations + conn = ActiveRecord::Base.connection + sm_table = ActiveRecord::Migrator.schema_migrations_table_name + conn.execute "DELETE FROM #{conn.quote_table_name(sm_table)}" + conn.remove_column "people", "last_name" rescue nil + conn.drop_table "reminders" rescue nil + conn.drop_table "people_reminders" rescue nil + ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid") + + output = standard_dump + assert_match %r{migration "1", "people_have_last_names"}, output + assert_match %r{migration "2", "we_need_reminders"}, output + assert_match %r{migration "3", "innocent_jointable"}, output + end + def test_schema_dump_excludes_sqlite_sequence output = standard_dump assert_no_match %r{create_table "sqlite_sequence"}, output -- cgit v1.2.3 From b07c2c0fd3130bb69cf8d846e46762a7c3972107 Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Sat, 20 Mar 2010 19:46:17 -0700 Subject: clear schema_migrations in Schema.define --- activerecord/lib/active_record/schema.rb | 4 +++- activerecord/lib/active_record/schema_dumper.rb | 8 +++----- activerecord/test/cases/ar_schema_test.rb | 11 +++++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index c9b5539e4f..4b846f2e27 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -46,10 +46,12 @@ module ActiveRecord # ... # end def self.define(info={}, &block) + Base.connection.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) + initialize_schema_migrations_table + schema = new schema.instance_eval(&block) - initialize_schema_migrations_table assume_migrated_upto_version(info[:version], schema.migrations_path) unless info[:version].blank? end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index a53dce0cf4..794a354917 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -35,12 +35,9 @@ module ActiveRecord def initialize(connection) @connection = connection @types = @connection.native_database_types - @version = Migrator::current_version rescue nil end def header(stream) - define_params = @version ? ":version => #{@version}" : "" - stream.puts <
Date: Wed, 1 Dec 2010 11:56:21 -0800 Subject: tests mostly pass adjust to work with instance-based migations migrated_at can't be null why must people have last names? it's killing me! --- .../active_record/connection_adapters/abstract/schema_statements.rb | 4 ++-- activerecord/lib/active_record/schema.rb | 2 +- activerecord/test/cases/ar_schema_test.rb | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index ccb6fe3be2..de7d358df9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -464,7 +464,7 @@ module ActiveRecord end unless migrated.include?(version) - execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" + execute "INSERT INTO #{sm_table} (version,migrated_at) VALUES ('#{version}','#{Time.now.to_s(:db)}')" end inserted = Set.new @@ -472,7 +472,7 @@ module ActiveRecord if inserted.include?(v) raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict." elsif v < version - execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" + execute "INSERT INTO #{sm_table} (version,migrated_at) VALUES ('#{v}','#{Time.now.to_s(:db)}')" inserted << v end end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index 4b846f2e27..9a47fd3b84 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -55,7 +55,7 @@ module ActiveRecord assume_migrated_upto_version(info[:version], schema.migrations_path) unless info[:version].blank? end - def self.migration(version, name="", options={}) + def migration(version, name="", options={}) name, options = "", name if name.is_a?(Hash) table = Arel::Table.new(ActiveRecord::Migrator.schema_migrations_table_name) diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 930a57330d..28f219e213 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -47,8 +47,9 @@ if ActiveRecord::Base.connection.supports_migrations? end def test_migration_adds_row_to_migrations_table - ActiveRecord::Schema.migration("123001") - ActiveRecord::Schema.migration("123002", "add_magic_power_to_unicorns") + schema = ActiveRecord::Schema.new + schema.migration("123001") + schema.migration("123002", "add_magic_power_to_unicorns") rows = @connection.select_all("SELECT * FROM #{@connection.quote_table_name(@sm_table)}") assert_equal 2, rows.length -- cgit v1.2.3 From 949eb91d0e771c14218f34f14cb4f00c09af60db Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 08:36:25 +1100 Subject: Add missing colons on the end of before_initialize and after_initialize documentation in configuring guide --- railties/guides/source/configuring.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index fcf7ba0ae5..a3e6f0b9be 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -332,8 +332,8 @@ Rails has 4 initialization events which can be hooked into (listed in order that * +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens. * +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ enviroment. -* +before_initialize+ This is run directly before the initialization process of the application occurs. -* +after_initialize+ Run directly after the initialization of the application, but before the application initializers are run. +* +before_initialize+: This is run directly before the initialization process of the application occurs. +* +after_initialize+: Run directly after the initialization of the application, but before the application initializers are run. WARNING: Some parts of your application, notably observers and routing, are not yet set up at the point where the +after_initialize+ block is called. -- cgit v1.2.3 From c266a6bf0f219f211979b67396750fc9071b0391 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 1 Dec 2010 14:33:59 -0800 Subject: fixing tests and warnings --- activerecord/test/cases/ar_schema_test.rb | 4 ++-- activerecord/test/cases/schema_dumper_test.rb | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 28f219e213..e007c12c33 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -55,11 +55,11 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "123001", rows[0]["version"] assert_equal "", rows[0]["name"] - assert_match /^2\d\d\d-/, rows[0]["migrated_at"] + assert_match(/^2\d\d\d-/, rows[0]["migrated_at"]) assert_equal "123002", rows[1]["version"] assert_equal "add_magic_power_to_unicorns", rows[1]["name"] - assert_match /^2\d\d\d-/, rows[1]["migrated_at"] + assert_match(/^2\d\d\d-/, rows[1]["migrated_at"]) end def test_define_clears_schema_migrations diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index fdcf1b5cd4..7914ee0a6f 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -18,6 +18,10 @@ class SchemaDumperTest < ActiveRecord::TestCase end def test_schema_dump_includes_migrations + $".delete_if do |fname| + fname == (MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb") + end + conn = ActiveRecord::Base.connection sm_table = ActiveRecord::Migrator.schema_migrations_table_name conn.execute "DELETE FROM #{conn.quote_table_name(sm_table)}" -- cgit v1.2.3 From 754493c790a10aa94a2bef831237e6e7bdba30ce Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 1 Dec 2010 14:35:57 -0800 Subject: updating CHANGELOG --- activerecord/CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index f46db909ba..e6c6883fc7 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,9 @@ *Rails 3.1.0 (unreleased)* +* The `schema_migrations` table now contains a column `name` which stores the +name of the migration that was executed, and `migrated_at` which stores the date +when the migration was executed. (Thanks Josh Susser!) + * ActiveRecord::Base#dup and ActiveRecord::Base#clone semantics have changed to closer match normal Ruby dup and clone semantics. -- cgit v1.2.3 From 7d2179608c21f1a36a3d0392f251b1ef7a2cf377 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 1 Dec 2010 14:59:17 -0800 Subject: not all databases return dates as strings --- activerecord/test/cases/ar_schema_test.rb | 4 ++-- activerecord/test/cases/migration_test.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index e007c12c33..cab27167b7 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -55,11 +55,11 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "123001", rows[0]["version"] assert_equal "", rows[0]["name"] - assert_match(/^2\d\d\d-/, rows[0]["migrated_at"]) + assert_match(/^2\d\d\d-/, rows[0]["migrated_at"].to_s) assert_equal "123002", rows[1]["version"] assert_equal "add_magic_power_to_unicorns", rows[1]["name"] - assert_match(/^2\d\d\d-/, rows[1]["migrated_at"]) + assert_match(/^2\d\d\d-/, rows[1]["migrated_at"].to_s) end def test_define_clears_schema_migrations diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 497391e713..37b2fea275 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1507,7 +1507,7 @@ if ActiveRecord::Base.connection.supports_migrations? rows = conn.select_all("SELECT * FROM #{conn.quote_table_name(sm_table)}") rows.each do |row| - assert_match( /^2\d\d\d-/, row["migrated_at"], "missing migrated_at" ) + assert_match(/^2\d\d\d-/, row["migrated_at"].to_s, "missing migrated_at") end assert_equal "people_have_last_names", rows[0]["name"] assert_equal "we_need_reminders", rows[1]["name"] -- cgit v1.2.3 From 4232454289ec12f8b21a22f764848263acd7af26 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 1 Dec 2010 15:07:44 -0800 Subject: make sure we have a value in migrated_at --- activerecord/test/cases/migration_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 37b2fea275..eed35e83f1 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1507,7 +1507,7 @@ if ActiveRecord::Base.connection.supports_migrations? rows = conn.select_all("SELECT * FROM #{conn.quote_table_name(sm_table)}") rows.each do |row| - assert_match(/^2\d\d\d-/, row["migrated_at"].to_s, "missing migrated_at") + assert_not_nil(row["migrated_at"], "missing migrated_at") end assert_equal "people_have_last_names", rows[0]["name"] assert_equal "we_need_reminders", rows[1]["name"] -- cgit v1.2.3 From 85a5318c2b03bd4a9b5e7ce096207a3a9f052419 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 1 Dec 2010 15:14:10 -0800 Subject: should not to_s the possibly nil value --- activerecord/test/cases/ar_schema_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index cab27167b7..df51a6e4ba 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -55,11 +55,11 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "123001", rows[0]["version"] assert_equal "", rows[0]["name"] - assert_match(/^2\d\d\d-/, rows[0]["migrated_at"].to_s) + assert_not_nil(rows[0]["migrated_at"]) assert_equal "123002", rows[1]["version"] assert_equal "add_magic_power_to_unicorns", rows[1]["name"] - assert_match(/^2\d\d\d-/, rows[1]["migrated_at"].to_s) + assert_not_nil(rows[1]["migrated_at"]) end def test_define_clears_schema_migrations -- cgit v1.2.3 From c15c14563e6604aab232336b16e14f1f235cc19d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 1 Dec 2010 15:14:56 -0800 Subject: removing duplicate test --- activerecord/test/cases/migration_test.rb | 37 ------------------------------- 1 file changed, 37 deletions(-) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index eed35e83f1..b698a5a3cf 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -445,43 +445,6 @@ if ActiveRecord::Base.connection.supports_migrations? Person.connection.drop_table table_name rescue nil end - def test_create_table_with_custom_sequence_name - return unless current_adapter? :OracleAdapter - - # table name is 29 chars, the standard sequence name will - # be 33 chars and fail - assert_raise(ActiveRecord::StatementInvalid) do - begin - Person.connection.create_table :table_with_name_thats_just_ok do |t| - t.column :foo, :string, :null => false - end - ensure - Person.connection.drop_table :table_with_name_thats_just_ok rescue nil - end - end - - # should be all good w/ a custom sequence name - assert_nothing_raised do - begin - Person.connection.create_table :table_with_name_thats_just_ok, - :sequence_name => 'suitably_short_seq' do |t| - t.column :foo, :string, :null => false - end - - Person.connection.execute("select suitably_short_seq.nextval from dual") - - ensure - Person.connection.drop_table :table_with_name_thats_just_ok, - :sequence_name => 'suitably_short_seq' rescue nil - end - end - - # confirm the custom sequence got dropped - assert_raise(ActiveRecord::StatementInvalid) do - Person.connection.execute("select suitably_short_seq.nextval from dual") - end - end - # Sybase, and SQLite3 will not allow you to add a NOT NULL # column to a table without a default value. unless current_adapter?(:SybaseAdapter, :SQLite3Adapter) -- cgit v1.2.3 From 3ec212e3c9c815e9993cbea409c60774ea75cd30 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 1 Dec 2010 17:08:01 -0800 Subject: rolling out migrated_at until I can fix the build --- activerecord/CHANGELOG | 4 -- .../abstract/schema_statements.rb | 18 ++------ activerecord/lib/active_record/migration.rb | 18 +++----- activerecord/lib/active_record/schema.rb | 19 ++------ activerecord/lib/active_record/schema_dumper.rb | 16 ++----- activerecord/test/cases/ar_schema_test.rb | 35 --------------- activerecord/test/cases/migration_test.rb | 52 +++------------------- activerecord/test/cases/schema_dumper_test.rb | 19 -------- 8 files changed, 25 insertions(+), 156 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index e6c6883fc7..f46db909ba 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,9 +1,5 @@ *Rails 3.1.0 (unreleased)* -* The `schema_migrations` table now contains a column `name` which stores the -name of the migration that was executed, and `migrated_at` which stores the date -when the migration was executed. (Thanks Josh Susser!) - * ActiveRecord::Base#dup and ActiveRecord::Base#clone semantics have changed to closer match normal Ruby dup and clone semantics. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index de7d358df9..4e770c37da 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -422,21 +422,9 @@ module ActiveRecord def initialize_schema_migrations_table sm_table = ActiveRecord::Migrator.schema_migrations_table_name - if table_exists?(sm_table) - cols = columns(sm_table).collect { |col| col.name } - unless cols.include?("migrated_at") - add_column sm_table, :migrated_at, :datetime - update "UPDATE #{quote_table_name(sm_table)} SET migrated_at = '#{quoted_date(Time.now)}' WHERE migrated_at IS NULL" - change_column sm_table, :migrated_at, :datetime, :null => false - end - unless cols.include?("name") - add_column sm_table, :name, :string, :null => false, :default => "" - end - else + unless table_exists?(sm_table) create_table(sm_table, :id => false) do |schema_migrations_table| schema_migrations_table.column :version, :string, :null => false - schema_migrations_table.column :name, :string, :null => false, :default => "" - schema_migrations_table.column :migrated_at, :datetime, :null => false end add_index sm_table, :version, :unique => true, :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}" @@ -464,7 +452,7 @@ module ActiveRecord end unless migrated.include?(version) - execute "INSERT INTO #{sm_table} (version,migrated_at) VALUES ('#{version}','#{Time.now.to_s(:db)}')" + execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" end inserted = Set.new @@ -472,7 +460,7 @@ module ActiveRecord if inserted.include?(v) raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict." elsif v < version - execute "INSERT INTO #{sm_table} (version,migrated_at) VALUES ('#{v}','#{Time.now.to_s(:db)}')" + execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" inserted << v end end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index d2ebf656cb..f6321f1499 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -628,7 +628,7 @@ module ActiveRecord raise UnknownMigrationVersionError.new(@target_version) if target.nil? unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i)) target.migrate(@direction) - record_version_state_after_migrating(target) + record_version_state_after_migrating(target.version) end end @@ -664,7 +664,7 @@ module ActiveRecord begin ddl_transaction do migration.migrate(@direction) - record_version_state_after_migrating(migration) + record_version_state_after_migrating(migration.version) end rescue => e canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : "" @@ -690,20 +690,16 @@ module ActiveRecord end private - def record_version_state_after_migrating(target) + def record_version_state_after_migrating(version) table = Arel::Table.new(self.class.schema_migrations_table_name) @migrated_versions ||= [] if down? - @migrated_versions.delete(target.version) - table.where(table["version"].eq(target.version.to_s)).delete + @migrated_versions.delete(version) + table.where(table["version"].eq(version.to_s)).delete else - @migrated_versions.push(target.version).sort! - table.insert( - table["version"] => target.version.to_s, - table["name"] => File.basename(target.filename,'.rb').gsub(/^\d+_/,''), - table["migrated_at"] => Time.now - ) + @migrated_versions.push(version).sort! + table.insert table["version"] => version.to_s end end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index 9a47fd3b84..c6bb5c1961 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -46,24 +46,13 @@ module ActiveRecord # ... # end def self.define(info={}, &block) - Base.connection.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) - initialize_schema_migrations_table - schema = new schema.instance_eval(&block) - assume_migrated_upto_version(info[:version], schema.migrations_path) unless info[:version].blank? - end - - def migration(version, name="", options={}) - name, options = "", name if name.is_a?(Hash) - - table = Arel::Table.new(ActiveRecord::Migrator.schema_migrations_table_name) - table.insert( - table["version"] => version, - table["name"] => name, - table["migrated_at"] => Time.now - ) + unless info[:version].blank? + initialize_schema_migrations_table + assume_migrated_upto_version(info[:version], schema.migrations_path) + end end end end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 794a354917..e30b481fe1 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -24,7 +24,6 @@ module ActiveRecord def dump(stream) header(stream) - migrations(stream) tables(stream) trailer(stream) stream @@ -35,9 +34,12 @@ module ActiveRecord def initialize(connection) @connection = connection @types = @connection.native_database_types + @version = Migrator::current_version rescue nil end def header(stream) + define_params = @version ? ":version => #{@version}" : "" + stream.puts <
false) do |schema_migrations_table| - schema_migrations_table.column :version, :string, :null => false - end - @conn.insert "INSERT INTO #{@conn.quote_table_name(sm_table)} (version) VALUES (100)" - @conn.insert "INSERT INTO #{@conn.quote_table_name(sm_table)} (version) VALUES (200)" - - @conn.initialize_schema_migrations_table - - rows = @conn.select_all("SELECT * FROM #{@conn.quote_table_name(sm_table)}") - assert rows[0].has_key?("name") - assert rows[0].has_key?("migrated_at") - end end class MigrationTest < ActiveRecord::TestCase @@ -1462,21 +1438,6 @@ if ActiveRecord::Base.connection.supports_migrations? ActiveRecord::Base.table_name_suffix = "" end - def test_migration_row_includes_name_and_timestamp - conn = ActiveRecord::Base.connection - sm_table = ActiveRecord::Migrator.schema_migrations_table_name - - ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid") - - rows = conn.select_all("SELECT * FROM #{conn.quote_table_name(sm_table)}") - rows.each do |row| - assert_not_nil(row["migrated_at"], "missing migrated_at") - end - assert_equal "people_have_last_names", rows[0]["name"] - assert_equal "we_need_reminders", rows[1]["name"] - assert_equal "innocent_jointable", rows[2]["name"] - end - def test_proper_table_name assert_equal "table", ActiveRecord::Migrator.proper_table_name('table') assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table) @@ -2132,3 +2093,4 @@ if ActiveRecord::Base.connection.supports_migrations? end end end + diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 7914ee0a6f..9b2c7c00df 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -17,25 +17,6 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_no_match %r{create_table "schema_migrations"}, output end - def test_schema_dump_includes_migrations - $".delete_if do |fname| - fname == (MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb") - end - - conn = ActiveRecord::Base.connection - sm_table = ActiveRecord::Migrator.schema_migrations_table_name - conn.execute "DELETE FROM #{conn.quote_table_name(sm_table)}" - conn.remove_column "people", "last_name" rescue nil - conn.drop_table "reminders" rescue nil - conn.drop_table "people_reminders" rescue nil - ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid") - - output = standard_dump - assert_match %r{migration "1", "people_have_last_names"}, output - assert_match %r{migration "2", "we_need_reminders"}, output - assert_match %r{migration "3", "innocent_jointable"}, output - end - def test_schema_dump_excludes_sqlite_sequence output = standard_dump assert_no_match %r{create_table "sqlite_sequence"}, output -- cgit v1.2.3 From 0afebd5b31bdc6edc1399c77153e65f5ea156fe1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 1 Dec 2010 18:00:24 -0800 Subject: no need for self --- .../associations/class_methods/join_dependency/join_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 3db80d341f..4dd11a7366 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -52,7 +52,7 @@ module ActiveRecord def find_parent_in(other_join_dependency) other_join_dependency.join_parts.detect do |join_part| - self.parent == join_part + parent == join_part end end -- cgit v1.2.3 From 697f734455753ad23ba719539bf961c6f1bc45d2 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 13:29:17 +1100 Subject: Associations guide: Add note that you must use the build_ prefix to build associations of the belongs_to and has_one type --- railties/guides/source/association_basics.textile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile index 14bbe907f3..2043daa4ed 100644 --- a/railties/guides/source/association_basics.textile +++ b/railties/guides/source/association_basics.textile @@ -550,6 +550,8 @@ build_customer create_customer +NOTE: When creating a new +belongs_to+ or +has_one+ association you must use the +build_+ prefix to build the association, rather than the +association.build+ method that would be used for +has_many+ or +has_and_belongs_to_many+ associations. + h6(#belongs_to-association). association(force_reload = false) The association method returns the associated object, if any. If no associated object is found, it returns +nil+. @@ -817,6 +819,8 @@ build_account create_account +NOTE: When creating a new +has_one+ or +belongs_to+ association you must use the +build_+ prefix to build the association, rather than the +association.build+ method that would be used for +has_many+ or +has_and_belongs_to_many+ associations. + h6(#has_one-association). association(force_reload = false) The association method returns the associated object, if any. If no associated object is found, it returns +nil+. -- cgit v1.2.3 From bde7d2351edf558ff5c2a3fb8d2822d5f8a2ae47 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 13:31:11 +1100 Subject: Association guide: change wording to say that build is for initialization, create is for creating --- railties/guides/source/association_basics.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile index 2043daa4ed..62abc40c81 100644 --- a/railties/guides/source/association_basics.textile +++ b/railties/guides/source/association_basics.textile @@ -550,7 +550,7 @@ build_customer create_customer -NOTE: When creating a new +belongs_to+ or +has_one+ association you must use the +build_+ prefix to build the association, rather than the +association.build+ method that would be used for +has_many+ or +has_and_belongs_to_many+ associations. +NOTE: When initializing a new +has_one+ or +belongs_to+ association you must use the +build_+ prefix to build the association, rather than the +association.build+ method that would be used for +has_many+ or +has_and_belongs_to_many+ associations. To create one, use the +create_+ prefix. h6(#belongs_to-association). association(force_reload = false) @@ -819,7 +819,7 @@ build_account create_account -NOTE: When creating a new +has_one+ or +belongs_to+ association you must use the +build_+ prefix to build the association, rather than the +association.build+ method that would be used for +has_many+ or +has_and_belongs_to_many+ associations. +NOTE: When initializing a new +has_one+ or +belongs_to+ association you must use the +build_+ prefix to build the association, rather than the +association.build+ method that would be used for +has_many+ or +has_and_belongs_to_many+ associations. To create one, use the +create_+ prefix. h6(#has_one-association). association(force_reload = false) -- cgit v1.2.3 From 32026b5867182dccbeeefb36728da4b0ce7b8b99 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 15:22:25 +1100 Subject: Add mention of config.to_prepare in configuring guide --- railties/guides/source/configuring.textile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index a3e6f0b9be..2482e64f70 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -331,8 +331,9 @@ h3. Initialization events Rails has 4 initialization events which can be hooked into (listed in order that they are ran): * +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens. -* +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ enviroment. * +before_initialize+: This is run directly before the initialization process of the application occurs. +* +to_prepare+: Run after the initializers are ran for all Railties, but before eager loading and the middleware stack is built. +* +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ enviroment. * +after_initialize+: Run directly after the initialization of the application, but before the application initializers are run. WARNING: Some parts of your application, notably observers and routing, are not yet set up at the point where the +after_initialize+ block is called. -- cgit v1.2.3 From 9cbdbd59d1b16772b55891cdb1da975855bd90c4 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 15:48:00 +1100 Subject: Config guide: Rails has *5* initialization events --- railties/guides/source/configuring.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 2482e64f70..f3ac3257c9 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -328,7 +328,7 @@ There are a few configuration options available in Active Support: h3. Initialization events -Rails has 4 initialization events which can be hooked into (listed in order that they are ran): +Rails has 5 initialization events which can be hooked into (listed in order that they are ran): * +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens. * +before_initialize+: This is run directly before the initialization process of the application occurs. -- cgit v1.2.3 From a3a50a0336cbb86fd77c3a793c98f52c55f3da1c Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 17:12:59 +1100 Subject: Configuring guide: Adding mention of the initializer method. --- railties/guides/source/configuring.textile | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index f3ac3257c9..710faa6585 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -332,13 +332,33 @@ Rails has 5 initialization events which can be hooked into (listed in order that * +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens. * +before_initialize+: This is run directly before the initialization process of the application occurs. -* +to_prepare+: Run after the initializers are ran for all Railties, but before eager loading and the middleware stack is built. +* +to_prepare+: Run after the initializers are ran for all Railties (including the application itself), but before eager loading and the middleware stack is built. * +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ enviroment. * +after_initialize+: Run directly after the initialization of the application, but before the application initializers are run. + WARNING: Some parts of your application, notably observers and routing, are not yet set up at the point where the +after_initialize+ block is called. -After loading the framework and any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of Ruby code stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded. +Rails has several initializers that run on startup that are all defined by using the +initializer+ method from +Rails::Railtie+. Here's an example of the +initialize_whiny_nils+ initializer from Active Support: + + + initializer "active_support.initialize_whiny_nils" do |app| + require 'active_support/whiny_nil' if app.config.whiny_nils + end + + + +The +initializer+ method takes three arguments with the first being the name for the initializer and the second being an options hash (not shown here) and the third being a block. The +:before+ key in the options hash can be specified to specify which initializer this new initializer must run before, and the +:after+ key will specify which initializer to run this initializer _after_. + +Initializers defined using the +initializer+ method will be ran in the order they are defined in, with the exception of ones that use the +:before+ or +:after+ methods. + +WARNING: You may put your initializer before or after any other initializer in the chain, as long as it is logical. Say you have 4 initializers called "one" through "four" (defined in that order) and you define "four" to go _before_ "four" but _after_ "three", that just isn't logical and Rails will not be able to determine your initializer order. + +The block's argument of the +initialize+ is the instance of the application itself, and so we can access the configuration on it by using the +config+ method as this initializer does. + +h4. Initializer files + +After loading the framework and any gems and plugins in your application, Rails turns to loading initialization code from +config/initializers+. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks and plugins are loaded. NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the +initializers+ folder on down. -- cgit v1.2.3 From 6349f5caaed1e20477296d54f0c1850cc358323e Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 17:19:32 +1100 Subject: Move Rails Environment settings to above the initialization events in the config guide --- railties/guides/source/configuring.textile | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 710faa6585..934080d60f 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -326,6 +326,19 @@ There are a few configuration options available in Active Support: * +ActiveSupport::Logger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+. + +h3. Rails Environment Settings + +Some parts of Rails can also be configured externally by supplying environment variables. The following environment variables are recognized by various parts of Rails: + +* +ENV['RAILS_ENV']+ defines the Rails environment (production, development, test, and so on) that Rails will run under. + +* +ENV['RAILS_RELATIVE_URL_ROOT']+ is used by the routing code to recognize URLs when you deploy your application to a subdirectory. + +* +ENV["RAILS_ASSET_ID"]+ will override the default cache-busting timestamps that Rails generates for downloadable assets. + +* +ENV["RAILS_CACHE_ID"]+ and +ENV["RAILS_APP_VERSION"]+ are used to generate expanded cache keys in Rails' caching code. This allows you to have multiple separate caches from the same application. + h3. Initialization events Rails has 5 initialization events which can be hooked into (listed in order that they are ran): @@ -364,18 +377,6 @@ NOTE: You can use subfolders to organize your initializers if you like, because TIP: If you have any ordering dependency in your initializers, you can control the load order by naming. For example, +01_critical.rb+ will be loaded before +02_normal.rb+. -h3. Rails Environment Settings - -Some parts of Rails can also be configured externally by supplying environment variables. The following environment variables are recognized by various parts of Rails: - -* +ENV['RAILS_ENV']+ defines the Rails environment (production, development, test, and so on) that Rails will run under. - -* +ENV['RAILS_RELATIVE_URL_ROOT']+ is used by the routing code to recognize URLs when you deploy your application to a subdirectory. - -* +ENV["RAILS_ASSET_ID"]+ will override the default cache-busting timestamps that Rails generates for downloadable assets. - -* +ENV["RAILS_CACHE_ID"]+ and +ENV["RAILS_APP_VERSION"]+ are used to generate expanded cache keys in Rails' caching code. This allows you to have multiple separate caches from the same application. - h3. Changelog * November 26, 2010: Removed all config settings not available in Rails 3 (Ryan Bigg) -- cgit v1.2.3 From 3dfcbe8d2c20e3a44805c1294459babfa8ede0db Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 17:22:00 +1100 Subject: Config guide: separate the initialization events and initializer method documentation --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 934080d60f..4f7ac60b87 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -352,6 +352,8 @@ Rails has 5 initialization events which can be hooked into (listed in order that WARNING: Some parts of your application, notably observers and routing, are not yet set up at the point where the +after_initialize+ block is called. +h4. +Rails::Railtie#initializer+ + Rails has several initializers that run on startup that are all defined by using the +initializer+ method from +Rails::Railtie+. Here's an example of the +initialize_whiny_nils+ initializer from Active Support: -- cgit v1.2.3 From e8ee2ba6cbbbc892a9db53420b8f0451a00388b0 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 2 Dec 2010 17:28:48 +1100 Subject: Config guide: Add config.active_support.bare --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 4f7ac60b87..7e40cad040 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -316,6 +316,8 @@ h4. Configuring Active Support There are a few configuration options available in Active Support: +* +config.active_support.bare+ enables or disables the loading of +active_support/all+ when booting Rails. Defaults to +nil+, which means +active_support/all+ is loaded. + * +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to +true+. * +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to +false+. -- cgit v1.2.3 From 96eec090dfd50326146b2f690408fefec50c5111 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Thu, 2 Dec 2010 15:36:05 +1300 Subject: Work around a strange piece of Syck behaviour where it checks Model#respond_to? before initializing the object. Things like YAML.load(YAML.dump(@post)) won't work without this. --- activerecord/lib/active_record/attribute_methods.rb | 2 +- activerecord/test/cases/attribute_methods_test.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 67f70c434e..4f4a0a5fee 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -54,7 +54,7 @@ module ActiveRecord protected def attribute_method?(attr_name) - attr_name == 'id' || @attributes.include?(attr_name) + attr_name == 'id' || (defined?(@attributes) && @attributes.include?(attr_name)) end end end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index bb0166a60c..3df8197e0d 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -85,6 +85,17 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert !topic.respond_to?("nothingness") assert !topic.respond_to?(:nothingness) end + + + # Syck calls respond_to? before actually calling initialize + def test_respond_to_with_allocated_object + topic = Topic.allocate + assert !topic.respond_to?("nothingness") + assert !topic.respond_to?(:nothingness) + assert_respond_to topic, "title" + assert_respond_to topic, :title + end + def test_array_content topic = Topic.new -- cgit v1.2.3 From da6ce2e2532a5209a224201a08f805f62dff8d72 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 08:44:31 -0800 Subject: adding a test for YAML round trip --- activerecord/test/cases/yaml_serialization_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb index f221def6b6..0fc9918744 100644 --- a/activerecord/test/cases/yaml_serialization_test.rb +++ b/activerecord/test/cases/yaml_serialization_test.rb @@ -2,10 +2,19 @@ require "cases/helper" require 'models/topic' class YamlSerializationTest < ActiveRecord::TestCase + fixtures :topics + def test_to_yaml_with_time_with_zone_should_not_raise_exception Time.zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"] ActiveRecord::Base.time_zone_aware_attributes = true topic = Topic.new(:written_on => DateTime.now) assert_nothing_raised { topic.to_yaml } end + + def test_roundtrip + topic = Topic.first + assert topic + t = YAML.load YAML.dump topic + assert_equal topic, t + end end -- cgit v1.2.3 From 077ec5a0ed3459816a4666364c722bb512c09bc7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 08:41:54 -0800 Subject: fixing space errors --- activerecord/test/cases/attribute_methods_test.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 3df8197e0d..8214815bde 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -85,8 +85,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert !topic.respond_to?("nothingness") assert !topic.respond_to?(:nothingness) end - - + # Syck calls respond_to? before actually calling initialize def test_respond_to_with_allocated_object topic = Topic.allocate @@ -95,7 +94,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_respond_to topic, "title" assert_respond_to topic, :title end - def test_array_content topic = Topic.new -- cgit v1.2.3 From deb90d9485516832d86afcf6e6810e1c98c836ab Mon Sep 17 00:00:00 2001 From: Alex Rothenberg Date: Tue, 23 Nov 2010 09:47:41 -0500 Subject: made in_clause_length default nil (no limit) --- .../lib/active_record/connection_adapters/abstract/database_limits.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb index a130c330dd..29ac9341ec 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb @@ -37,9 +37,9 @@ module ActiveRecord 16 end - # the maximum number of elements in an IN (x,y,z) clause + # the maximum number of elements in an IN (x,y,z) clause. nil means no limit def in_clause_length - 65535 + nil end # the maximum length of an SQL query -- cgit v1.2.3 From acbabd96268237826e1ab094273a1d7d53b4e579 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 2 Dec 2010 15:43:12 -0200 Subject: Don't compute this string again --- activemodel/lib/active_model/validations/numericality.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index b6aff7aa6b..95fe20de75 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -24,7 +24,7 @@ module ActiveModel def validate_each(record, attr_name, value) before_type_cast = "#{attr_name}_before_type_cast" - raw_value = record.send("#{attr_name}_before_type_cast") if record.respond_to?(before_type_cast.to_sym) + raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym) raw_value ||= value return if options[:allow_nil] && raw_value.nil? -- cgit v1.2.3 From 15633519f7fbe5450e4b232e556deca3340eb88d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 13:53:59 -0800 Subject: removing some calls to insert on arel --- .../associations/has_and_belongs_to_many_association.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index f6f291a039..367104ec1b 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -49,7 +49,7 @@ module ActiveRecord timestamps = record_timestamp_columns(record) timezone = record.send(:current_time_from_proper_timezone) if timestamps.any? - attributes = Hash[columns.map do |column| + attributes = columns.map do |column| name = column.name value = case name.to_s when @reflection.primary_key_name.to_s @@ -62,9 +62,10 @@ module ActiveRecord @owner.send(:quote_value, record[name], column) if record.has_attribute?(name) end [relation[name], value] unless value.nil? - end] + end - relation.insert(attributes) + stmt = relation.compile_insert Hash[attributes] + @owner.connection.insert stmt.to_sql end true -- cgit v1.2.3 From 6673d88f6f234ab493ad314f930901ce3a288da8 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 14:01:37 -0800 Subject: avoiding deprecated methods in arel --- activerecord/lib/active_record/migration.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index f6321f1499..b4b9dfab88 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -699,7 +699,8 @@ module ActiveRecord table.where(table["version"].eq(version.to_s)).delete else @migrated_versions.push(version).sort! - table.insert table["version"] => version.to_s + stmt = table.compile_insert table["version"] => version.to_s + Base.connection.insert stmt.to_sql end end -- cgit v1.2.3 From 42c51b8527e49ae2d7a0f6cc009e53865b7910c1 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 2 Dec 2010 17:59:55 -0200 Subject: Doesn't need to sort, lets users of attribute_names sort them if they want --- activerecord/lib/active_record/base.rb | 4 ++-- activerecord/test/cases/reflection_test.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b972b193e4..6573cd5e89 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1455,9 +1455,9 @@ MSG @attributes.has_key?(attr_name.to_s) end - # Returns an array of names for the attributes available on this object sorted alphabetically. + # Returns an array of names for the attributes available on this object. def attribute_names - @attributes.keys.sort + @attributes.keys end # Returns the value of the attribute identified by attr_name after it has been typecast (for example, diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 3b9e4f42a6..389ca9eae6 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -25,7 +25,7 @@ class ReflectionTest < ActiveRecord::TestCase def test_read_attribute_names assert_equal( %w( id title author_name author_email_address bonus_time written_on last_read content group approved replies_count parent_id parent_title type created_at updated_at ).sort, - @first.attribute_names + @first.attribute_names.sort ) end -- cgit v1.2.3 From 21eadc1b3f2eb818a4833381ee0a6cfa205f2955 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 2 Dec 2010 20:05:06 -0200 Subject: Base#[] and Base#[]= are aliases so implement them as aliases :) --- activerecord/lib/active_record/base.rb | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 6573cd5e89..ba7f6f0129 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1460,19 +1460,6 @@ MSG @attributes.keys end - # Returns the value of the attribute identified by attr_name after it has been typecast (for example, - # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). - # (Alias for the protected read_attribute method). - def [](attr_name) - read_attribute(attr_name) - end - - # Updates the attribute identified by attr_name with the specified +value+. - # (Alias for the protected write_attribute method). - def []=(attr_name, value) - write_attribute(attr_name, value) - end - # Allows you to set all the attributes at once by passing in a hash with keys # matching the attribute names (which again matches the column names). # @@ -1873,6 +1860,17 @@ MSG include Aggregations, Transactions, Reflection, Serialization NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner) + + # Returns the value of the attribute identified by attr_name after it has been typecast (for example, + # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). + # (Alias for the protected read_attribute method). + alias [] read_attribute + + # Updates the attribute identified by attr_name with the specified +value+. + # (Alias for the protected write_attribute method). + alias []= write_attribute + + public :[], :[]= end end -- cgit v1.2.3 From ae35a36afc616a0225d2f58da125abb318fd9b4a Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 2 Dec 2010 20:31:06 -0200 Subject: :method: is not needed when RDoc can detect it --- activesupport/lib/active_support/core_ext/object/try.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index 4d742b9ed2..ff812234e3 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -1,7 +1,4 @@ class Object - ## - # :method: try - # # Invokes the method identified by the symbol +method+, passing it any arguments # and/or the block specified, just like the regular Ruby Object#send does. # @@ -28,7 +25,6 @@ class Object # @person.try { |p| "#{p.first_name} #{p.last_name}" } #-- # +try+ behaves like +Object#send+, unless called on +NilClass+. - def try(*a, &b) if a.empty? && block_given? yield self @@ -36,7 +32,6 @@ class Object __send__(*a, &b) end end - end class NilClass #:nodoc: -- cgit v1.2.3 From 9f0133fbf4465d97559d97ff04c67775b8379b1c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 14:38:33 -0800 Subject: refactor `self.class` to a variable --- activerecord/lib/active_record/persistence.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 75dba0206d..fad83cde09 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -250,7 +250,8 @@ module ActiveRecord def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values) + klass = self.class + klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.update(attributes_with_values) end # Creates a record with values matching those of the instance attributes -- cgit v1.2.3 From 12b9920a15d93ead97bad83c30fd3a15bc7b074a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 14:43:08 -0800 Subject: removing calls to deprecated methods --- activerecord/lib/active_record/persistence.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index fad83cde09..9ac8fcb176 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -251,7 +251,8 @@ module ActiveRecord attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? klass = self.class - klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.update(attributes_with_values) + stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values) + klass.connection.update stmt.to_sql end # Creates a record with values matching those of the instance attributes -- cgit v1.2.3 From ead0c6eed35c71cc990f8c5b6d98e20c6072c136 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 14:49:02 -0800 Subject: removing more calls to deprecated methods --- activerecord/lib/active_record/associations/has_many_association.rb | 5 +++-- activerecord/lib/active_record/counter_cache.rb | 3 ++- activerecord/lib/active_record/relation.rb | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 685d818ab3..4ff61fff45 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -67,9 +67,10 @@ module ActiveRecord @reflection.klass.delete(records.map { |record| record.id }) else relation = Arel::Table.new(@reflection.table_name) - relation.where(relation[@reflection.primary_key_name].eq(@owner.id). + stmt = relation.where(relation[@reflection.primary_key_name].eq(@owner.id). and(relation[@reflection.klass.primary_key].in(records.map { |r| r.id })) - ).update(relation[@reflection.primary_key_name] => nil) + ).compile_update(relation[@reflection.primary_key_name] => nil) + @owner.connection.update stmt.to_sql @owner.class.update_counters(@owner.id, cached_counter_attribute_name => -records.size) if has_cached_counter? end diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index ed0d4aef7f..8180bf0987 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -30,9 +30,10 @@ module ActiveRecord reflection = belongs_to.find { |e| e.class_name == expected_name } counter_name = reflection.counter_cache_column - self.unscoped.where(arel_table[self.primary_key].eq(object.id)).arel.update({ + stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({ arel_table[counter_name] => object.send(association).count }) + connection.update stmt.to_sql end return true end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index bebdd1292c..646bf8f4be 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -156,7 +156,8 @@ module ActiveRecord else # Apply limit and order only if they're both present if @limit_value.present? == @order_values.present? - arel.update(Arel::SqlLiteral.new(@klass.send(:sanitize_sql_for_assignment, updates))) + stmt = arel.compile_update(Arel::SqlLiteral.new(@klass.send(:sanitize_sql_for_assignment, updates))) + @klass.connection.update stmt.to_sql else except(:limit, :order).update_all(updates) end -- cgit v1.2.3 From 8c9b5e413ff9bab8607377223dd42a0ff4375405 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 15:06:43 -0800 Subject: removing more calls to deprecated methods --- activerecord/lib/active_record/locking/optimistic.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index c0e1dda2bd..e5065de7fb 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -87,11 +87,13 @@ module ActiveRecord begin relation = self.class.unscoped - affected_rows = relation.where( + stmt = relation.where( relation.table[self.class.primary_key].eq(quoted_id).and( relation.table[lock_col].eq(quote_value(previous_value)) ) - ).arel.update(arel_attributes_values(false, false, attribute_names)) + ).arel.compile_update(arel_attributes_values(false, false, attribute_names)) + + affected_rows = connection.update stmt.to_sql unless affected_rows == 1 raise ActiveRecord::StaleObjectError, "Attempted to update a stale object: #{self.class.name}" -- cgit v1.2.3 From b0fc912f1b829391760b50107dad365fa87c744f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 15:50:34 -0800 Subject: avoid deprecate api --- activerecord/lib/active_record/relation.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 646bf8f4be..3009bb70c1 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -271,7 +271,14 @@ module ActiveRecord # If you need to destroy dependent associations or call your before_* or # +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) - conditions ? where(conditions).delete_all : arel.delete.tap { reset } + if conditions + where(conditions).delete_all + else + statement = arel.compile_delete + affected = @klass.connection.delete statement.to_sql + reset + affected + end end # Deletes the row with a primary key matching the +id+ argument, using a -- cgit v1.2.3 From 9a3e29e126d9daf6175b4d2be50112d1c8771d17 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 2 Dec 2010 15:56:55 -0800 Subject: remove calls to deprecated methods --- .../associations/has_and_belongs_to_many_association.rb | 5 +++-- activerecord/lib/active_record/migration.rb | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 367104ec1b..e2ce9aefcf 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -76,9 +76,10 @@ module ActiveRecord records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) } else relation = Arel::Table.new(@reflection.options[:join_table]) - relation.where(relation[@reflection.primary_key_name].eq(@owner.id). + stmt = relation.where(relation[@reflection.primary_key_name].eq(@owner.id). and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }.compact)) - ).delete + ).compile_delete + @owner.connection.delete stmt.to_sql end end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index b4b9dfab88..d80416149e 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -696,7 +696,8 @@ module ActiveRecord @migrated_versions ||= [] if down? @migrated_versions.delete(version) - table.where(table["version"].eq(version.to_s)).delete + stmt = table.where(table["version"].eq(version.to_s)).compile_delete + Base.connection.delete stmt.to_sql else @migrated_versions.push(version).sort! stmt = table.compile_insert table["version"] => version.to_s -- cgit v1.2.3 From 2e021ef477dd7d26ca6183b4e3113be3fcc6a5e1 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 11:57:42 +1100 Subject: Clarify that preload_frameworks defaults to nil --- railties/guides/source/configuring.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 7e40cad040..3ea9db2373 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -91,7 +91,7 @@ h4. Rails General Configuration * +config.plugins+ accepts the list of plugins to load. If this is set to nil, all plugins will be loaded. If this is set to [], no plugins will be loaded. Otherwise, plugins will be loaded in the order specified. -* +config.preload_frameworks+ enables or disables preloading all frameworks at startup. Can also be enabled with +threadsafe!+. +* +config.preload_frameworks+ enables or disables preloading all frameworks at startup. Can also be enabled with +threadsafe!+. Defaults to +nil+, so is disabled. * +config.reload_plugins+ enables or disables plugin reloading. -- cgit v1.2.3 From 4d11af2e8fa76b32e7480938c85037d907da3ae9 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 12:16:08 +1100 Subject: Config guide: clarify what cache_store defaults to. --- railties/guides/source/configuring.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 3ea9db2373..7475575e31 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -63,7 +63,7 @@ h4. Rails General Configuration * +config.cache_classes+ controls whether or not application classes should be reloaded on each request. Defaults to _true_ in development, _false_ in test and production. Can also be enabled with +threadsafe!+. -* +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class. +* +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class. Defaults to +:file_store+. * +config.colorize_logging+ specifies whether or not to use ANSI color codes when logging information. Defaults to _true_. -- cgit v1.2.3 From 1304c92b68ef365fd4863de0a9fd5ce38b71b67c Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 13:05:58 +1100 Subject: Config guide: Mention ActionDispatch::Callbacks methods: before, to_prepare and after --- railties/guides/source/configuring.textile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 7475575e31..89c6686690 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -260,6 +260,10 @@ h4. Configuring Action Dispatch * +config.action_dispatch.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class. +* +ActionDispatch::Callbacks.before+ takes a block of code to run before the request. +* +ActionDispatch::Callbacks.to_prepare+ takes a block to run after +ActionDispatch::Callbacks.before+, but before the request. Runs for every request in +development+ mode, but only once for +production+ or environments with +cache_classes+ set to +true+. +* +ActionDispatch::Callbacks.after+ takes a block of code to run after the request. + h4. Configuring Action View There are only a few configuration options for Action View, starting with four on +ActionView::Base+: -- cgit v1.2.3 From 7f1c794a11c463f4b50d46180932fa4a62e369a5 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 13:29:02 +1100 Subject: Remove extra f --- activesupport/lib/active_support/dependencies.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 787437c49a..dab6fdbac6 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -331,7 +331,7 @@ module ActiveSupport #:nodoc: if load? log "loading #{file_name}" - # Enable warnings iff this file has not been loaded before and + # Enable warnings if this file has not been loaded before and # warnings_on_first_load is set. load_args = ["#{file_name}.rb"] load_args << const_path unless const_path.nil? -- cgit v1.2.3 From 79b14407ad044133587ccb2d7fbc89b2e78e5da4 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 17:26:40 +1100 Subject: Config guide: add tld_length to ActionDispatch section --- railties/guides/source/configuring.textile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 89c6686690..ac1c6e5cc0 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -261,9 +261,13 @@ h4. Configuring Action Dispatch * +config.action_dispatch.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class. * +ActionDispatch::Callbacks.before+ takes a block of code to run before the request. + * +ActionDispatch::Callbacks.to_prepare+ takes a block to run after +ActionDispatch::Callbacks.before+, but before the request. Runs for every request in +development+ mode, but only once for +production+ or environments with +cache_classes+ set to +true+. + * +ActionDispatch::Callbacks.after+ takes a block of code to run after the request. +* +ActionDispatch::Http::URL.tld_length+ sets the TLD (top-level domain) length for the application. Defaults to +1+. + h4. Configuring Action View There are only a few configuration options for Action View, starting with four on +ActionView::Base+: -- cgit v1.2.3 From e600f8420821f1356553e908155be7a153c43391 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 17:28:51 +1100 Subject: Config guide: Add cache_asset_ids --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index ac1c6e5cc0..b761a4f826 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -282,6 +282,8 @@ There are only a few configuration options for Action View, starting with four o * +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information. +* +ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids+ With the cache enabled, the asset tag helper methods will make fewer expensive file system calls (the default implementation checks the file system timestamp). However this prevents you from modifying any asset files while the server is running. + h4. Configuring Action Mailer There are a number of settings available on +config.action_mailer+: -- cgit v1.2.3 From 04779fbadd4e77887a8899585f01141cfc27babc Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 17:34:34 +1100 Subject: Config guide: should use config.action_dispatch.tld_length to set the TLD --- railties/guides/source/configuring.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index b761a4f826..83789ab710 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -260,14 +260,14 @@ h4. Configuring Action Dispatch * +config.action_dispatch.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class. +* +config.action_dispatch.tld_length+ sets the TLD (top-level domain) length for the application. Defaults to +1+. + * +ActionDispatch::Callbacks.before+ takes a block of code to run before the request. * +ActionDispatch::Callbacks.to_prepare+ takes a block to run after +ActionDispatch::Callbacks.before+, but before the request. Runs for every request in +development+ mode, but only once for +production+ or environments with +cache_classes+ set to +true+. * +ActionDispatch::Callbacks.after+ takes a block of code to run after the request. -* +ActionDispatch::Http::URL.tld_length+ sets the TLD (top-level domain) length for the application. Defaults to +1+. - h4. Configuring Action View There are only a few configuration options for Action View, starting with four on +ActionView::Base+: -- cgit v1.2.3 From b800125ae8a9ab1a1b4d773f21b4275a36fb0351 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 18:06:02 +1100 Subject: Config guide: Add javascript_expansions and stylesheet_expansions --- railties/guides/source/configuring.textile | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 83789ab710..837b3b563c 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -282,6 +282,30 @@ There are only a few configuration options for Action View, starting with four o * +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information. +* +config.action_view.javascript_expansions+ a hash containining expansions that can be used for javascript include tag. By default, this is defined as: + + + config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] } + + +However, you may add to this by defining others: + + + config.action_view.javascript_expansions[:jquery] = ["jquery", "jquery-ui"] + + +Then this can be referenced in the view with the following code: + + + <%= javascript_include_tag :jquery %> + + +* +config.action_view.stylesheet_expansions+ works in much the same way as +javascript_expansions+, but has no default key. Keys defined for this hash can be referenced in the view like such: + + + <%= stylesheet_link_tag :special %> + + * +ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids+ With the cache enabled, the asset tag helper methods will make fewer expensive file system calls (the default implementation checks the file system timestamp). However this prevents you from modifying any asset files while the server is running. h4. Configuring Action Mailer -- cgit v1.2.3 From 30bcdff4057374f8ca864bbbbc3f25e35b269801 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 3 Dec 2010 19:16:25 +1100 Subject: Config guide: begin documenting the initializers --- railties/guides/source/configuring.textile | 105 +++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 837b3b563c..0236e72529 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -362,7 +362,6 @@ There are a few configuration options available in Active Support: * +ActiveSupport::Logger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+. - h3. Rails Environment Settings Some parts of Rails can also be configured externally by supplying environment variables. The following environment variables are recognized by various parts of Rails: @@ -380,7 +379,7 @@ h3. Initialization events Rails has 5 initialization events which can be hooked into (listed in order that they are ran): * +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens. -* +before_initialize+: This is run directly before the initialization process of the application occurs. +* +before_initialize+: This is run directly before the initialization process of the application occurs with the +:bootstrap_hook+ initializer near the beginning of the Rails initialization process. * +to_prepare+: Run after the initializers are ran for all Railties (including the application itself), but before eager loading and the middleware stack is built. * +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ enviroment. * +after_initialize+: Run directly after the initialization of the application, but before the application initializers are run. @@ -398,7 +397,6 @@ Rails has several initializers that run on startup that are all defined by using end - The +initializer+ method takes three arguments with the first being the name for the initializer and the second being an options hash (not shown here) and the third being a block. The +:before+ key in the options hash can be specified to specify which initializer this new initializer must run before, and the +:after+ key will specify which initializer to run this initializer _after_. Initializers defined using the +initializer+ method will be ran in the order they are defined in, with the exception of ones that use the +:before+ or +:after+ methods. @@ -407,6 +405,104 @@ WARNING: You may put your initializer before or after any other initializer in t The block's argument of the +initialize+ is the instance of the application itself, and so we can access the configuration on it by using the +config+ method as this initializer does. +Because +Rails::Application+ inherits from +Rails::Railtie+ (indirectly), you can use the +initializer+ method in +config/application.rb+ to define initializers for the application. + +Below is a comprehensive list of all the initializers found in Rails in the order that they are defined (and therefore run in, unless otherwise stated). + +h4. +load_environment_hook+ + +Serves as a placeholder so that +:load_environment_config+ can be defined to run before it. + +h4. +load_active_support+ + +Requires +active_support/dependencies+ which sets up the basis for Active Support. Optionally requires +active_support/all+ if +config.active_support.bare+ is un-truthful, which is the default. + +h4. +preload_frameworks+ + +Will load all autoload dependencies of Rails automatically if +config.preload_frameworks+ is +true+ or "truthful". By default this configuration option is disabled. In Rails, when internal classes are referenced for the first time they are autoloaded. +:preload_frameworks+ loads all of this at once on initialization. + +h4. +initialize_logger+ + +Initializes the logger (an +ActiveSupport::BufferedLogger+ object) for the application and makes it accessible at +Rails.logger+, providing that there's no initializer inserted before this point that has defined +Rails.logger+. + +h4. +initialize_cache+ + +If +RAILS_CACHE+ isn't yet set, initializes the cache by referencing the value in +config.cache_store+ and stores the outcome as +RAILS_CACHE+. If this object responds to the +middleware+ method, its middleware is inserted before +Rack::Runtime+ in the middleware stack. + +h4. +set_clear_dependencies_hook+ + +Provides a hook for +active_record.set_dispatch_hooks+ to use, which will run before this initializer. + +This initializer -- which runs only if +cache_classes+ is set to +false+ -- uses +ActionDispatch::Callbacks.after+ to remove the constants which have been referenced during the request from the object space so that they will be reloaded during the following request. + +h4. +initialize_dependency_mechanism+ + +If +config.cache_classes+ is set to +true+, configures +ActiveSupport::Dependencies.mechanism+ to +require+ dependencies rather than +load+ them. + +h4. +bootstrap_hook+ + +Runs all configured +before_initialize+ blocks. + +h4. +i18n.callbacks+ + +In the development environment, sets up a +to_prepare+ callback which will call +I18n.reload!+ if any of the locales have changed since the last request. In production mode this callback will only run on the first request. + +h4. +active_support.initialize_whiny_nils+ + +Will require +active_support/whiny_nil+ if +config.whiny_nil+ is set to +true+. This file will output errors such as: + + + Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id + + +And: + + + You have a nil object when you didn't expect it! + You might have expected an instance of Array. + The error occurred while evaluating nil.each + + +h4. +active_support.deprecation_behavior+ + +Sets up deprecation reporting for environments, defaulting to +log+ for development, +notify+ for production and +stderr+ for test. If a value isn't set for +config.active_support.deprecation+ then this initializer will prompt the user to configure this line in the current environment's +config/environments+ file. + +h4. +active_support.initialize_time_zone+ + +Sets the default time zone for the application based off the +config.time_zone+ setting, which defaults to "UTC". + +h4. +action_dispatch.configure+ + +Configures the +ActionDispatch::Http::URL.tld_length+ to be set to the value of +config.action_dispatch.tld_length+. + +h4. +action_view.cache_asset_ids+ + +Will set +ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids+ to +false+ when Active Support loads, but only if +config.cache_classes+ is too. + +h4. +action_view.javascript_expansions+ + +Registers the expansions set up by +config.action_view.javascript_expansions+ and +config.action_view.stylesheet_expansions+ to be recognised by Action View and therefore usable in the views. + +h4. +action_view.set_configs+ + +Sets up Action View by using the settings in +config.action_view+ by +send+'ing the method names as setters to +ActionView::Base+ and passing the values through. + +h4. +action_controller.logger+ + +Sets +ActionController::Base.logger+ -- if it's not already set -- to +Rails.logger+. + +h4. +action_controller.initialize_framework_caches+ + +Sets +ActionController::Base.cache_store+ -- if it's not already set -- to +RAILS_CACHE+. + +h4. +action_controller.set_configs+ + +Sets up Action View by using the settings in +config.action_controller+ by +send+'ing the method names as setters to +ActionController::Base+ and passing the values through. + +h4 +active_record.initialize_timezone+ + +Sets +ActiveRecord::Base.time_zone_aware_attributes+ to true, as well as setting +ActiveRecord::Base.default_timezone+ to UTC. + h4. Initializer files After loading the framework and any gems and plugins in your application, Rails turns to loading initialization code from +config/initializers+. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks and plugins are loaded. @@ -417,7 +513,8 @@ TIP: If you have any ordering dependency in your initializers, you can control t h3. Changelog -* November 26, 2010: Removed all config settings not available in Rails 3 (Ryan Bigg) +* December 3, 2010: Added initialization events for Rails 3 ("Ryan Bigg":http://ryanbigg.com) +* November 26, 2010: Removed all config settings not available in Rails 3 ("Ryan Bigg":http://ryanbigg.com) * August 13, 2009: Updated with config syntax and added general configuration options by "John Pignata" * January 3, 2009: First reasonably complete draft by "Mike Gunderloy":credits.html#mgunderloy * November 5, 2008: Rough outline by "Mike Gunderloy":credits.html#mgunderloy -- cgit v1.2.3 From 226ea0e9e817a2e449d989d925c36cf6a4cfd67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 3 Dec 2010 13:27:43 +0100 Subject: Wrap everything in class << self. --- actionpack/lib/action_dispatch/http/url.rb | 79 ++++++++++++++---------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 51d4766282..796cd8c09b 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -4,55 +4,54 @@ module ActionDispatch mattr_accessor :tld_length self.tld_length = 1 - def self.extract_domain(host, tld_length = @@tld_length) - return nil unless named_host?(host) - - host.split('.').last(1 + tld_length).join('.') - end - - def self.extract_subdomains(host, tld_length = @@tld_length) - return [] unless named_host?(host) - parts = host.split('.') - parts[0..-(tld_length+2)] - end - - def self.extract_subdomain(host, tld_length = @@tld_length) - extract_subdomains(host, tld_length).join('.') - end + class << self + def extract_domain(host, tld_length = @@tld_length) + return nil unless named_host?(host) + host.split('.').last(1 + tld_length).join('.') + end - def self.named_host?(host) - !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host)) - end + def extract_subdomains(host, tld_length = @@tld_length) + return [] unless named_host?(host) + parts = host.split('.') + parts[0..-(tld_length+2)] + end - def self.url_for(options = {}) - unless options[:host].present? || options[:only_path].present? - raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true' + def extract_subdomain(host, tld_length = @@tld_length) + extract_subdomains(host, tld_length).join('.') end - rewritten_url = "" + def url_for(options = {}) + unless options[:host].present? || options[:only_path].present? + raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true' + end - unless options[:only_path] - rewritten_url << (options[:protocol] || "http") - rewritten_url << "://" unless rewritten_url.match("://") - rewritten_url << rewrite_authentication(options) - rewritten_url << host_or_subdomain_and_domain(options) - rewritten_url << ":#{options.delete(:port)}" if options[:port] - end + rewritten_url = "" - path = options.delete(:path) || '' + unless options[:only_path] + rewritten_url << (options[:protocol] || "http") + rewritten_url << "://" unless rewritten_url.match("://") + rewritten_url << rewrite_authentication(options) + rewritten_url << host_or_subdomain_and_domain(options) + rewritten_url << ":#{options.delete(:port)}" if options[:port] + end - params = options[:params] || {} - params.reject! {|k,v| !v } + path = options.delete(:path) || '' - rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path) - rewritten_url << "?#{params.to_query}" unless params.empty? - rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor] - rewritten_url - end + params = options[:params] || {} + params.reject! {|k,v| !v } + + rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path) + rewritten_url << "?#{params.to_query}" unless params.empty? + rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor] + rewritten_url + end - class << self private + def named_host?(host) + !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host)) + end + def rewrite_authentication(options) if options[:user] && options[:password] "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@" @@ -72,11 +71,8 @@ module ActionDispatch host << (options[:domain] || extract_domain(options[:host], tld_length)) host end - end - - # Returns the complete URL used for this request. def url protocol + host_with_port + fullpath @@ -168,7 +164,6 @@ module ActionDispatch def subdomain(tld_length = @@tld_length) subdomains(tld_length) end - end end end -- cgit v1.2.3 From 47737681fdfe4986dc7426b174f81d2b8daca991 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 3 Dec 2010 11:44:11 -0800 Subject: in the middle of refactoring --- activerecord/lib/active_record/migration.rb | 4 ++ activerecord/test/cases/migration_test.rb | 80 ++++++++-------------- .../interleaved/pass_1/3_innocent_jointable.rb | 12 ---- .../pass_1/3_interleaved_innocent_jointable.rb | 12 ++++ .../pass_2/1_interleaved_people_have_last_names.rb | 9 +++ .../interleaved/pass_2/1_people_have_last_names.rb | 9 --- .../interleaved/pass_2/3_innocent_jointable.rb | 12 ---- .../pass_2/3_interleaved_innocent_jointable.rb | 12 ++++ .../pass_3/1_interleaved_people_have_last_names.rb | 9 +++ .../interleaved/pass_3/1_people_have_last_names.rb | 9 --- .../interleaved/pass_3/2_i_raise_on_down.rb | 8 --- .../pass_3/2_interleaved_i_raise_on_down.rb | 8 +++ .../interleaved/pass_3/3_innocent_jointable.rb | 12 ---- .../pass_3/3_interleaved_innocent_jointable.rb | 12 ++++ .../migrations/valid/1_people_have_last_names.rb | 9 --- .../valid/1_valid_people_have_last_names.rb | 9 +++ .../20100101010101_people_have_last_names.rb | 9 --- ...valid_with_timestamps_people_have_last_names.rb | 9 +++ ...0101_valid_with_timestamps_we_need_reminders.rb | 12 ++++ .../20100201010101_we_need_reminders.rb | 12 ---- .../20100301010101_innocent_jointable.rb | 12 ---- ...101_valid_with_timestamps_innocent_jointable.rb | 12 ++++ 22 files changed, 138 insertions(+), 154 deletions(-) delete mode 100644 activerecord/test/migrations/interleaved/pass_1/3_innocent_jointable.rb create mode 100644 activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb create mode 100644 activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb delete mode 100644 activerecord/test/migrations/interleaved/pass_2/1_people_have_last_names.rb delete mode 100644 activerecord/test/migrations/interleaved/pass_2/3_innocent_jointable.rb create mode 100644 activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb create mode 100644 activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb delete mode 100644 activerecord/test/migrations/interleaved/pass_3/1_people_have_last_names.rb delete mode 100644 activerecord/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb create mode 100644 activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb delete mode 100644 activerecord/test/migrations/interleaved/pass_3/3_innocent_jointable.rb create mode 100644 activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb delete mode 100644 activerecord/test/migrations/valid/1_people_have_last_names.rb create mode 100644 activerecord/test/migrations/valid/1_valid_people_have_last_names.rb delete mode 100644 activerecord/test/migrations/valid_with_timestamps/20100101010101_people_have_last_names.rb create mode 100644 activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb create mode 100644 activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb delete mode 100644 activerecord/test/migrations/valid_with_timestamps/20100201010101_we_need_reminders.rb delete mode 100644 activerecord/test/migrations/valid_with_timestamps/20100301010101_innocent_jointable.rb create mode 100644 activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index d80416149e..4dc67a0905 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -518,6 +518,7 @@ module ActiveRecord when target_version.nil? up(migrations_path, target_version) when current_version == 0 && target_version == 0 + [] when current_version > target_version down(migrations_path, target_version) else @@ -647,6 +648,7 @@ module ActiveRecord # skip the last migration if we're headed down, but not ALL the way down runnable.pop if down? && target + ran = [] runnable.each do |migration| Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger @@ -666,11 +668,13 @@ module ActiveRecord migration.migrate(@direction) record_version_state_after_migrating(migration.version) end + ran << migration rescue => e canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : "" raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace end end + ran end def migrations diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 3037d73a1b..d7ff3e68d9 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -5,10 +5,8 @@ require 'models/person' require 'models/topic' require 'models/developer' -require MIGRATIONS_ROOT + "/valid/1_people_have_last_names" require MIGRATIONS_ROOT + "/valid/2_we_need_reminders" require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers" -require MIGRATIONS_ROOT + "/interleaved/pass_3/2_i_raise_on_down" if ActiveRecord::Base.connection.supports_migrations? class BigNumber < ActiveRecord::Base; end @@ -21,8 +19,8 @@ if ActiveRecord::Base.connection.supports_migrations? end def puts(text="") - self.class.message_count ||= 0 - self.class.message_count += 1 + ActiveRecord::Migration.message_count ||= 0 + ActiveRecord::Migration.message_count += 1 end end @@ -52,7 +50,7 @@ if ActiveRecord::Base.connection.supports_migrations? def setup ActiveRecord::Migration.verbose = true - PeopleHaveLastNames.message_count = 0 + ActiveRecord::Migration.message_count = 0 end def teardown @@ -1271,7 +1269,7 @@ if ActiveRecord::Base.connection.supports_migrations? def test_finds_migrations migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations - [[1, 'PeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i| + [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i| assert_equal migrations[i].version, pair.first assert_equal migrations[i].name, pair.last end @@ -1283,39 +1281,30 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal 1, migrations.size assert_equal migrations[0].version, 3 - assert_equal migrations[0].name, 'InnocentJointable' + assert_equal migrations[0].name, 'InterleavedInnocentJointable' end def test_relative_migrations - $".delete_if do |fname| - fname == (MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb") - end - Object.send(:remove_const, :PeopleHaveLastNames) - - Dir.chdir(MIGRATIONS_ROOT) do + list = Dir.chdir(MIGRATIONS_ROOT) do ActiveRecord::Migrator.up("valid/", 1) end - assert defined?(PeopleHaveLastNames) + migration_proxy = list.find { |item| + item.name == 'ValidPeopleHaveLastNames' + } + assert migration_proxy, 'should find pending migration' end def test_only_loads_pending_migrations # migrate up to 1 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1) - # now unload the migrations that have been defined - Object.send(:remove_const, :PeopleHaveLastNames) - - ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil) + proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil) - assert !defined? PeopleHaveLastNames - - %w(WeNeedReminders, InnocentJointable).each do |migration| - assert defined? migration - end - - ensure - load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb") + names = proxies.map(&:name) + assert !names.include?('ValidPeopleHaveLastNames') + assert names.include?('WeNeedReminders') + assert names.include?('InnocentJointable') end def test_target_version_zero_should_run_only_once @@ -1325,16 +1314,9 @@ if ActiveRecord::Base.connection.supports_migrations? # migrate down to 0 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0) - # now unload the migrations that have been defined - PeopleHaveLastNames.unloadable - ActiveSupport::Dependencies.remove_unloadable_constants! - # migrate down to 0 again - ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0) - - assert !defined? PeopleHaveLastNames - ensure - load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb") + proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0) + assert_equal [], proxies end def test_migrator_db_has_no_schema_migrations_table @@ -1351,20 +1333,20 @@ if ActiveRecord::Base.connection.supports_migrations? def test_migrator_verbosity ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1) - assert_operator PeopleHaveLastNames.message_count, :>, 0 - PeopleHaveLastNames.message_count = 0 + assert_not_equal 0, ActiveRecord::Migration.message_count + ActiveRecord::Migration.message_count = 0 ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0) - assert_operator PeopleHaveLastNames.message_count, :>, 0 - PeopleHaveLastNames.message_count = 0 + assert_not_equal 0, ActiveRecord::Migration.message_count + ActiveRecord::Migration.message_count = 0 end def test_migrator_verbosity_off - PeopleHaveLastNames.verbose = false + ActiveRecord::Migration.verbose = false ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1) - assert_equal 0, PeopleHaveLastNames.message_count + assert_equal 0, ActiveRecord::Migration.message_count ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0) - assert_equal 0, PeopleHaveLastNames.message_count + assert_equal 0, ActiveRecord::Migration.message_count end def test_migrator_going_down_due_to_version_target @@ -1658,10 +1640,6 @@ if ActiveRecord::Base.connection.supports_migrations? end # SexyMigrationsTest class MigrationLoggerTest < ActiveRecord::TestCase - def setup - Object.send(:remove_const, :InnocentJointable) - end - def test_migration_should_be_run_without_logger previous_logger = ActiveRecord::Base.logger ActiveRecord::Base.logger = nil @@ -1675,7 +1653,7 @@ if ActiveRecord::Base.connection.supports_migrations? class InterleavedMigrationsTest < ActiveRecord::TestCase def setup - Object.send(:remove_const, :PeopleHaveLastNames) + #Object.send(:remove_const, :PeopleHaveLastNames) end def test_migrator_interleaved_migrations @@ -1688,10 +1666,12 @@ if ActiveRecord::Base.connection.supports_migrations? Person.reset_column_information assert Person.column_methods_hash.include?(:last_name) - Object.send(:remove_const, :PeopleHaveLastNames) - Object.send(:remove_const, :InnocentJointable) assert_nothing_raised do - ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3") + proxies = ActiveRecord::Migrator.down( + MIGRATIONS_ROOT + "/interleaved/pass_3") + names = proxies.map(&:name) + assert names.include?('InterleavedPeopleHaveLastNames') + assert names.include?('InterleavedInnocentJointable') end end end diff --git a/activerecord/test/migrations/interleaved/pass_1/3_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_1/3_innocent_jointable.rb deleted file mode 100644 index 21c9ca5328..0000000000 --- a/activerecord/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +++ /dev/null @@ -1,12 +0,0 @@ -class InnocentJointable < ActiveRecord::Migration - def self.up - create_table("people_reminders", :id => false) do |t| - t.column :reminder_id, :integer - t.column :person_id, :integer - end - end - - def self.down - drop_table "people_reminders" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb new file mode 100644 index 0000000000..bf912fbfc8 --- /dev/null +++ b/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb @@ -0,0 +1,12 @@ +class InterleavedInnocentJointable < ActiveRecord::Migration + def self.up + create_table("people_reminders", :id => false) do |t| + t.column :reminder_id, :integer + t.column :person_id, :integer + end + end + + def self.down + drop_table "people_reminders" + end +end diff --git a/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb b/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb new file mode 100644 index 0000000000..c6c94213a0 --- /dev/null +++ b/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb @@ -0,0 +1,9 @@ +class InterleavedPeopleHaveLastNames < ActiveRecord::Migration + def self.up + add_column "people", "last_name", :string + end + + def self.down + remove_column "people", "last_name" + end +end diff --git a/activerecord/test/migrations/interleaved/pass_2/1_people_have_last_names.rb b/activerecord/test/migrations/interleaved/pass_2/1_people_have_last_names.rb deleted file mode 100644 index 81af5fef5e..0000000000 --- a/activerecord/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +++ /dev/null @@ -1,9 +0,0 @@ -class PeopleHaveLastNames < ActiveRecord::Migration - def self.up - add_column "people", "last_name", :string - end - - def self.down - remove_column "people", "last_name" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/interleaved/pass_2/3_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_2/3_innocent_jointable.rb deleted file mode 100644 index 21c9ca5328..0000000000 --- a/activerecord/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +++ /dev/null @@ -1,12 +0,0 @@ -class InnocentJointable < ActiveRecord::Migration - def self.up - create_table("people_reminders", :id => false) do |t| - t.column :reminder_id, :integer - t.column :person_id, :integer - end - end - - def self.down - drop_table "people_reminders" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb new file mode 100644 index 0000000000..bf912fbfc8 --- /dev/null +++ b/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb @@ -0,0 +1,12 @@ +class InterleavedInnocentJointable < ActiveRecord::Migration + def self.up + create_table("people_reminders", :id => false) do |t| + t.column :reminder_id, :integer + t.column :person_id, :integer + end + end + + def self.down + drop_table "people_reminders" + end +end diff --git a/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb b/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb new file mode 100644 index 0000000000..c6c94213a0 --- /dev/null +++ b/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb @@ -0,0 +1,9 @@ +class InterleavedPeopleHaveLastNames < ActiveRecord::Migration + def self.up + add_column "people", "last_name", :string + end + + def self.down + remove_column "people", "last_name" + end +end diff --git a/activerecord/test/migrations/interleaved/pass_3/1_people_have_last_names.rb b/activerecord/test/migrations/interleaved/pass_3/1_people_have_last_names.rb deleted file mode 100644 index 81af5fef5e..0000000000 --- a/activerecord/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +++ /dev/null @@ -1,9 +0,0 @@ -class PeopleHaveLastNames < ActiveRecord::Migration - def self.up - add_column "people", "last_name", :string - end - - def self.down - remove_column "people", "last_name" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb b/activerecord/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb deleted file mode 100644 index 9b1ce9f017..0000000000 --- a/activerecord/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +++ /dev/null @@ -1,8 +0,0 @@ -class IRaiseOnDown < ActiveRecord::Migration - def self.up - end - - def self.down - raise - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb b/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb new file mode 100644 index 0000000000..6849995f5e --- /dev/null +++ b/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb @@ -0,0 +1,8 @@ +class InterleavedIRaiseOnDown < ActiveRecord::Migration + def self.up + end + + def self.down + raise + end +end diff --git a/activerecord/test/migrations/interleaved/pass_3/3_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_3/3_innocent_jointable.rb deleted file mode 100644 index 21c9ca5328..0000000000 --- a/activerecord/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +++ /dev/null @@ -1,12 +0,0 @@ -class InnocentJointable < ActiveRecord::Migration - def self.up - create_table("people_reminders", :id => false) do |t| - t.column :reminder_id, :integer - t.column :person_id, :integer - end - end - - def self.down - drop_table "people_reminders" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb new file mode 100644 index 0000000000..bf912fbfc8 --- /dev/null +++ b/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb @@ -0,0 +1,12 @@ +class InterleavedInnocentJointable < ActiveRecord::Migration + def self.up + create_table("people_reminders", :id => false) do |t| + t.column :reminder_id, :integer + t.column :person_id, :integer + end + end + + def self.down + drop_table "people_reminders" + end +end diff --git a/activerecord/test/migrations/valid/1_people_have_last_names.rb b/activerecord/test/migrations/valid/1_people_have_last_names.rb deleted file mode 100644 index 81af5fef5e..0000000000 --- a/activerecord/test/migrations/valid/1_people_have_last_names.rb +++ /dev/null @@ -1,9 +0,0 @@ -class PeopleHaveLastNames < ActiveRecord::Migration - def self.up - add_column "people", "last_name", :string - end - - def self.down - remove_column "people", "last_name" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb b/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb new file mode 100644 index 0000000000..06cb911117 --- /dev/null +++ b/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb @@ -0,0 +1,9 @@ +class ValidPeopleHaveLastNames < ActiveRecord::Migration + def self.up + add_column "people", "last_name", :string + end + + def self.down + remove_column "people", "last_name" + end +end diff --git a/activerecord/test/migrations/valid_with_timestamps/20100101010101_people_have_last_names.rb b/activerecord/test/migrations/valid_with_timestamps/20100101010101_people_have_last_names.rb deleted file mode 100644 index 81af5fef5e..0000000000 --- a/activerecord/test/migrations/valid_with_timestamps/20100101010101_people_have_last_names.rb +++ /dev/null @@ -1,9 +0,0 @@ -class PeopleHaveLastNames < ActiveRecord::Migration - def self.up - add_column "people", "last_name", :string - end - - def self.down - remove_column "people", "last_name" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb b/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb new file mode 100644 index 0000000000..1da99ceaba --- /dev/null +++ b/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb @@ -0,0 +1,9 @@ +class ValidWithTimestampsPeopleHaveLastNames < ActiveRecord::Migration + def self.up + add_column "people", "last_name", :string + end + + def self.down + remove_column "people", "last_name" + end +end diff --git a/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb b/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb new file mode 100644 index 0000000000..cb6d735c8b --- /dev/null +++ b/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb @@ -0,0 +1,12 @@ +class ValidWithTimestampsWeNeedReminders < ActiveRecord::Migration + def self.up + create_table("reminders") do |t| + t.column :content, :text + t.column :remind_at, :datetime + end + end + + def self.down + drop_table "reminders" + end +end diff --git a/activerecord/test/migrations/valid_with_timestamps/20100201010101_we_need_reminders.rb b/activerecord/test/migrations/valid_with_timestamps/20100201010101_we_need_reminders.rb deleted file mode 100644 index d5e71ce8ef..0000000000 --- a/activerecord/test/migrations/valid_with_timestamps/20100201010101_we_need_reminders.rb +++ /dev/null @@ -1,12 +0,0 @@ -class WeNeedReminders < ActiveRecord::Migration - def self.up - create_table("reminders") do |t| - t.column :content, :text - t.column :remind_at, :datetime - end - end - - def self.down - drop_table "reminders" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/valid_with_timestamps/20100301010101_innocent_jointable.rb b/activerecord/test/migrations/valid_with_timestamps/20100301010101_innocent_jointable.rb deleted file mode 100644 index 21c9ca5328..0000000000 --- a/activerecord/test/migrations/valid_with_timestamps/20100301010101_innocent_jointable.rb +++ /dev/null @@ -1,12 +0,0 @@ -class InnocentJointable < ActiveRecord::Migration - def self.up - create_table("people_reminders", :id => false) do |t| - t.column :reminder_id, :integer - t.column :person_id, :integer - end - end - - def self.down - drop_table "people_reminders" - end -end \ No newline at end of file diff --git a/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb b/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb new file mode 100644 index 0000000000..4bd4b4714d --- /dev/null +++ b/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb @@ -0,0 +1,12 @@ +class ValidWithTimestampsInnocentJointable < ActiveRecord::Migration + def self.up + create_table("people_reminders", :id => false) do |t| + t.column :reminder_id, :integer + t.column :person_id, :integer + end + end + + def self.down + drop_table "people_reminders" + end +end -- cgit v1.2.3 From 399730bdd2f133e9fecac501e2f2333be5f29aa2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 3 Dec 2010 11:51:33 -0800 Subject: removing unused setup --- activerecord/test/cases/migration_test.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index d7ff3e68d9..96da3be655 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1652,10 +1652,6 @@ if ActiveRecord::Base.connection.supports_migrations? end class InterleavedMigrationsTest < ActiveRecord::TestCase - def setup - #Object.send(:remove_const, :PeopleHaveLastNames) - end - def test_migrator_interleaved_migrations ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1") -- cgit v1.2.3 From 62d5bb26864f3139354b677fbf9cd9c5b13962b1 Mon Sep 17 00:00:00 2001 From: Samus_ Date: Fri, 3 Dec 2010 18:05:52 -0200 Subject: typo --- railties/guides/source/active_record_querying.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index e41b5fb606..b9ad7ccbd2 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -450,7 +450,7 @@ SQL uses the +HAVING+ clause to specify conditions on the +GROUP BY+ fields. You For example: -Order.group("date(created_at)".having("created_at > ?", 1.month.ago) +Order.group("date(created_at)").having("created_at > ?", 1.month.ago) The SQL that would be executed would be something like this: -- cgit v1.2.3 From d21f38aaa204ee8df81bd34a6dad3d6de8ad63d9 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 3 Dec 2010 15:33:59 -0800 Subject: use the columns hash for primary key lookup --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 1da7411589..51ba17c9be 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -290,7 +290,7 @@ module ActiveRecord def find_one(id) id = id.id if ActiveRecord::Base === id - column = primary_key.column + column = columns_hash[primary_key.name.to_s] substitute = connection.substitute_for(column, @bind_values) relation = where(primary_key.eq(substitute)) -- cgit v1.2.3 From a299fcaef90741090c5719d0569715c209149a59 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 3 Dec 2010 16:22:42 -0800 Subject: not a responsibility for rails to test --- activerecord/test/cases/associations/join_model_test.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 1ece961d2e..4581cb1acd 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -44,16 +44,6 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert !authors(:mary).unique_categorized_posts.loaded? end - def test_column_caching - # pre-heat our cache - Post.arel_table.columns - Comment.columns - - Post.connection.column_calls = 0 - 2.times { Post.joins(:comments).to_a } - assert_equal 0, Post.connection.column_calls - end - def test_has_many_uniq_through_find assert_equal 1, authors(:mary).unique_categorized_posts.find(:all).size end -- cgit v1.2.3 From c4669d3c027e5d9252ecfbf3a9792fbdb4d878d0 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sat, 4 Dec 2010 11:27:17 +1100 Subject: Config guide: continuing work on documenting the initializers --- railties/guides/source/configuring.textile | 70 ++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 0236e72529..afafabd1e9 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -497,11 +497,75 @@ Sets +ActionController::Base.cache_store+ -- if it's not already set -- to +RAI h4. +action_controller.set_configs+ -Sets up Action View by using the settings in +config.action_controller+ by +send+'ing the method names as setters to +ActionController::Base+ and passing the values through. +Sets up Action Controller by using the settings in +config.action_controller+ by +send+'ing the method names as setters to +ActionController::Base+ and passing the values through. + +h4. +action_controller.compile_config_methods+ + +Initializes methods for the config settings specified so that they are quicker to access. + +h4. +active_record.initialize_timezone+ + +Sets +ActiveRecord::Base.time_zone_aware_attributes+ to true, as well as setting +ActiveRecord::Base.default_timezone+ to UTC. When attributes are read from the database, they will be converted into the time zone specified by +Time.zone+. + +h4. +active_record.logger+ + +Sets +ActiveRecord::Base.logger+ -- if it's not already set -- to +Rails.logger+. + +h4. +active_record.set_configs+ + +Sets up Active Record by using the settings in +config.active_record+ by +send+'ing the method names as setters to +ActiveRecord::Base+ and passing the values through. + +h4. +active_record.initialize_database+ + +Loads the database configuration (by default) from +config/database.yml+ and establishes a connection for the current environment. + +h4. +active_record.log_runtime+ + +Includes +ActiveRecord::Railties::ControllerRuntime+ which is responsible for reporting the length of time Active Record calls took for the request back to the logger. + +h4. +active_record.set_dispatch_hooks+ + +If +config.cache_classes+ is set to false, all reloadable connections to the database will be reset. + +h4. +action_mailer.logger+ + +Sets +ActionMailer::Base.logger+ -- if it's not already set -- to +Rails.logger+. + +h4. +action_mailer.set_configs+ + +Sets up Action Mailer by using the settings in +config.action_mailer+ by +send+'ing the method names as setters to +ActionMailer::Base+ and passing the values through. + +h4. +action_mailer.compile_config_methods+ + +Initializes methods for the config settings specified so that they are quicker to access. + +h4. +active_resource.set_configs+ + +Sets up Active Resource by using the settings in +config.active_resource+ by +send+'ing the method names as setters to +ActiveResource::Base+ and passing the values through. + +h4. +set_load_path+ + +This initializer runs before +bootstrap_hook+. Adds the +vendor+, +lib+, all directories of +app+ and any paths specified by +config.load_paths+ to +$LOAD_PATH+. + +h4. +set_autoload_path+ + +This initializer runs before +bootstrap_hook+. Adds all sub-directories of +app+ and paths specified by +config.autoload_paths+ to +ActiveSupport::Dependencies.autoload_paths+. + +h4. +add_routing_paths+ + +Loads (by default) all +config/routes.rb+ files (in the application and railties, including engines) and sets up the routes for the application. + +h4. +add_locales+ + +Adds the files in +config/locales+ (from the application, railties and engines) to +I18n.load_path+, making available the translations in these files. + +h4. +add_view_paths+ + +Adds the directory +app/views+ from the application, railties and engines to the lookup path for view files for the application. + +h4. +load_environment_config+ -h4 +active_record.initialize_timezone+ -Sets +ActiveRecord::Base.time_zone_aware_attributes+ to true, as well as setting +ActiveRecord::Base.default_timezone+ to UTC. h4. Initializer files -- cgit v1.2.3 From 7ffd5daa7fe1fd805fd071d40dfad1773299c7d1 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 14 Nov 2010 18:30:09 +0100 Subject: bump i18n gem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/actionpack.gemspec | 2 +- activemodel/activemodel.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 44967631ff..b7d1d8c2af 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', version) s.add_dependency('rack-cache', '~> 0.5.3') s.add_dependency('builder', '~> 3.0.0') - s.add_dependency('i18n', '~> 0.4.2') + s.add_dependency('i18n', '~> 0.5.0') s.add_dependency('rack', '~> 1.2.1') s.add_dependency('rack-test', '~> 0.5.6') s.add_dependency('rack-mount', '~> 0.6.13') diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index 318d71a610..1f38e70c36 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -21,5 +21,5 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('builder', '~> 3.0.0') - s.add_dependency('i18n', '~> 0.4.2') + s.add_dependency('i18n', '~> 0.5.0') end -- cgit v1.2.3 From 896e25e994e272a1cb485d9a850501b488992cb2 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 14 Nov 2010 18:46:40 +0100 Subject: Make TranslationHelper#translate use the :rescue_format option in I18n 0.5.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't catch exceptions here. Instead only declare that we want exceptions to be rescued as :html, but also let users configure reactions to exceptions in I18n. Signed-off-by: José Valim --- .../lib/action_view/helpers/translation_helper.rb | 32 ++++++++--- .../test/fixtures/test/scoped_translation.erb | 1 - actionpack/test/fixtures/test/translation.erb | 1 - .../test/fixtures/translations/templates/array.erb | 1 + .../test/fixtures/translations/templates/found.erb | 1 + .../fixtures/translations/templates/missing.erb | 1 + .../test/template/translation_helper_test.rb | 67 +++++++++++++--------- 7 files changed, 68 insertions(+), 36 deletions(-) delete mode 100644 actionpack/test/fixtures/test/scoped_translation.erb delete mode 100644 actionpack/test/fixtures/test/translation.erb create mode 100644 actionpack/test/fixtures/translations/templates/array.erb create mode 100644 actionpack/test/fixtures/translations/templates/found.erb create mode 100644 actionpack/test/fixtures/translations/templates/missing.erb diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 8574ca6595..e7ec1df2c8 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -1,13 +1,33 @@ require 'action_view/helpers/tag_helper' +require 'i18n/exceptions' + +module I18n + class ExceptionHandler + include Module.new { + def call(exception, locale, key, options) + exception.is_a?(MissingTranslationData) ? super.html_safe : super + end + } + end +end module ActionView # = Action View Translation Helpers module Helpers module TranslationHelper # Delegates to I18n#translate but also performs three additional functions. - # First, it'll catch MissingTranslationData exceptions and turn them into - # inline spans that contains the missing key, such that you can see in a - # view what is missing where. + # + # First, it'll pass the :rescue_format => :html option to I18n so that any caught + # MissingTranslationData exceptions will be turned into inline spans that + # + # * have a "translation-missing" class set, + # * contain the missing key as a title attribute and + # * a titleized version of the last key segment as a text. + # + # E.g. the value returned for a missing translation key :"blog.post.title" will be + # Title. + # This way your views will display rather reasonableful strings but it will still + # be easy to spot missing translations. # # Second, it'll scope the key by the current partial if the key starts # with a period. So if you call translate(".foo") from the @@ -24,15 +44,13 @@ module ActionView # naming convention helps to identify translations that include HTML tags so that # you know what kind of output to expect when you call translate in a template. def translate(key, options = {}) - translation = I18n.translate(scope_key_by_partial(key), options.merge!(:raise => true)) + options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) + translation = I18n.translate(scope_key_by_partial(key), options) if html_safe_translation_key?(key) && translation.respond_to?(:html_safe) translation.html_safe else translation end - rescue I18n::MissingTranslationData => e - keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope]) - content_tag('span', keys.join(', '), :class => 'translation_missing') end alias :t :translate diff --git a/actionpack/test/fixtures/test/scoped_translation.erb b/actionpack/test/fixtures/test/scoped_translation.erb deleted file mode 100644 index 3be63ab3cc..0000000000 --- a/actionpack/test/fixtures/test/scoped_translation.erb +++ /dev/null @@ -1 +0,0 @@ -<%= t('.foo.bar').join %> \ No newline at end of file diff --git a/actionpack/test/fixtures/test/translation.erb b/actionpack/test/fixtures/test/translation.erb deleted file mode 100644 index 81a837d1ff..0000000000 --- a/actionpack/test/fixtures/test/translation.erb +++ /dev/null @@ -1 +0,0 @@ -<%= t('.helper') %> \ No newline at end of file diff --git a/actionpack/test/fixtures/translations/templates/array.erb b/actionpack/test/fixtures/translations/templates/array.erb new file mode 100644 index 0000000000..d86045a172 --- /dev/null +++ b/actionpack/test/fixtures/translations/templates/array.erb @@ -0,0 +1 @@ +<%= t('.foo.bar') %> diff --git a/actionpack/test/fixtures/translations/templates/found.erb b/actionpack/test/fixtures/translations/templates/found.erb new file mode 100644 index 0000000000..080c9c0aee --- /dev/null +++ b/actionpack/test/fixtures/translations/templates/found.erb @@ -0,0 +1 @@ +<%= t('.foo') %> diff --git a/actionpack/test/fixtures/translations/templates/missing.erb b/actionpack/test/fixtures/translations/templates/missing.erb new file mode 100644 index 0000000000..0f3f17f8ef --- /dev/null +++ b/actionpack/test/fixtures/translations/templates/missing.erb @@ -0,0 +1 @@ +<%= t('.missing') %> diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index 763080550b..67d459136b 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -4,60 +4,73 @@ class TranslationHelperTest < ActiveSupport::TestCase include ActionView::Helpers::TagHelper include ActionView::Helpers::TranslationHelper - attr_reader :request + attr_reader :request, :view + def setup + I18n.backend.store_translations(:en, + :translations => { + :templates => { + :found => { :foo => 'Foo' }, + :array => { :foo => { :bar => 'Foo Bar' } } + }, + :foo => 'Foo', + :hello => 'Hello World', + :html => 'Hello World', + :hello_html => 'Hello World', + :array_html => %w(foo bar), + :array => %w(foo bar) + } + ) + @view = ::ActionView::Base.new(ActionController::Base.view_paths, {}) end - def test_delegates_to_i18n_setting_the_raise_option - I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true).returns("") + def test_delegates_to_i18n_setting_the_rescue_format_option_to_html + I18n.expects(:translate).with(:foo, :locale => 'en', :rescue_format => :html).returns("") translate :foo, :locale => 'en' end + def test_delegates_localize_to_i18n + @time = Time.utc(2008, 7, 8, 12, 18, 38) + I18n.expects(:localize).with(@time) + localize @time + end + def test_returns_missing_translation_message_wrapped_into_span - expected = 'en, foo' - assert_equal expected, translate(:foo) + expected = 'Missing' + assert_equal expected, translate(:"translations.missing") end def test_translation_returning_an_array - I18n.expects(:translate).with(:foo, :raise => true).returns(["foo", "bar"]) - assert_equal ["foo", "bar"], translate(:foo) + expected = %w(foo bar) + assert_equal expected, translate(:"translations.array") end - def test_delegates_localize_to_i18n - @time = Time.utc(2008, 7, 8, 12, 18, 38) - I18n.expects(:localize).with(@time) - localize @time + def test_finds_translation_scoped_by_partial + assert_equal 'Foo', view.render(:file => 'translations/templates/found').strip end - def test_scoping_by_partial - I18n.expects(:translate).with("test.translation.helper", :raise => true).returns("helper") - @view = ::ActionView::Base.new(ActionController::Base.view_paths, {}) - assert_equal "helper", @view.render(:file => "test/translation") + def test_finds_array_of_translations_scoped_by_partial + assert_equal 'Foo Bar', @view.render(:file => 'translations/templates/array').strip end - def test_scoping_by_partial_of_an_array - I18n.expects(:translate).with("test.scoped_translation.foo.bar", :raise => true).returns(["foo", "bar"]) - @view = ::ActionView::Base.new(ActionController::Base.view_paths, {}) - assert_equal "foobar", @view.render(:file => "test/scoped_translation") + def test_missing_translation_scoped_by_partial + expected = 'Missing' + assert_equal expected, view.render(:file => 'translations/templates/missing').strip end def test_translate_does_not_mark_plain_text_as_safe_html - I18n.expects(:translate).with("hello", :raise => true).returns("Hello World") - assert_equal false, translate("hello").html_safe? + assert_equal false, translate(:'translations.hello').html_safe? end def test_translate_marks_translations_named_html_as_safe_html - I18n.expects(:translate).with("html", :raise => true).returns("Hello World") - assert translate("html").html_safe? + assert translate(:'translations.html').html_safe? end def test_translate_marks_translations_with_a_html_suffix_as_safe_html - I18n.expects(:translate).with("hello_html", :raise => true).returns("Hello World") - assert translate("hello_html").html_safe? + assert translate(:'translations.hello_html').html_safe? end def test_translation_returning_an_array_ignores_html_suffix - I18n.expects(:translate).with(:foo_html, :raise => true).returns(["foo", "bar"]) - assert_equal ["foo", "bar"], translate(:foo_html) + assert_equal ["foo", "bar"], translate(:'translations.array_html') end end -- cgit v1.2.3 From 3e842559427746eb9051ce5ee3cd36caeff303a3 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 3 Dec 2010 22:31:05 -0200 Subject: attributes_before_type_cast are just the value of @attributes --- activerecord/lib/active_record/attribute_methods/before_type_cast.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb index 23195b02f7..f7e5ef853f 100644 --- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb +++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb @@ -13,7 +13,7 @@ module ActiveRecord # Returns a hash of attributes before typecasting and deserialization. def attributes_before_type_cast - Hash[attribute_names.map { |name| [name, read_attribute_before_type_cast(name)] }] + @attributes end private -- cgit v1.2.3 From a11ddf3ee2d2db0378c241a1fd0648b74ff48e20 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 3 Dec 2010 22:46:45 -0200 Subject: Fix indentation here --- .../active_record/attribute_methods/before_type_cast.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb index f7e5ef853f..bde11d0494 100644 --- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb +++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb @@ -17,14 +17,15 @@ module ActiveRecord end private - # Handle *_before_type_cast for method_missing. - def attribute_before_type_cast(attribute_name) - if attribute_name == 'id' - read_attribute_before_type_cast(self.class.primary_key) - else - read_attribute_before_type_cast(attribute_name) - end + + # Handle *_before_type_cast for method_missing. + def attribute_before_type_cast(attribute_name) + if attribute_name == 'id' + read_attribute_before_type_cast(self.class.primary_key) + else + read_attribute_before_type_cast(attribute_name) end + end end end end -- cgit v1.2.3 From be4551f63bc3dec7e182371d25e7b9ee0e9148c4 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 4 Dec 2010 19:01:00 -0200 Subject: Use ||= here --- actionpack/lib/action_view/helpers/translation_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index e7ec1df2c8..76dac20f4b 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -44,7 +44,7 @@ module ActionView # naming convention helps to identify translations that include HTML tags so that # you know what kind of output to expect when you call translate in a template. def translate(key, options = {}) - options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) + options[:rescue_format] ||= :html translation = I18n.translate(scope_key_by_partial(key), options) if html_safe_translation_key?(key) && translation.respond_to?(:html_safe) translation.html_safe -- cgit v1.2.3 From b696dd70db651c6cb4a096dd0d5762416acd9917 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 4 Dec 2010 20:07:54 -0200 Subject: Allow back passing :rescue_format => nil and test added --- actionpack/lib/action_view/helpers/translation_helper.rb | 2 +- actionpack/test/template/translation_helper_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 76dac20f4b..e7ec1df2c8 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -44,7 +44,7 @@ module ActionView # naming convention helps to identify translations that include HTML tags so that # you know what kind of output to expect when you call translate in a template. def translate(key, options = {}) - options[:rescue_format] ||= :html + options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) translation = I18n.translate(scope_key_by_partial(key), options) if html_safe_translation_key?(key) && translation.respond_to?(:html_safe) translation.html_safe diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index 67d459136b..9b5c6d127c 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -40,6 +40,11 @@ class TranslationHelperTest < ActiveSupport::TestCase assert_equal expected, translate(:"translations.missing") 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) + end + def test_translation_returning_an_array expected = %w(foo bar) assert_equal expected, translate(:"translations.array") -- cgit v1.2.3 From e1d0d5ae81d2b02eebb1afbfb79edf0a2da2d48e Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Sat, 4 Dec 2010 22:40:05 -0200 Subject: Fix mime type doc typos --- actionpack/lib/action_dispatch/http/mime_type.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 08eab5634a..c85aa64572 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -173,10 +173,10 @@ module Mime end # input: 'text' - # returend value: [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT] + # returned value: [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT] # # input: 'application' - # returend value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM + # returned value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM] def parse_data_with_trailing_star(input) keys = Mime::LOOKUP.keys.select{|k| k.include?(input)} Mime::LOOKUP.values_at(*keys).uniq -- cgit v1.2.3 From b870d6729090865f0a21c2ea6b9f1fffdd1c4d7e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 12:31:06 -0200 Subject: Test using default option added for human_attribute_name --- activemodel/test/cases/translation_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index ac2e56321e..f0cb02eb5c 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -17,6 +17,10 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'name default attribute', Person.human_attribute_name('name') end + def test_translated_model_attributes_using_default_option + assert_equal 'name default attribute', Person.human_attribute_name('name', :default => "name default attribute") + end + def test_translated_model_attributes_with_symbols I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } assert_equal 'person name attribute', Person.human_attribute_name(:name) -- cgit v1.2.3 From 0dbf4ac709c0c31d4d6664bcb53b861c6d6b0157 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 12:32:29 -0200 Subject: Test falling back to default added for human_attribute_name --- activemodel/test/cases/translation_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index f0cb02eb5c..da6cb98bc9 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -21,6 +21,10 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'name default attribute', Person.human_attribute_name('name', :default => "name default attribute") end + def test_translated_model_attributes_falling_back_to_default + assert_equal 'Name', Person.human_attribute_name('name') + end + def test_translated_model_attributes_with_symbols I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } assert_equal 'person name attribute', Person.human_attribute_name(:name) -- cgit v1.2.3 From 3ab98cf6e0d399042424d44a788b8bc679b8dd1c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 12:34:58 -0200 Subject: If default is provided don't add attribute.to_s.humanize to the options --- activemodel/lib/active_model/translation.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index 920a133159..6959f99b5e 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -48,8 +48,7 @@ module ActiveModel end defaults << :"attributes.#{attribute}" - defaults << options.delete(:default) if options[:default] - defaults << attribute.to_s.humanize + defaults << (options[:default] ? options.delete(:default) : attribute.to_s.humanize) options.reverse_merge! :count => 1, :default => defaults I18n.translate(defaults.shift, options) -- cgit v1.2.3 From c4809d9984ab5d20759075a96ddea6096ab93802 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 3 Dec 2010 22:41:54 -0200 Subject: use map instead of each --- activerecord/lib/active_record/base.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index ba7f6f0129..f54c9d118d 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1500,9 +1500,7 @@ MSG # Returns a hash of all the attributes with their names as keys and the values of the attributes as values. def attributes - attrs = {} - attribute_names.each { |name| attrs[name] = read_attribute(name) } - attrs + Hash[attribute_names.map { |name| [name, read_attribute(name)] }] end # Returns an #inspect-like string for the value of the -- cgit v1.2.3 From 20ba81e47d04b0ffd510c4de9c65a439b22a00b2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 3 Dec 2010 22:45:02 -0200 Subject: We can use the keys of the @attributes hash here and avoid a method call --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f54c9d118d..b55aaddbd7 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1500,7 +1500,7 @@ MSG # Returns a hash of all the attributes with their names as keys and the values of the attributes as values. def attributes - Hash[attribute_names.map { |name| [name, read_attribute(name)] }] + Hash[@attributes.map { |name, _| [name, read_attribute(name)] }] end # Returns an #inspect-like string for the value of the -- cgit v1.2.3 From 33b0a30fcc5694d26034bc74ed61e34edecbfbc4 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 12:57:45 -0200 Subject: default could be a symbol here so attribute.to_s.humanize should be the final option --- activemodel/lib/active_model/translation.rb | 3 ++- activemodel/test/cases/translation_test.rb | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index 6959f99b5e..920a133159 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -48,7 +48,8 @@ module ActiveModel end defaults << :"attributes.#{attribute}" - defaults << (options[:default] ? options.delete(:default) : attribute.to_s.humanize) + defaults << options.delete(:default) if options[:default] + defaults << attribute.to_s.humanize options.reverse_merge! :count => 1, :default => defaults I18n.translate(defaults.shift, options) diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index da6cb98bc9..17c1083cf5 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -25,6 +25,10 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'Name', Person.human_attribute_name('name') end + def test_translated_model_attributes_using_default_option_as_symbol_and_falling_back_to_default + assert_equal 'Name', Person.human_attribute_name('name', :default => :default_name) + end + def test_translated_model_attributes_with_symbols I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } } assert_equal 'person name attribute', Person.human_attribute_name(:name) -- cgit v1.2.3 From 7c920631ec3b314cfaa3a60d265de40cba3e8135 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 5 Dec 2010 13:26:14 -0200 Subject: Test using default option as symbol added for human_attribute_name --- activemodel/test/cases/translation_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index 17c1083cf5..c299d6eb5e 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -21,6 +21,11 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'name default attribute', Person.human_attribute_name('name', :default => "name default attribute") end + def test_translated_model_attributes_using_default_option_as_symbol + I18n.backend.store_translations 'en', :default_name => 'name default attribute' + assert_equal 'name default attribute', Person.human_attribute_name('name', :default => :default_name) + end + def test_translated_model_attributes_falling_back_to_default assert_equal 'Name', Person.human_attribute_name('name') end -- cgit v1.2.3 From 638b409eb4c75b9187c6cedc9f71e2e38ec84731 Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Sun, 5 Dec 2010 09:46:52 -0800 Subject: fixed typo in test method name --- railties/guides/source/testing.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index c292a5d83b..c9109a0ddf 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -183,7 +183,7 @@ class PostTest < ActiveSupport::TestCase The +PostTest+ class defines a _test case_ because it inherits from +ActiveSupport::TestCase+. +PostTest+ thus has all the methods available from +ActiveSupport::TestCase+. You'll see those methods a little later in this guide. -def test_truth +def test_the_truth Any method defined within a +Test::Unit+ test case that begins with +test+ (case sensitive) is simply called a test. So, +test_password+, +test_valid_password+ and +testValidPassword+ all are legal test names and are run automatically when the test case is run. @@ -256,7 +256,7 @@ This will run all the test methods from the test case. Note that +test_helper.rb You can also run a particular test method from the test case by using the +-n+ switch with the +test method name+. -$ ruby -Itest test/unit/post_test.rb -n test_truth +$ ruby -Itest test/unit/post_test.rb -n test_the_truth Loaded suite unit/post_test Started -- cgit v1.2.3 From 323a64ecb551bf87d13337ab47a3051d94fc4894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Cat=C3=B3n?= Date: Sun, 5 Dec 2010 15:58:29 -0200 Subject: Fix link on generations' page --- railties/guides/source/generators.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index a0d744333f..ee3891c43b 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -134,7 +134,7 @@ $ rails generate initializer core_extensions We can see that now a initializer named core_extensions was created at +config/initializers/core_extensions.rb+ with the contents of our template. That means that +copy_file+ copied a file in our source root to the destination path we gave. The method +file_name+ is automatically created when we inherit from +Rails::Generators::NamedBase+. -The methods that are available for generators are covered in the "final section":#generator_methods of this guide. +The methods that are available for generators are covered in the "final section":#generator-methods of this guide. h3. Generators Lookup -- cgit v1.2.3 From 3ac844deec6070f35634acca0ae9007280b13e5d Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 6 Dec 2010 16:38:47 -0500 Subject: Add to documentation that action caching does handle HTTP_ACCEPT attribute properly and might provide wrong result. Use params[:format] to avoid this issue. --- actionpack/lib/action_controller/caching/actions.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index d69d96b974..cb79e51dbf 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -71,6 +71,11 @@ module ActionController #:nodoc: # If you pass :layout => false, it will only cache your action # content. It is useful when your layout has dynamic information. # + # Note: If action caching is being performed for different MIME types + # and those MIME types are being determined by HTTP_ACCEPT header atttribute + # and noth using params[:format] then both the cached data and the content-type + # of the response could be wrong. The safest way to use action caching is to + # pass non-html attribute as params[:format] . module Actions extend ActiveSupport::Concern -- cgit v1.2.3 From 70ab4502749d23464f06d50779aeea0b3275d67c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 7 Dec 2010 14:01:37 -0800 Subject: cleaning up custom_join_sql method --- activerecord/lib/active_record/relation/query_methods.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0483950db7..2933d427f0 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -163,13 +163,15 @@ module ActiveRecord end def custom_join_sql(joins) - arel = table.select_manager + joins = joins.reject { |join| join.blank? } - joins.each do |join| - next if join.blank? + return if joins.empty? - @implicit_readonly = true + @implicit_readonly = true + arel = table.select_manager + + joins.each do |join| case join when Array join = Arel.sql(join.join(' ')) if array_of_strings?(join) -- cgit v1.2.3 From ef79658bd143a9243eefa90db2f9ce300f18dd0d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 7 Dec 2010 14:47:21 -0800 Subject: save the AR reference rather than delegating --- .../active_record/associations/class_methods/join_dependency.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index c578845878..9f171f891d 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -6,9 +6,10 @@ module ActiveRecord module Associations module ClassMethods class JoinDependency # :nodoc: - attr_reader :join_parts, :reflections, :table_aliases + attr_reader :join_parts, :reflections, :table_aliases, :active_record def initialize(base, associations, joins) + @active_record = base @table_joins = joins || '' @join_parts = [JoinBase.new(base)] @associations = {} @@ -45,7 +46,7 @@ module ActiveRecord def count_aliases_from_table_joins(name) # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase - quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase + quoted_name = active_record.connection.quote_table_name(name.downcase).downcase join_sql = @table_joins.downcase join_sql.blank? ? 0 : # Table names @@ -61,11 +62,11 @@ module ActiveRecord records = rows.map { |model| primary_id = model[primary_key] parent = parents[primary_id] ||= join_base.instantiate(model) - construct(parent, @associations, join_associations.dup, model) + construct(parent, @associations, join_associations, model) parent }.uniq - remove_duplicate_results!(join_base.active_record, records, @associations) + remove_duplicate_results!(active_record, records, @associations) records end -- cgit v1.2.3 From 4c4c7a272fdcec12691ccf48b5eefce49b7bac8d Mon Sep 17 00:00:00 2001 From: Pavel Gorbokon Date: Tue, 7 Dec 2010 18:55:43 +0200 Subject: Ommit nil in method call Signed-off-by: Santiago Pastorino --- activesupport/lib/active_support/callbacks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 905dfb040b..4dcbbb819a 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -388,7 +388,7 @@ module ActiveSupport # key. See #define_callbacks for more information. # def __define_runner(symbol) #:nodoc: - body = send("_#{symbol}_callbacks").compile(nil) + body = send("_#{symbol}_callbacks").compile silence_warnings do undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") -- cgit v1.2.3 From 1ce9b73e5c7cbf6c29c57bb60c97eb2d2c10801d Mon Sep 17 00:00:00 2001 From: Pavel Gorbokon Date: Tue, 7 Dec 2010 19:00:49 +0200 Subject: Replace nested ifs with case/when Signed-off-by: Santiago Pastorino --- activesupport/lib/active_support/callbacks.rb | 90 +++++++++++++-------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 4dcbbb819a..844237fe6a 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -178,49 +178,48 @@ module ActiveSupport # options[0] is the compiled form of supplied conditions # options[1] is the "end" for the conditional # - if @kind == :before || @kind == :around - if @kind == :before - # if condition # before_save :filter_name, :if => :condition - # filter_name - # end - filter = <<-RUBY_EVAL - unless halted - result = #{@filter} - halted = (#{chain.config[:terminator]}) - end - RUBY_EVAL - - [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n") - else - # Compile around filters with conditions into proxy methods - # that contain the conditions. - # - # For `around_save :filter_name, :if => :condition': - # - # def _conditional_callback_save_17 - # if condition - # filter_name do - # yield self - # end - # else - # yield self - # end - # end - # - name = "_conditional_callback_#{@kind}_#{next_id}" - @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{name}(halted) - #{@compiled_options[0] || "if true"} && !halted - #{@filter} do - yield self - end - else + case @kind + when :before + # if condition # before_save :filter_name, :if => :condition + # filter_name + # end + filter = <<-RUBY_EVAL + unless halted + result = #{@filter} + halted = (#{chain.config[:terminator]}) + end + RUBY_EVAL + + [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n") + when :around + # Compile around filters with conditions into proxy methods + # that contain the conditions. + # + # For `around_save :filter_name, :if => :condition': + # + # def _conditional_callback_save_17 + # if condition + # filter_name do + # yield self + # end + # else + # yield self + # end + # end + # + name = "_conditional_callback_#{@kind}_#{next_id}" + @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{name}(halted) + #{@compiled_options[0] || "if true"} && !halted + #{@filter} do yield self end + else + yield self end - RUBY_EVAL - "#{name}(halted) do" - end + end + RUBY_EVAL + "#{name}(halted) do" end end @@ -229,15 +228,14 @@ module ActiveSupport def end(key=nil, object=nil) return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?") - if @kind == :around || @kind == :after + case @kind + when :after # if condition # after_save :filter_name, :if => :condition # filter_name # end - if @kind == :after - [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n") - else - "end" - end + [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n") + when :around + "end" end end -- cgit v1.2.3 From ddbd2039615ca4376a46a97a17612732f24568cd Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 7 Dec 2010 15:23:29 -0800 Subject: reduce string objects, reduce method calls --- .../active_record/associations/class_methods/join_dependency.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 9f171f891d..34928bc764 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -10,7 +10,7 @@ module ActiveRecord def initialize(base, associations, joins) @active_record = base - @table_joins = joins || '' + @table_joins = joins @join_parts = [JoinBase.new(base)] @associations = {} @reflections = [] @@ -45,12 +45,13 @@ module ActiveRecord end def count_aliases_from_table_joins(name) + return 0 unless @table_joins + # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase quoted_name = active_record.connection.quote_table_name(name.downcase).downcase join_sql = @table_joins.downcase - join_sql.blank? ? 0 : - # Table names - join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + + # Table names + join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + # Table aliases join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size end -- cgit v1.2.3 From d6133209ce54bac430e63ec512251eae182b79e1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 7 Dec 2010 15:23:50 -0800 Subject: arel is lazy about column information, so no need to pass it around --- .../associations/class_methods/join_dependency/join_base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb index 97003c1457..67567f06df 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb @@ -13,7 +13,7 @@ module ActiveRecord end def table - Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns) + Arel::Table.new(table_name, arel_engine) end def aliased_table_name -- cgit v1.2.3 From 2d9d6cd4c263f639adf8e26c7d95e7772e0c4eb7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 7 Dec 2010 16:48:59 -0800 Subject: passing the ast to JoinDependency --- .../associations/class_methods/join_dependency.rb | 13 +++++++++++-- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 34928bc764..86d43f21d3 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -45,11 +45,20 @@ module ActiveRecord end def count_aliases_from_table_joins(name) - return 0 unless @table_joins + return 0 if !@table_joins || Arel::Table === @table_joins + return count_aliases_from_string(@table_joins.downcase, name) if String === @table_joins + + @table_joins.grep(Arel::Table).find_all { |table| + table.name.downcase == name + }.length + @table_joins.grep(String).map { |s| + count_aliases_from_string(s, name) + }.sum + end + + def count_aliases_from_string(join_sql, name) # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase quoted_name = active_record.connection.quote_table_name(name.downcase).downcase - join_sql = @table_joins.downcase # Table names join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + # Table aliases diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 51ba17c9be..906ad7699c 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -196,7 +196,7 @@ module ActiveRecord def construct_relation_for_association_calculations including = (@eager_load_values + @includes_values).uniq - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.join_sql) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.froms.first) relation = except(:includes, :eager_load, :preload) apply_join_dependency(relation, join_dependency) end -- cgit v1.2.3 From d98cb5153dad97cde82ec91a1993043cfa9bc0e4 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 7 Dec 2010 17:05:15 -0800 Subject: JoinDependency is always created with an AST now --- .../associations/class_methods/join_dependency.rb | 14 ++++++++++---- activerecord/lib/active_record/relation/query_methods.rb | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 86d43f21d3..928e8152a5 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -49,10 +49,16 @@ module ActiveRecord return count_aliases_from_string(@table_joins.downcase, name) if String === @table_joins - @table_joins.grep(Arel::Table).find_all { |table| - table.name.downcase == name - }.length + @table_joins.grep(String).map { |s| - count_aliases_from_string(s, name) + @table_joins.grep(Arel::Nodes::Join).map { |join| + right = join.right + case right + when Arel::Table + right.name.downcase == name ? 1 : 0 + when String + count_aliases_from_string(right.downcase, name) + else + 0 + end }.sum end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 2933d427f0..194ce881e0 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -185,6 +185,20 @@ module ActiveRecord arel.join_sql end + def custom_join_ast(joins) + joins = joins.reject { |join| join.blank? } + + return if joins.empty? + + @implicit_readonly = true + + head = table.create_string_join(table, joins.shift) + + joins.inject(head) do |ast, join| + ast.right = table.create_string_join(ast.right, join) + end + end + def build_arel arel = table @@ -256,7 +270,7 @@ module ActiveRecord non_association_joins = (joins - association_joins - stashed_association_joins) custom_joins = custom_join_sql(non_association_joins) - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_join_ast(non_association_joins)) join_dependency.graft(*stashed_association_joins) -- cgit v1.2.3 From 1d96d44da318faab8d213f330aebcecdbec52f5d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 7 Dec 2010 18:54:04 -0800 Subject: passing the ast to a table when the relation is a table --- .../associations/class_methods/join_dependency.rb | 2 -- .../lib/active_record/relation/query_methods.rb | 23 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 928e8152a5..d508c3b7aa 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -47,8 +47,6 @@ module ActiveRecord def count_aliases_from_table_joins(name) return 0 if !@table_joins || Arel::Table === @table_joins - return count_aliases_from_string(@table_joins.downcase, name) if String === @table_joins - @table_joins.grep(Arel::Nodes::Join).map { |join| right = join.right case right diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 194ce881e0..df41f756ce 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -185,18 +185,30 @@ module ActiveRecord arel.join_sql end - def custom_join_ast(joins) + def custom_join_ast(table, joins) joins = joins.reject { |join| join.blank? } return if joins.empty? @implicit_readonly = true + joins.map! do |join| + case join + when Array + join = Arel.sql(join.join(' ')) if array_of_strings?(join) + when String + join = Arel.sql(join) + end + join + end + head = table.create_string_join(table, joins.shift) joins.inject(head) do |ast, join| ast.right = table.create_string_join(ast.right, join) end + + head end def build_arel @@ -269,8 +281,9 @@ module ActiveRecord non_association_joins = (joins - association_joins - stashed_association_joins) custom_joins = custom_join_sql(non_association_joins) + ast = custom_join_ast(relation, non_association_joins) - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_join_ast(non_association_joins)) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, ast) join_dependency.graft(*stashed_association_joins) @@ -280,7 +293,11 @@ module ActiveRecord relation = association.join_to(relation) end - relation.join(custom_joins) + if Arel::Table === relation + relation.from(ast || relation) + else + relation.join(custom_joins) + end end def build_select(arel, selects) -- cgit v1.2.3 From 2fd385d47163149ec74029731bc29370fa768735 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 10:29:52 -0800 Subject: reducing use of custom joins --- activerecord/lib/active_record/relation/query_methods.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index df41f756ce..705bf21eae 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -296,7 +296,12 @@ module ActiveRecord if Arel::Table === relation relation.from(ast || relation) else - relation.join(custom_joins) + if relation.froms.length > 0 && ast + ast.left = relation.froms.first + relation.from ast + else + relation.join(custom_joins) + end end end -- cgit v1.2.3 From ddd6dee04343d3731b2f2fc558501c3707cde8fb Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 10:35:26 -0800 Subject: further reducing dependence on custom_joins --- activerecord/lib/active_record/relation/query_methods.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 705bf21eae..3a2cbc8dda 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -299,6 +299,8 @@ module ActiveRecord if relation.froms.length > 0 && ast ast.left = relation.froms.first relation.from ast + elsif relation.froms.length == 0 && ast + relation.from(ast) else relation.join(custom_joins) end -- cgit v1.2.3 From 3499f882cc62e891057f806cf9a09a84f97a5e5c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 10:54:24 -0800 Subject: renaming variables, making the join_ast method private --- .../lib/active_record/relation/query_methods.rb | 78 ++++++++-------------- 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 3a2cbc8dda..f9e5d7d925 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -162,29 +162,32 @@ module ActiveRecord @arel ||= build_arel end - def custom_join_sql(joins) - joins = joins.reject { |join| join.blank? } + def build_arel + arel = table - return if joins.empty? + arel = build_joins(arel, @joins_values) unless @joins_values.empty? - @implicit_readonly = true + arel = collapse_wheres(arel, (@where_values - ['']).uniq) - arel = table.select_manager + arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? - joins.each do |join| - case join - when Array - join = Arel.sql(join.join(' ')) if array_of_strings?(join) - when String - join = Arel.sql(join) - end + arel = arel.take(@limit_value) if @limit_value + arel = arel.skip(@offset_value) if @offset_value - arel.join(join) - end + arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? - arel.join_sql + arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty? + + arel = build_select(arel, @select_values.uniq) + + arel = arel.from(@from_value) if @from_value + arel = arel.lock(@lock_value) if @lock_value + + arel end + private + def custom_join_ast(table, joins) joins = joins.reject { |join| join.blank? } @@ -211,32 +214,6 @@ module ActiveRecord head end - def build_arel - arel = table - - arel = build_joins(arel, @joins_values) unless @joins_values.empty? - - arel = collapse_wheres(arel, (@where_values - ['']).uniq) - - arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? - - arel = arel.take(@limit_value) if @limit_value - arel = arel.skip(@offset_value) if @offset_value - - arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? - - arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty? - - arel = build_select(arel, @select_values.uniq) - - arel = arel.from(@from_value) if @from_value - arel = arel.lock(@lock_value) if @lock_value - - arel - end - - private - def collapse_wheres(arel, wheres) equalities = wheres.grep(Arel::Nodes::Equality) @@ -280,10 +257,9 @@ module ActiveRecord stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) non_association_joins = (joins - association_joins - stashed_association_joins) - custom_joins = custom_join_sql(non_association_joins) - ast = custom_join_ast(relation, non_association_joins) + join_ast = custom_join_ast(relation, non_association_joins) - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, ast) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_ast) join_dependency.graft(*stashed_association_joins) @@ -294,15 +270,15 @@ module ActiveRecord end if Arel::Table === relation - relation.from(ast || relation) + relation.from(join_ast || relation) else - if relation.froms.length > 0 && ast - ast.left = relation.froms.first - relation.from ast - elsif relation.froms.length == 0 && ast - relation.from(ast) + if relation.froms.length > 0 && join_ast + join_ast.left = relation.froms.first + relation.from join_ast + elsif relation.froms.length == 0 && join_ast + relation.from(join_ast) else - relation.join(custom_joins) + relation end end end -- cgit v1.2.3 From 17d72dd19fc7f979ae17097c3ee1e0b5c1d75fb7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 11:02:15 -0800 Subject: adding a fixme comment --- activerecord/lib/active_record/relation/query_methods.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index f9e5d7d925..6886c7538b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -265,6 +265,7 @@ module ActiveRecord @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty? + # FIXME: refactor this to build an AST join_dependency.join_associations.each do |association| relation = association.join_to(relation) end -- cgit v1.2.3 From f4223cc7abeeae5f4849eab27951faa55f3c6996 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 11:18:26 -0800 Subject: arel ignores the columns parameter --- .../associations/class_methods/join_dependency/join_association.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 4dd11a7366..2c20e304d0 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -67,8 +67,7 @@ module ActiveRecord def table @table ||= Arel::Table.new( - table_name, :as => aliased_table_name, - :engine => arel_engine, :columns => active_record.columns + table_name, :as => aliased_table_name, :engine => arel_engine ) end -- cgit v1.2.3 From b8d57a0400f95abe1b87634e9cada8868ff0a22e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 11:50:16 -0800 Subject: have table_aliases call count_aliases to set the default value --- .../lib/active_record/associations/class_methods/join_dependency.rb | 4 +++- .../associations/class_methods/join_dependency/join_association.rb | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index d508c3b7aa..13576e1aec 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -14,7 +14,9 @@ module ActiveRecord @join_parts = [JoinBase.new(base)] @associations = {} @reflections = [] - @table_aliases = Hash.new(0) + @table_aliases = Hash.new do |h,name| + h[name] = count_aliases_from_table_joins(name) + end @table_aliases[base.table_name] = 1 build(associations) end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 2c20e304d0..15c1e7de55 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -77,10 +77,6 @@ module ActiveRecord protected def aliased_table_name_for(name, suffix = nil) - if @join_dependency.table_aliases[name].zero? - @join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name) - end - if !@join_dependency.table_aliases[name].zero? # We need an alias name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" @join_dependency.table_aliases[name] += 1 -- cgit v1.2.3 From de4bd472c3298f0455de8e3862c31900184f3c31 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 11:54:45 -0800 Subject: remove code that could never be executed --- .../associations/class_methods/join_dependency/join_association.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 15c1e7de55..9c43e9edd7 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -80,10 +80,6 @@ module ActiveRecord if !@join_dependency.table_aliases[name].zero? # We need an alias name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" @join_dependency.table_aliases[name] += 1 - if @join_dependency.table_aliases[name] == 1 # First time we've seen this name - # Also need to count the aliases from the table_aliases to avoid incorrect count - @join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name) - end table_index = @join_dependency.table_aliases[name] name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 else -- cgit v1.2.3 From dcc0c9a8bb49d956476a7165f3eef6699ec4628b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 12:05:02 -0800 Subject: dry up calls to table_aliases --- .../class_methods/join_dependency/join_association.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 9c43e9edd7..bf0190e40a 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -77,13 +77,15 @@ module ActiveRecord protected def aliased_table_name_for(name, suffix = nil) - if !@join_dependency.table_aliases[name].zero? # We need an alias + aliases = @join_dependency.table_aliases + + if !aliases[name].zero? # We need an alias name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" - @join_dependency.table_aliases[name] += 1 - table_index = @join_dependency.table_aliases[name] + aliases[name] += 1 + table_index = aliases[name] name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 else - @join_dependency.table_aliases[name] += 1 + aliases[name] += 1 end name -- cgit v1.2.3 From c69bd59a9cc7c0de3d6db7f8c90e73778516debf Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 12:09:00 -0800 Subject: only call active_record once --- .../associations/class_methods/join_dependency/join_association.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index bf0190e40a..09094f3ed5 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -80,10 +80,12 @@ module ActiveRecord aliases = @join_dependency.table_aliases if !aliases[name].zero? # We need an alias - name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" + connection = active_record.connection + + name = connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" aliases[name] += 1 table_index = aliases[name] - name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 + name = name[0, connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 else aliases[name] += 1 end -- cgit v1.2.3 From 45ea60eee7e6284768b78d51dbed4a2913aa18dd Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 12:11:43 -0800 Subject: fewer method calls, fewer code branches --- .../class_methods/join_dependency/join_association.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 09094f3ed5..25511a092f 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -79,17 +79,16 @@ module ActiveRecord def aliased_table_name_for(name, suffix = nil) aliases = @join_dependency.table_aliases - if !aliases[name].zero? # We need an alias + if aliases[name] != 0 # We need an alias connection = active_record.connection name = connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}" - aliases[name] += 1 - table_index = aliases[name] + table_index = aliases[name] + 1 name = name[0, connection.table_alias_length-3] + "_#{table_index}" if table_index > 1 - else - aliases[name] += 1 end + aliases[name] += 1 + name end -- cgit v1.2.3 From 0aa78826e4857dbaaaf8a0779848d11ab6e0ad5d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 8 Dec 2010 21:20:53 +0100 Subject: makes a pass to the action caching rdoc --- .../lib/action_controller/caching/actions.rb | 73 ++++++++++++---------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index cb79e51dbf..a4bac3caed 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -4,25 +4,26 @@ module ActionController #:nodoc: module Caching # Action caching is similar to page caching by the fact that the entire # output of the response is cached, but unlike page caching, every - # request still goes through the Action Pack. The key benefit - # of this is that filters are run before the cache is served, which - # allows for authentication and other restrictions on whether someone - # is allowed to see the cache. Example: + # request still goes through Action Pack. The key benefit of this is + # that filters run before the cache is served, which allows for + # authentication and other restrictions on whether someone is allowed + # to execute such action. Example: # # class ListsController < ApplicationController # before_filter :authenticate, :except => :public + # # caches_page :public - # caches_action :index, :show, :feed + # caches_action :index, :show # end # - # In this example, the public action doesn't require authentication, - # so it's possible to use the faster page caching method. But both - # the show and feed action are to be shielded behind the authenticate - # filter, so we need to implement those as action caches. + # In this example, the +public+ action doesn't require authentication + # so it's possible to use the faster page caching. On the other hand + # +index+ and +show+ require authentication. They can still be cached, + # but we need action caching for them. # - # Action caching internally uses the fragment caching and an around - # filter to do the job. The fragment cache is named according to both - # the current host and the path. So a page that is accessed at + # Action caching uses fragment caching internally and an around + # filter to do the job. The fragment cache is named according to + # the host and path of the request. A page that is accessed at # http://david.example.com/lists/show/1 will result in a fragment named # david.example.com/lists/show/1. This allows the cacher to # differentiate between david.example.com/lists/ and @@ -38,19 +39,23 @@ module ActionController #:nodoc: # :action => 'list', :format => :xml. # # You can set modify the default action cache path by passing a - # :cache_path option. This will be passed directly to - # ActionCachePath.path_for. This is handy for actions with multiple - # possible routes that should be cached differently. If a block is - # given, it is called with the current controller instance. + # :cache_path option. This will be passed directly to + # ActionCachePath.path_for. This is handy for actions with + # multiple possible routes that should be cached differently. If a + # block is given, it is called with the current controller instance. + # + # And you can also use :if (or :unless) to pass a + # proc that specifies when the action should be cached. # - # And you can also use :if (or :unless) to pass a Proc that - # specifies when the action should be cached. + # Finally, if you are using memcached, you can also pass :expires_in. # - # Finally, if you are using memcached, you can also pass :expires_in. + # The following example depicts some of the points made above: # # class ListsController < ApplicationController # before_filter :authenticate, :except => :public - # caches_page :public + # + # caches_page :public + # # caches_action :index, :if => proc do |c| # !c.request.format.json? # cache if is not a JSON request # end @@ -58,24 +63,28 @@ module ActionController #:nodoc: # caches_action :show, :cache_path => { :project => 1 }, # :expires_in => 1.hour # - # caches_action :feed, :cache_path => proc do |controller| - # if controller.params[:user_id] - # controller.send(:user_list_url, - # controller.params[:user_id], controller.params[:id]) + # caches_action :feed, :cache_path => proc do |c| + # if c.params[:user_id] + # c.send(:user_list_url, + # c.params[:user_id], c.params[:id]) # else - # controller.send(:list_url, controller.params[:id]) + # c.send(:list_url, c.params[:id]) # end # end # end # - # If you pass :layout => false, it will only cache your action - # content. It is useful when your layout has dynamic information. + # If you pass :layout => false, it will only cache your action + # content. That's useful when your layout has dynamic information. + # + # Warning: If the format of the request is determined by the Accept HTTP + # header the Content-Type of the cached response could be wrong because + # no information about the MIME type is stored in the cache key. So, if + # you first ask for MIME type M in the Accept header, a cache entry is + # created, and then perform a second resquest to the same resource asking + # for a different MIME type, you'd get the content cached for M. # - # Note: If action caching is being performed for different MIME types - # and those MIME types are being determined by HTTP_ACCEPT header atttribute - # and noth using params[:format] then both the cached data and the content-type - # of the response could be wrong. The safest way to use action caching is to - # pass non-html attribute as params[:format] . + # The :format parameter is taken into account though. The safest + # way to cache by MIME type is to pass the format in the route. module Actions extend ActiveSupport::Concern -- cgit v1.2.3 From 53bbbcc6638a9df86e74e4e420840f076e7c2994 Mon Sep 17 00:00:00 2001 From: Ivan Evtukhovich Date: Thu, 9 Dec 2010 00:41:47 +0300 Subject: Fix doc about nested transaction rollback Because AR::Rollback do not reraise and inner transaction is not "real" nothing rollback at all --- activerecord/lib/active_record/transactions.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 654c475aed..181280baaa 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -141,7 +141,8 @@ module ActiveRecord # end # end # - # User.find(:all) # => empty + # User.find(:all) # => Return both Kotori and Nemu, because inner transaction do not rollback + # # without :requiers_new => true option, and Rollback exception do not reraise # # It is also possible to requires a sub-transaction by passing # :requires_new => true. If anything goes wrong, the -- cgit v1.2.3 From e924814b28d655242a49f543cf453d55712071b1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 13:40:10 -0800 Subject: just allocate the table in initialize --- .../class_methods/join_dependency/join_association.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 25511a092f..b063d927b9 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -42,6 +42,9 @@ module ActiveRecord # depends on the state of the join dependency, but we want it to work the same way # every time. allocate_aliases + @table = Arel::Table.new( + table_name, :as => aliased_table_name, :engine => arel_engine + ) end def ==(other) @@ -65,12 +68,7 @@ module ActiveRecord joining_relation.joins(self) end - def table - @table ||= Arel::Table.new( - table_name, :as => aliased_table_name, :engine => arel_engine - ) - end - + attr_reader :table # More semantic name given we are talking about associations alias_method :target_table, :table -- cgit v1.2.3 From d0467e08e54a84fc4672c508716615aa0177994a Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Fri, 3 Dec 2010 13:12:59 +0100 Subject: Allow to run migrations from more than one directory --- .../abstract/schema_statements.rb | 6 ++- activerecord/lib/active_record/migration.rb | 57 +++++++++++++--------- activerecord/lib/active_record/schema.rb | 6 +-- activerecord/test/cases/migration_test.rb | 12 +++++ 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 4e770c37da..3eb81b0dfa 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -442,12 +442,14 @@ module ActiveRecord end end - def assume_migrated_upto_version(version, migrations_path = ActiveRecord::Migrator.migrations_path) + def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths) + migrations_paths = [migrations_paths] unless migrations_paths.kind_of?(Array) version = version.to_i sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name) migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i } - versions = Dir["#{migrations_path}/[0-9]*_*.rb"].map do |filename| + paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" } + versions = Dir[*paths].map do |filename| filename.split('/').last.split('_').first.to_i end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 4dc67a0905..130b43cc15 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -511,39 +511,40 @@ module ActiveRecord class Migrator#:nodoc: class << self - attr_writer :migrations_path + attr_writer :migrations_paths + alias :migrations_path= :migrations_paths= - def migrate(migrations_path, target_version = nil) + def migrate(migrations_paths, target_version = nil) case when target_version.nil? - up(migrations_path, target_version) + up(migrations_paths, target_version) when current_version == 0 && target_version == 0 [] when current_version > target_version - down(migrations_path, target_version) + down(migrations_paths, target_version) else - up(migrations_path, target_version) + up(migrations_paths, target_version) end end - def rollback(migrations_path, steps=1) - move(:down, migrations_path, steps) + def rollback(migrations_paths, steps=1) + move(:down, migrations_paths, steps) end - def forward(migrations_path, steps=1) - move(:up, migrations_path, steps) + def forward(migrations_paths, steps=1) + move(:up, migrations_paths, steps) end - def up(migrations_path, target_version = nil) - self.new(:up, migrations_path, target_version).migrate + def up(migrations_paths, target_version = nil) + self.new(:up, migrations_paths, target_version).migrate end - def down(migrations_path, target_version = nil) - self.new(:down, migrations_path, target_version).migrate + def down(migrations_paths, target_version = nil) + self.new(:down, migrations_paths, target_version).migrate end - def run(direction, migrations_path, target_version) - self.new(direction, migrations_path, target_version).run + def run(direction, migrations_paths, target_version) + self.new(direction, migrations_paths, target_version).run end def schema_migrations_table_name @@ -569,12 +570,20 @@ module ActiveRecord name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}" end + def migrations_paths + @migrations_paths ||= ['db/migrate'] + # just to not break things if someone uses: migration_path = some_string + @migrations_paths.kind_of?(Array) ? @migrations_paths : [@migrations_paths] + end + def migrations_path - @migrations_path ||= 'db/migrate' + migrations_paths.first end - def migrations(path) - files = Dir["#{path}/[0-9]*_*.rb"] + def migrations(paths) + paths = [paths] unless paths.kind_of?(Array) + + files = Dir[*paths.map { |p| "#{p}/[0-9]*_*.rb" }] seen = Hash.new false @@ -598,22 +607,22 @@ module ActiveRecord private - def move(direction, migrations_path, steps) - migrator = self.new(direction, migrations_path) + def move(direction, migrations_paths, steps) + migrator = self.new(direction, migrations_paths) start_index = migrator.migrations.index(migrator.current_migration) if start_index finish = migrator.migrations[start_index + steps] version = finish ? finish.version : 0 - send(direction, migrations_path, version) + send(direction, migrations_paths, version) end end end - def initialize(direction, migrations_path, target_version = nil) + def initialize(direction, migrations_paths, target_version = nil) raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations? Base.connection.initialize_schema_migrations_table - @direction, @migrations_path, @target_version = direction, migrations_path, target_version + @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version end def current_version @@ -679,7 +688,7 @@ module ActiveRecord def migrations @migrations ||= begin - migrations = self.class.migrations(@migrations_path) + migrations = self.class.migrations(@migrations_paths) down? ? migrations.reverse : migrations end end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index c6bb5c1961..d815ab05ac 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -30,8 +30,8 @@ module ActiveRecord # ActiveRecord::Schema is only supported by database adapters that also # support migrations, the two features being very similar. class Schema < Migration - def migrations_path - ActiveRecord::Migrator.migrations_path + def migrations_paths + ActiveRecord::Migrator.migrations_paths end # Eval the given block. All methods available to the current connection @@ -51,7 +51,7 @@ module ActiveRecord unless info[:version].blank? initialize_schema_migrations_table - assume_migrated_upto_version(info[:version], schema.migrations_path) + assume_migrated_upto_version(info[:version], schema.migrations_paths) end end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 96da3be655..95b7ce9f34 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1275,6 +1275,18 @@ if ActiveRecord::Base.connection.supports_migrations? end end + def test_finds_migrations_from_two_directories + directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps'] + migrations = ActiveRecord::Migrator.new(:up, directories).migrations + + [[20090101010101, "PeopleHaveHobbies"], [20090101010202, "PeopleHaveDescriptions"], + [20100101010101, "PeopleHaveLastNames"], [20100201010101, "WeNeedReminders"], + [20100301010101, "InnocentJointable"]].each_with_index do |pair, i| + assert_equal migrations[i].version, pair.first + assert_equal migrations[i].name, pair.last + end + end + def test_finds_pending_migrations ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1) migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations -- cgit v1.2.3 From c9d23214aefcf0bcebb56fce53eefa4598cbb64d Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Fri, 3 Dec 2010 13:41:51 +0100 Subject: Allow to run migrations from more than one directory in rake tasks --- activerecord/lib/active_record/railties/databases.rake | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 1fbc8a1d32..316e0d9a3b 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -2,7 +2,7 @@ namespace :db do task :load_config => :rails_env do require 'active_record' ActiveRecord::Base.configurations = Rails.application.config.database_configuration - ActiveRecord::Migrator.migrations_path = Rails.application.paths["db/migrate"].first + ActiveRecord::Migrator.migrations_paths = Rails.application.paths["db/migrate"].to_a end namespace :create do @@ -140,7 +140,7 @@ namespace :db do desc "Migrate the database (options: VERSION=x, VERBOSE=false)." task :migrate => :environment do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true - ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) + ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end @@ -163,7 +163,7 @@ namespace :db do task :up => :environment do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version - ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_path, version) + ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end @@ -171,7 +171,7 @@ namespace :db do task :down => :environment do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version - ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_path, version) + ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end @@ -209,14 +209,14 @@ namespace :db do desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).' task :rollback => :environment do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 - ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_path, step) + ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' task :forward => :environment do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 - ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_path, step) + ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end @@ -261,7 +261,7 @@ namespace :db do # desc "Raises an error if there are pending migrations" task :abort_if_pending_migrations => :environment do if defined? ActiveRecord - pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_path).pending_migrations + pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations if pending_migrations.any? puts "You have #{pending_migrations.size} pending migrations:" @@ -501,7 +501,7 @@ namespace :railties do puts "Copied migration #{migration.basename} from #{name}" end - ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_path, railties, + ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_paths.first, railties, :on_skip => on_skip, :on_copy => on_copy) end end -- cgit v1.2.3 From 23ba7e456dd0302aeaddb50d14e1f595c82ca6c8 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Sat, 4 Dec 2010 08:33:06 +0100 Subject: properly load database config in database rake tasks, to properly set migrations_paths --- activerecord/lib/active_record/railties/databases.rake | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 316e0d9a3b..f1e21bea3f 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -138,7 +138,7 @@ namespace :db do desc "Migrate the database (options: VERSION=x, VERBOSE=false)." - task :migrate => :environment do + task :migrate => [:environment, :load_config] do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby @@ -146,7 +146,7 @@ namespace :db do namespace :migrate do # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).' - task :redo => :environment do + task :redo => [:environment, :load_config] do if ENV["VERSION"] Rake::Task["db:migrate:down"].invoke Rake::Task["db:migrate:up"].invoke @@ -160,7 +160,7 @@ namespace :db do task :reset => ["db:drop", "db:create", "db:migrate"] # desc 'Runs the "up" for a given migration VERSION.' - task :up => :environment do + task :up => [:environment, :load_config] do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version) @@ -168,7 +168,7 @@ namespace :db do end # desc 'Runs the "down" for a given migration VERSION.' - task :down => :environment do + task :down => [:environment, :load_config] do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version) @@ -176,7 +176,7 @@ namespace :db do end desc "Display status of migrations" - task :status => :environment do + task :status => [:environment, :load_config] do config = ActiveRecord::Base.configurations[Rails.env || 'development'] ActiveRecord::Base.establish_connection(config) unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) @@ -207,14 +207,14 @@ namespace :db do end desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).' - task :rollback => :environment do + task :rollback => [:environment, :load_config] do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' - task :forward => :environment do + task :forward => [:environment, :load_config] do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby -- cgit v1.2.3 From 7ecee054a322e214e4f285b1a8327751bd79a418 Mon Sep 17 00:00:00 2001 From: Jeff Dean Date: Fri, 3 Dec 2010 23:16:45 -0700 Subject: Setting the id of a belongs_to object updates all referenced objects [#2989 state:resolved] --- activerecord/lib/active_record/associations.rb | 37 ++++++++++++++++++++++ .../associations/belongs_to_associations_test.rb | 37 ++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 0d9171d876..4711522b00 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1223,12 +1223,14 @@ module ActiveRecord if reflection.options[:polymorphic] association_accessor_methods(reflection, BelongsToPolymorphicAssociation) + association_foreign_type_setter_method(reflection) else association_accessor_methods(reflection, BelongsToAssociation) association_constructor_method(:build, reflection, BelongsToAssociation) association_constructor_method(:create, reflection, BelongsToAssociation) end + association_foreign_key_setter_method(reflection) add_counter_cache_callbacks(reflection) if options[:counter_cache] add_touch_callbacks(reflection, options[:touch]) if options[:touch] @@ -1555,6 +1557,41 @@ module ActiveRecord end end + def association_foreign_key_setter_method(reflection) + setters = reflect_on_all_associations(:belongs_to).map do |belongs_to_reflection| + if belongs_to_reflection.primary_key_name == reflection.primary_key_name + "association_instance_set(:#{belongs_to_reflection.name}, nil);" + end + end.compact.join + + class_eval <<-FILE, __FILE__, __LINE__ + 1 + def #{reflection.primary_key_name}=(new_id) + write_attribute :#{reflection.primary_key_name}, new_id + if #{reflection.primary_key_name}_changed? + #{ setters } + end + end + FILE + end + + def association_foreign_type_setter_method(reflection) + setters = reflect_on_all_associations(:belongs_to).map do |belongs_to_reflection| + if belongs_to_reflection.options[:foreign_type] == reflection.options[:foreign_type] + "association_instance_set(:#{belongs_to_reflection.name}, nil);" + end + end.compact.join + + field = reflection.options[:foreign_type] + class_eval <<-FILE, __FILE__, __LINE__ + 1 + def #{field}=(new_id) + write_attribute :#{field}, new_id + if #{field}_changed? + #{ setters } + end + end + FILE + end + def add_counter_cache_callbacks(reflection) cache_column = reflection.counter_cache_column diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 1b0c00bd5a..1820f95261 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -486,4 +486,41 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase new_firm = accounts(:signals37).build_firm(:name => 'Apple') assert_equal new_firm.name, "Apple" end + + def test_reassigning_the_parent_id_updates_the_object + original_parent = Firm.create! :name => "original" + updated_parent = Firm.create! :name => "updated" + + client = Client.new("client_of" => original_parent.id) + assert_equal original_parent, client.firm + assert_equal original_parent, client.firm_with_condition + assert_equal original_parent, client.firm_with_other_name + + client.client_of = updated_parent.id + assert_equal updated_parent, client.firm + assert_equal updated_parent, client.firm_with_condition + assert_equal updated_parent, client.firm_with_other_name + end + + def test_polymorphic_reassignment_of_associated_id_updates_the_object + member1 = Member.create! + member2 = Member.create! + + sponsor = Sponsor.new("sponsorable_type" => "Member", "sponsorable_id" => member1.id) + assert_equal member1, sponsor.sponsorable + + sponsor.sponsorable_id = member2.id + assert_equal member2, sponsor.sponsorable + end + + def test_polymorphic_reassignment_of_associated_type_updates_the_object + member1 = Member.create! + + sponsor = Sponsor.new("sponsorable_type" => "Member", "sponsorable_id" => member1.id) + assert_equal member1, sponsor.sponsorable + + sponsor.sponsorable_type = "Firm" + assert_not_equal member1, sponsor.sponsorable + end + end -- cgit v1.2.3 From 6c5b734e6a529284bab4c7bdca770d390b8b9084 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 15:37:13 -0800 Subject: removing method to prevent warnings --- activerecord/lib/active_record/associations.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 4711522b00..cdc8f25119 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1564,6 +1564,10 @@ module ActiveRecord end end.compact.join + if method_defined?(:"#{reflection.primary_key_name}=") + undef_method :"#{reflection.primary_key_name}=" + end + class_eval <<-FILE, __FILE__, __LINE__ + 1 def #{reflection.primary_key_name}=(new_id) write_attribute :#{reflection.primary_key_name}, new_id -- cgit v1.2.3 From 285f456391014c6e5ce1a694d43088012fbc7772 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 15:38:39 -0800 Subject: updating CHANGELOG --- activerecord/CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index f46db909ba..9c2e311c75 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,8 @@ *Rails 3.1.0 (unreleased)* +* Setting the id of a belongs_to object will update the reference to the +object. [#2989 state:resolved] + * ActiveRecord::Base#dup and ActiveRecord::Base#clone semantics have changed to closer match normal Ruby dup and clone semantics. -- cgit v1.2.3 From 7a237d56aa38c51987af0fd52d001989c4f3da07 Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Mon, 29 Nov 2010 18:13:17 +0530 Subject: Ensure that boolean false is properly serialized [#6079 state:resolved] --- activerecord/lib/active_record/attribute_methods/read.rb | 3 ++- activerecord/lib/active_record/base.rb | 2 +- activerecord/test/cases/base_test.rb | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index ad5a3e7562..506f6e878f 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -85,7 +85,8 @@ module ActiveRecord def _read_attribute(attr_name) attr_name = attr_name.to_s attr_name = self.class.primary_key if attr_name == 'id' - if value = @attributes[attr_name] + value = @attributes[attr_name] + unless value.nil? if column = column_for_attribute(attr_name) if unserializable_attribute?(attr_name, column) unserialize_attribute(attr_name) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b55aaddbd7..7b9ce21ceb 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1680,7 +1680,7 @@ MSG if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name)) value = read_attribute(name) - if value && self.class.serialized_attributes.key?(name) + if !value.nil? && self.class.serialized_attributes.key?(name) value = YAML.dump value end attrs[self.class.arel_table[name]] = value diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index c3ba1f0c35..86d4a90fc4 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -997,6 +997,22 @@ class BasicsTest < ActiveRecord::TestCase Topic.serialize(:content) end + def test_serialized_boolean_value_true + Topic.serialize(:content) + topic = Topic.new(:content => true) + assert topic.save + topic = topic.reload + assert_equal topic.content, true + end + + def test_serialized_boolean_value_false + Topic.serialize(:content) + topic = Topic.new(:content => false) + assert topic.save + topic = topic.reload + assert_equal topic.content, false + end + def test_quote author_name = "\\ \001 ' \n \\n \"" topic = Topic.create('author_name' => author_name) -- cgit v1.2.3 From 2dc3342f75577fa521d9d0c54260da75450126a2 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 9 Dec 2010 01:58:58 +0100 Subject: Do not require fixtures in example integration test in engine if active record is skipeed --- .../rails/plugin_new/templates/test/integration/navigation_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb b/railties/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb index d06fe7cbd0..dd4d2da4eb 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb +++ b/railties/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb @@ -1,7 +1,9 @@ require 'test_helper' class NavigationTest < ActionDispatch::IntegrationTest +<% unless options[:skip_active_record] -%> fixtures :all +<% end -%> # Replace this with your real tests. test "the truth" do -- cgit v1.2.3 From 5df72a238e9fcb18daf6ab6e6dc9051c9106d7bb Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Sat, 4 Dec 2010 17:12:57 +0100 Subject: Rake tasks for managing database while development and testing of engines Some of the engines needs database. As engine needs to be run inside Rails application, migrations and other database operations are run from dummy application. To make that process simple I changed db tasks to look for migrations in both engine's and application's db/migrate directory. You can run all of the database tasks from test/dummy or directly from engine with prefix app, like: rake app:db:migrate rake app:db:migrate:redo --- .../lib/active_record/railties/databases.rake | 28 +++++++++++----------- .../rails/plugin_new/plugin_new_generator.rb | 10 ++++++++ .../generators/rails/plugin_new/templates/Rakefile | 12 ++++++++++ .../rails/plugin_new/templates/test/test_helper.rb | 5 ---- .../test/generators/plugin_new_generator_test.rb | 2 +- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index f1e21bea3f..2e73cd4f1d 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -1,4 +1,4 @@ -namespace :db do +db_namespace = namespace :db do task :load_config => :rails_env do require 'active_record' ActiveRecord::Base.configurations = Rails.application.config.database_configuration @@ -141,18 +141,18 @@ namespace :db do task :migrate => [:environment, :load_config] do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) - Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end namespace :migrate do # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).' task :redo => [:environment, :load_config] do if ENV["VERSION"] - Rake::Task["db:migrate:down"].invoke - Rake::Task["db:migrate:up"].invoke + db_namespace["migrate:down"].invoke + db_namespace["migrate:up"].invoke else - Rake::Task["db:rollback"].invoke - Rake::Task["db:migrate"].invoke + db_namespace["rollback"].invoke + db_namespace["migrate"].invoke end end @@ -164,7 +164,7 @@ namespace :db do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version) - Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end # desc 'Runs the "down" for a given migration VERSION.' @@ -172,7 +172,7 @@ namespace :db do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil raise "VERSION is required" unless version ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version) - Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end desc "Display status of migrations" @@ -210,14 +210,14 @@ namespace :db do task :rollback => [:environment, :load_config] do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) - Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' task :forward => [:environment, :load_config] do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step) - Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' @@ -321,12 +321,12 @@ namespace :db do namespace :schema do desc "Create a db/schema.rb file that can be portably used against any DB supported by AR" - task :dump => :environment do + task :dump => :load_config do require 'active_record/schema_dumper' File.open(ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb", "w") do |file| ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) end - Rake::Task["db:schema:dump"].reenable + db_namespace["schema:dump"].reenable end desc "Load a schema.rb file into the database" @@ -383,7 +383,7 @@ namespace :db do task :load => 'db:test:purge' do ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) ActiveRecord::Schema.verbose = false - Rake::Task["db:schema:load"].invoke + db_namespace["schema:load"].invoke end # desc "Recreate the test database from the current environment's database schema" @@ -457,7 +457,7 @@ namespace :db do # desc 'Check for pending migrations and load the test schema' task :prepare => 'db:abort_if_pending_migrations' do if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank? - Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].invoke + db_namespace[{ :sql => "test:clone_structure", :ruby => "test:load" }[ActiveRecord::Base.schema_format]].invoke end end end diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 9c54b98238..77497f9e44 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -70,6 +70,16 @@ task :default => :test if mountable? template "rails/routes.rb", "#{dummy_path}/config/routes.rb", :force => true end + + if full? && !options[:skip_active_record] + append_file "#{dummy_path}/Rakefile", <<-EOF + +task :"db:load_config" do + ActiveRecord::Migrator.migrations_paths = Rails.application.config.paths["db/migrate"].to_a + + <%= camelized %>::Engine.config.paths["db/migrate"].to_a +end + EOF + end end def test_dummy_clean diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile index 12350309bf..b7cab3859a 100755 --- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile @@ -14,3 +14,15 @@ Rake::RDocTask.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('README.rdoc') rdoc.rdoc_files.include('lib/**/*.rb') end + +<% if full? && !options[:skip_active_record] -%> +namespace :app do + load File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__) + + task :"db:load_config" do + ActiveRecord::Migrator.migrations_paths = Rails.application.config.paths["db/migrate"].to_a + + <%= camelized %>::Engine.config.paths["db/migrate"].to_a + end +end +<% end -%> + diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb index 7b61047e77..791b901593 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb +++ b/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb @@ -6,10 +6,5 @@ require "rails/test_help" Rails.backtrace_cleaner.remove_silencers! -<% if full? && !options[:skip_active_record] -%> -# Run any available migration from application -ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__) -<% end -%> - # Load support files Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index 2105585272..0d24821ff6 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -114,7 +114,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase end def test_ensure_that_tests_works_in_full_mode - run_generator [destination_root, "--full"] + run_generator [destination_root, "--full", "--skip_active_record"] FileUtils.cd destination_root `bundle install` assert_match /2 tests, 2 assertions, 0 failures, 0 errors/, `bundle exec rake test` -- cgit v1.2.3 From 051127d4d38329238bb0b3587ef128f131174786 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 8 Dec 2010 23:15:13 +0100 Subject: Rails::Engine.find(path) - method to find engine by path --- railties/lib/rails/application/railties.rb | 8 -------- railties/lib/rails/engine.rb | 5 +++++ railties/lib/rails/engine/railties.rb | 10 ++++++++++ railties/test/railties/engine_test.rb | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/railties/lib/rails/application/railties.rb b/railties/lib/rails/application/railties.rb index c1d2de571f..4fc5e92837 100644 --- a/railties/lib/rails/application/railties.rb +++ b/railties/lib/rails/application/railties.rb @@ -8,14 +8,6 @@ module Rails @all.each(&block) if block @all end - - def railties - @railties ||= ::Rails::Railtie.subclasses.map(&:instance) - end - - def engines - @engines ||= ::Rails::Engine.subclasses.map(&:instance) - end end end end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 85fa4424c4..b188fdfca1 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -371,6 +371,11 @@ module Rails end end end + + # Finds engine with given path + def find(path) + Rails::Engine::Railties.engines.find { |r| File.expand_path(r.root.to_s) == File.expand_path(path.to_s) } + end end delegate :middleware, :root, :paths, :to => :config diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb index e91bdbf1e5..d5ecd2e48d 100644 --- a/railties/lib/rails/engine/railties.rb +++ b/railties/lib/rails/engine/railties.rb @@ -18,6 +18,16 @@ module Rails Plugin.all(plugin_names, @config.paths["vendor/plugins"].existent) end end + + def self.railties + @railties ||= ::Rails::Railtie.subclasses.map(&:instance) + end + + def self.engines + @engines ||= ::Rails::Engine.subclasses.map(&:instance) + end + + delegate :railties, :engines, :to => "self.class" end end end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 7548c6318e..05bd0c36cd 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -702,5 +702,24 @@ module RailtiesTest assert_equal "foo", Bukkits.table_name_prefix end + + test "fetching engine by path" do + @plugin.write "lib/bukkits.rb", <<-RUBY + module Bukkits + class Engine < ::Rails::Engine + end + end + RUBY + + boot_rails + require "#{rails_root}/config/environment" + + assert_equal Bukkits::Engine.instance, Rails::Engine.find(@plugin.path) + + # check expanding paths + engine_dir = @plugin.path.chomp("/").split("/").last + engine_path = File.join(@plugin.path, '..', engine_dir) + assert_equal Bukkits::Engine.instance, Rails::Engine.find(engine_path) + end end end -- cgit v1.2.3 From 843130dbe7fb0545db13bdfc163b919cfd0e128f Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 8 Dec 2010 23:25:39 +0100 Subject: Use Rails::Engine.find in commands.rb --- railties/lib/rails/commands.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb index 338565247f..46363d7921 100644 --- a/railties/lib/rails/commands.rb +++ b/railties/lib/rails/commands.rb @@ -18,8 +18,7 @@ when 'generate', 'destroy', 'plugin' require APP_PATH Rails.application.require_environment! - if defined?(ENGINE_PATH) - engine = Rails.application.railties.engines.find { |r| r.root.to_s == ENGINE_PATH } + if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH) Rails.application = engine end -- cgit v1.2.3 From 07b0e59988beb1b29bdfadd02f0d015eae9191cf Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 8 Dec 2010 17:31:20 -0800 Subject: use sort_by --- .../lib/active_record/associations/class_methods/join_dependency.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 13576e1aec..1856395f2d 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -151,7 +151,7 @@ module ActiveRecord build(association, parent, join_type) end when Hash - associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name| + associations.keys.sort_by { |a| a.to_s }.each do |name| join_association = build(name, parent, join_type) build(associations[name], join_association, join_type) end -- cgit v1.2.3 From b6eec1decf0a7ba66827fd897f8f01973773092d Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sat, 4 Dec 2010 15:52:22 +1100 Subject: Config guide: eager_load_paths by default contains every directory in the app directory --- railties/guides/source/configuring.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index afafabd1e9..691418860d 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -73,7 +73,7 @@ h4. Rails General Configuration * +config.dependency_loading+ enables or disables dependency loading during the request cycle. Setting dependency_loading to _true_ will allow new classes to be loaded during a request and setting it to _false_ will disable this behavior. Can also be enabled with +threadsafe!+. -* +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. All elements of this array must also be in +load_paths+. +* +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. Defaults to every folder in the +app+ directory of the application. All elements of this array must also be in +load_paths+. * +config.encoding+ sets up the application-wide encoding. Defaults to UTF-8. -- cgit v1.2.3 From 4902fc73b397ee707771e5320e9f67d23bca31e5 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sun, 5 Dec 2010 20:10:17 +1100 Subject: Config guide: add session store config option --- railties/guides/source/configuring.textile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 691418860d..9e5dca9945 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -101,6 +101,14 @@ h4. Rails General Configuration * +config.serve_static_assets+ configures Rails to serve static assets. Defaults to _true_, but in the production environment is turned off. The server software used to run the application should be used to serve the assets instead. +* +config.session_store+ is usually set up in +config/initializers/session_store.rb+ and specifies what class to use to store the session. Custom session stores can be specified like so: + + + config.session_store = :my_custom_store + + +This custom store must be defined as +ActionDispatch::Session::MyCustomStore+. + * +config.threadsafe!+ enables +allow_concurrency+, +cache_classes+, +dependency_loading+ and +preload_frameworks+ to make the application threadsafe. WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. -- cgit v1.2.3 From ceb164a420b3e91b2ff8e80ede1a818a016e5d9b Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sun, 5 Dec 2010 20:10:31 +1100 Subject: Config guide: add further initializers --- railties/guides/source/configuring.textile | 51 ++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 9e5dca9945..bd8d4247fd 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -573,16 +573,63 @@ Adds the directory +app/views+ from the application, railties and engines to the h4. +load_environment_config+ +Loads the +config/environments+ file for the current environment. +h4. +append_asset_paths+ -h4. Initializer files +Finds asset paths for the application and all attached railties and keeps a track of the available directories in +config.static_asset_paths+. -After loading the framework and any gems and plugins in your application, Rails turns to loading initialization code from +config/initializers+. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks and plugins are loaded. +h4. +prepend_helpers_path+ + +Adds the directory +app/helpers+ from the application, railties and engines to the lookup path for helpers for the application. + +h4. +load_config_initializers+ + +Loads all files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks and plugins are loaded. NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the +initializers+ folder on down. TIP: If you have any ordering dependency in your initializers, you can control the load order by naming. For example, +01_critical.rb+ will be loaded before +02_normal.rb+. +h4. +engines_blank_point+ + +Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are ran. + +h4. +add_generator_templates+ + +Finds templates for generators at +lib/templates+ for the application, railities and engines and adds these to the +config.generators.templates+ setting, which will make the templates available for all generators to reference. + +h4. +ensure_autoload_once_paths_as_subset+ + +Ensures that the +config.autoload_once_paths+ only contains paths from +config.autoload_paths+. If it contains extra paths, then an exception will be raised. + +h4. +add_to_prepare_blocks+ + +The block for every +config.to_prepare+ call in the application, a railtie or engine is added to the +to_prepare+ callbacks for Action Dispatch which will be ran per request in development, or before the first request in production. + +h4. +add_builtin_route+ + +If the application is running under the development environment then this will append the route for +rails/info/properties+ to the application routes. This route provides the detailed information such as Rails and Ruby version for +public/index.html+ in a default Rails application. + +h4. +build_middleware_stack+ + +Builds the middleware stack for the application, returning an object which has a +call+ method which takes a Rack environment object for the request. + +h4. +eager_load!+ + +If +config.cache_classes+ is +true+, runs the +config.before_eager_load+ hooks and then calls +eager_load!+ which will load all the Ruby files from +config.eager_load_paths+. + +h4. +finisher_hook+ + +Provides a hook for after the initialization of process of the application is complete, as well as running all the +config.after_initialize+ blocks for the application, railties and engines. + +h4. +set_routes_reloader+ + +Configures Action Dispatch to reload the routes file using +ActionDispatch::Callbacks.to_prepare+. + +h4. +disable_dependency_loading+ + + h3. Changelog * December 3, 2010: Added initialization events for Rails 3 ("Ryan Bigg":http://ryanbigg.com) -- cgit v1.2.3 From 9560f50920f7f425b2e74e8db10590a382679ed5 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 9 Dec 2010 10:41:08 +1000 Subject: Config guide: Space out initialization events to improve readability --- railties/guides/source/configuring.textile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index bd8d4247fd..5b344ec979 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -387,9 +387,13 @@ h3. Initialization events Rails has 5 initialization events which can be hooked into (listed in order that they are ran): * +before_configuration+: This is run as soon as the application constant inherits from +Rails::Application+. The +config+ calls are evaluated before this happens. + * +before_initialize+: This is run directly before the initialization process of the application occurs with the +:bootstrap_hook+ initializer near the beginning of the Rails initialization process. + * +to_prepare+: Run after the initializers are ran for all Railties (including the application itself), but before eager loading and the middleware stack is built. + * +before_eager_load+: This is run directly before eager loading occurs, which is the default behaviour for the _production_ environment and not for the +development+ enviroment. + * +after_initialize+: Run directly after the initialization of the application, but before the application initializers are run. -- cgit v1.2.3 From e4456b6f03318147f119271770a14e0edd58a4a8 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 9 Dec 2010 11:45:36 +1000 Subject: Add note which links to documentation regarding the types of columns available. --- railties/guides/source/command_line.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile index acd105c622..11ce3a5003 100644 --- a/railties/guides/source/command_line.textile +++ b/railties/guides/source/command_line.textile @@ -197,6 +197,8 @@ Examples: Creates a Post model with a string title, text body, and published flag. +NOTE: For a list of available field types, refer to the "API documentation":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-column for the column method for the +TableDefinition+ class. + But instead of generating a model directly (which we'll be doing later), let's set up a scaffold. A *scaffold* in Rails is a full set of model, database migration for that model, controller to manipulate it, views to view and manipulate the data, and a test suite for each of the above. We will set up a simple resource called "HighScore" that will keep track of our highest score on video games we play. -- cgit v1.2.3 From ae7825d57ca78aa5e97cd4b15119a24df5369c86 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 9 Dec 2010 17:53:34 +1000 Subject: Config guide: Use bold titles for initializers instead of headings. --- railties/guides/source/configuring.textile | 211 ++++++++--------------------- 1 file changed, 58 insertions(+), 153 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 5b344ec979..44f7a61a77 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -419,219 +419,124 @@ The block's argument of the +initialize+ is the instance of the application itse Because +Rails::Application+ inherits from +Rails::Railtie+ (indirectly), you can use the +initializer+ method in +config/application.rb+ to define initializers for the application. -Below is a comprehensive list of all the initializers found in Rails in the order that they are defined (and therefore run in, unless otherwise stated). +h4. Initializers -h4. +load_environment_hook+ +Below is a comprehensive list of all the initializers found in Rails in the order that they are defined (and therefore run in, unless otherwise stated). +*+load_environment_hook+* Serves as a placeholder so that +:load_environment_config+ can be defined to run before it. -h4. +load_active_support+ - -Requires +active_support/dependencies+ which sets up the basis for Active Support. Optionally requires +active_support/all+ if +config.active_support.bare+ is un-truthful, which is the default. - -h4. +preload_frameworks+ - -Will load all autoload dependencies of Rails automatically if +config.preload_frameworks+ is +true+ or "truthful". By default this configuration option is disabled. In Rails, when internal classes are referenced for the first time they are autoloaded. +:preload_frameworks+ loads all of this at once on initialization. - -h4. +initialize_logger+ - -Initializes the logger (an +ActiveSupport::BufferedLogger+ object) for the application and makes it accessible at +Rails.logger+, providing that there's no initializer inserted before this point that has defined +Rails.logger+. +*+load_active_support+* Requires +active_support/dependencies+ which sets up the basis for Active Support. Optionally requires +active_support/all+ if +config.active_support.bare+ is un-truthful, which is the default. -h4. +initialize_cache+ +*+preload_frameworks+* Will load all autoload dependencies of Rails automatically if +config.preload_frameworks+ is +true+ or "truthful". By default this configuration option is disabled. In Rails, when internal classes are referenced for the first time they are autoloaded. +:preload_frameworks+ loads all of this at once on initialization. -If +RAILS_CACHE+ isn't yet set, initializes the cache by referencing the value in +config.cache_store+ and stores the outcome as +RAILS_CACHE+. If this object responds to the +middleware+ method, its middleware is inserted before +Rack::Runtime+ in the middleware stack. +*+initialize_logger+* Initializes the logger (an +ActiveSupport::BufferedLogger+ object) for the application and makes it accessible at +Rails.logger+, providing that there's no initializer inserted before this point that has defined +Rails.logger+. -h4. +set_clear_dependencies_hook+ +*+initialize_cache+* If +RAILS_CACHE+ isn't yet set, initializes the cache by referencing the value in +config.cache_store+ and stores the outcome as +RAILS_CACHE+. If this object responds to the +middleware+ method, its middleware is inserted before +Rack::Runtime+ in the middleware stack. -Provides a hook for +active_record.set_dispatch_hooks+ to use, which will run before this initializer. +*+set_clear_dependencies_hook+* Provides a hook for +active_record.set_dispatch_hooks+ to use, which will run before this initializer. This initializer -- which runs only if +cache_classes+ is set to +false+ -- uses +ActionDispatch::Callbacks.after+ to remove the constants which have been referenced during the request from the object space so that they will be reloaded during the following request. -This initializer -- which runs only if +cache_classes+ is set to +false+ -- uses +ActionDispatch::Callbacks.after+ to remove the constants which have been referenced during the request from the object space so that they will be reloaded during the following request. +*+initialize_dependency_mechanism+* If +config.cache_classes+ is set to +true+, configures +ActiveSupport::Dependencies.mechanism+ to +require+ dependencies rather than +load+ them. -h4. +initialize_dependency_mechanism+ +*+bootstrap_hook+* Runs all configured +before_initialize+ blocks. -If +config.cache_classes+ is set to +true+, configures +ActiveSupport::Dependencies.mechanism+ to +require+ dependencies rather than +load+ them. +*+i18n.callbacks+* In the development environment, sets up a +to_prepare+ callback which will call +I18n.reload!+ if any of the locales have changed since the last request. In production mode this callback will only run on the first request. -h4. +bootstrap_hook+ +*+active_support.initialize_whiny_nils+* Will require +active_support/whiny_nil+ if +config.whiny_nil+ is set to +true+. This file will output errors such as: -Runs all configured +before_initialize+ blocks. - -h4. +i18n.callbacks+ - -In the development environment, sets up a +to_prepare+ callback which will call +I18n.reload!+ if any of the locales have changed since the last request. In production mode this callback will only run on the first request. - -h4. +active_support.initialize_whiny_nils+ - -Will require +active_support/whiny_nil+ if +config.whiny_nil+ is set to +true+. This file will output errors such as: - - + Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id - + And: - - You have a nil object when you didn't expect it! - You might have expected an instance of Array. - The error occurred while evaluating nil.each - - -h4. +active_support.deprecation_behavior+ - -Sets up deprecation reporting for environments, defaulting to +log+ for development, +notify+ for production and +stderr+ for test. If a value isn't set for +config.active_support.deprecation+ then this initializer will prompt the user to configure this line in the current environment's +config/environments+ file. - -h4. +active_support.initialize_time_zone+ - -Sets the default time zone for the application based off the +config.time_zone+ setting, which defaults to "UTC". - -h4. +action_dispatch.configure+ - -Configures the +ActionDispatch::Http::URL.tld_length+ to be set to the value of +config.action_dispatch.tld_length+. - -h4. +action_view.cache_asset_ids+ - -Will set +ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids+ to +false+ when Active Support loads, but only if +config.cache_classes+ is too. - -h4. +action_view.javascript_expansions+ - -Registers the expansions set up by +config.action_view.javascript_expansions+ and +config.action_view.stylesheet_expansions+ to be recognised by Action View and therefore usable in the views. - -h4. +action_view.set_configs+ - -Sets up Action View by using the settings in +config.action_view+ by +send+'ing the method names as setters to +ActionView::Base+ and passing the values through. - -h4. +action_controller.logger+ + +You have a nil object when you didn't expect it! +You might have expected an instance of Array. +The error occurred while evaluating nil.each + -Sets +ActionController::Base.logger+ -- if it's not already set -- to +Rails.logger+. +*+active_support.deprecation_behavior+* Sets up deprecation reporting for environments, defaulting to +log+ for development, +notify+ for production and +stderr+ for test. If a value isn't set for +config.active_support.deprecation+ then this initializer will prompt the user to configure this line in the current environment's +config/environments+ file. -h4. +action_controller.initialize_framework_caches+ +*+active_support.initialize_time_zone+* Sets the default time zone for the application based off the +config.time_zone+ setting, which defaults to "UTC". -Sets +ActionController::Base.cache_store+ -- if it's not already set -- to +RAILS_CACHE+. +*+action_dispatch.configure+* Configures the +ActionDispatch::Http::URL.tld_length+ to be set to the value of +config.action_dispatch.tld_length+. -h4. +action_controller.set_configs+ +*+action_view.cache_asset_ids+* Will set +ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids+ to +false+ when Active Support loads, but only if +config.cache_classes+ is too. -Sets up Action Controller by using the settings in +config.action_controller+ by +send+'ing the method names as setters to +ActionController::Base+ and passing the values through. +*+action_view.javascript_expansions+* Registers the expansions set up by +config.action_view.javascript_expansions+ and +config.action_view.stylesheet_expansions+ to be recognised by Action View and therefore usable in the views. -h4. +action_controller.compile_config_methods+ +*+action_view.set_configs+* Sets up Action View by using the settings in +config.action_view+ by +send+'ing the method names as setters to +ActionView::Base+ and passing the values through. -Initializes methods for the config settings specified so that they are quicker to access. +*+action_controller.logger+* Sets +ActionController::Base.logger+ -- if it's not already set -- to +Rails.logger+. -h4. +active_record.initialize_timezone+ +*+action_controller.initialize_framework_caches+* Sets +ActionController::Base.cache_store+ -- if it's not already set -- to +RAILS_CACHE+. -Sets +ActiveRecord::Base.time_zone_aware_attributes+ to true, as well as setting +ActiveRecord::Base.default_timezone+ to UTC. When attributes are read from the database, they will be converted into the time zone specified by +Time.zone+. +*+action_controller.set_configs+* Sets up Action Controller by using the settings in +config.action_controller+ by +send+'ing the method names as setters to +ActionController::Base+ and passing the values through. -h4. +active_record.logger+ +*+action_controller.compile_config_methods+* Initializes methods for the config settings specified so that they are quicker to access. -Sets +ActiveRecord::Base.logger+ -- if it's not already set -- to +Rails.logger+. +*+active_record.initialize_timezone+* Sets +ActiveRecord::Base.time_zone_aware_attributes+ to true, as well as setting +ActiveRecord::Base.default_timezone+ to UTC. When attributes are read from the database, they will be converted into the time zone specified by +Time.zone+. -h4. +active_record.set_configs+ +*+active_record.logger+* Sets +ActiveRecord::Base.logger+ -- if it's not already set -- to +Rails.logger+. -Sets up Active Record by using the settings in +config.active_record+ by +send+'ing the method names as setters to +ActiveRecord::Base+ and passing the values through. +*+active_record.set_configs+* Sets up Active Record by using the settings in +config.active_record+ by +send+'ing the method names as setters to +ActiveRecord::Base+ and passing the values through. -h4. +active_record.initialize_database+ +*+active_record.initialize_database+* Loads the database configuration (by default) from +config/database.yml+ and establishes a connection for the current environment. -Loads the database configuration (by default) from +config/database.yml+ and establishes a connection for the current environment. +*+active_record.log_runtime+* Includes +ActiveRecord::Railties::ControllerRuntime+ which is responsible for reporting the length of time Active Record calls took for the request back to the logger. -h4. +active_record.log_runtime+ +*+active_record.set_dispatch_hooks+* If +config.cache_classes+ is set to false, all reloadable connections to the database will be reset. -Includes +ActiveRecord::Railties::ControllerRuntime+ which is responsible for reporting the length of time Active Record calls took for the request back to the logger. +*+action_mailer.logger+* Sets +ActionMailer::Base.logger+ -- if it's not already set -- to +Rails.logger+. -h4. +active_record.set_dispatch_hooks+ +*+action_mailer.set_configs+* Sets up Action Mailer by using the settings in +config.action_mailer+ by +send+'ing the method names as setters to +ActionMailer::Base+ and passing the values through. -If +config.cache_classes+ is set to false, all reloadable connections to the database will be reset. +*+action_mailer.compile_config_methods+* Initializes methods for the config settings specified so that they are quicker to access. -h4. +action_mailer.logger+ +*+active_resource.set_configs+* Sets up Active Resource by using the settings in +config.active_resource+ by +send+'ing the method names as setters to +ActiveResource::Base+ and passing the values through. -Sets +ActionMailer::Base.logger+ -- if it's not already set -- to +Rails.logger+. +*+set_load_path+* This initializer runs before +bootstrap_hook+. Adds the +vendor+, +lib+, all directories of +app+ and any paths specified by +config.load_paths+ to +$LOAD_PATH+. -h4. +action_mailer.set_configs+ +*+set_autoload_path+* This initializer runs before +bootstrap_hook+. Adds all sub-directories of +app+ and paths specified by +config.autoload_paths+ to +ActiveSupport::Dependencies.autoload_paths+. -Sets up Action Mailer by using the settings in +config.action_mailer+ by +send+'ing the method names as setters to +ActionMailer::Base+ and passing the values through. +*+add_routing_paths+* Loads (by default) all +config/routes.rb+ files (in the application and railties, including engines) and sets up the routes for the application. -h4. +action_mailer.compile_config_methods+ +*+add_locales+* Adds the files in +config/locales+ (from the application, railties and engines) to +I18n.load_path+, making available the translations in these files. -Initializes methods for the config settings specified so that they are quicker to access. +*+add_view_paths+* Adds the directory +app/views+ from the application, railties and engines to the lookup path for view files for the application. -h4. +active_resource.set_configs+ +*+load_environment_config+* Loads the +config/environments+ file for the current environment. -Sets up Active Resource by using the settings in +config.active_resource+ by +send+'ing the method names as setters to +ActiveResource::Base+ and passing the values through. +*+append_asset_paths+* Finds asset paths for the application and all attached railties and keeps a track of the available directories in +config.static_asset_paths+. -h4. +set_load_path+ +*+prepend_helpers_path+* Adds the directory +app/helpers+ from the application, railties and engines to the lookup path for helpers for the application. -This initializer runs before +bootstrap_hook+. Adds the +vendor+, +lib+, all directories of +app+ and any paths specified by +config.load_paths+ to +$LOAD_PATH+. - -h4. +set_autoload_path+ - -This initializer runs before +bootstrap_hook+. Adds all sub-directories of +app+ and paths specified by +config.autoload_paths+ to +ActiveSupport::Dependencies.autoload_paths+. - -h4. +add_routing_paths+ - -Loads (by default) all +config/routes.rb+ files (in the application and railties, including engines) and sets up the routes for the application. - -h4. +add_locales+ - -Adds the files in +config/locales+ (from the application, railties and engines) to +I18n.load_path+, making available the translations in these files. - -h4. +add_view_paths+ - -Adds the directory +app/views+ from the application, railties and engines to the lookup path for view files for the application. - -h4. +load_environment_config+ - -Loads the +config/environments+ file for the current environment. - -h4. +append_asset_paths+ - -Finds asset paths for the application and all attached railties and keeps a track of the available directories in +config.static_asset_paths+. - -h4. +prepend_helpers_path+ - -Adds the directory +app/helpers+ from the application, railties and engines to the lookup path for helpers for the application. - -h4. +load_config_initializers+ - -Loads all files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks and plugins are loaded. +*+load_config_initializers+* Loads all Ruby files from +config/initializers+ in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks and plugins are loaded. NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the +initializers+ folder on down. TIP: If you have any ordering dependency in your initializers, you can control the load order by naming. For example, +01_critical.rb+ will be loaded before +02_normal.rb+. -h4. +engines_blank_point+ - -Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are ran. - -h4. +add_generator_templates+ - -Finds templates for generators at +lib/templates+ for the application, railities and engines and adds these to the +config.generators.templates+ setting, which will make the templates available for all generators to reference. - -h4. +ensure_autoload_once_paths_as_subset+ - -Ensures that the +config.autoload_once_paths+ only contains paths from +config.autoload_paths+. If it contains extra paths, then an exception will be raised. - -h4. +add_to_prepare_blocks+ - -The block for every +config.to_prepare+ call in the application, a railtie or engine is added to the +to_prepare+ callbacks for Action Dispatch which will be ran per request in development, or before the first request in production. - -h4. +add_builtin_route+ - -If the application is running under the development environment then this will append the route for +rails/info/properties+ to the application routes. This route provides the detailed information such as Rails and Ruby version for +public/index.html+ in a default Rails application. +*+engines_blank_point+* Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are ran. -h4. +build_middleware_stack+ +*+add_generator_templates+* Finds templates for generators at +lib/templates+ for the application, railities and engines and adds these to the +config.generators.templates+ setting, which will make the templates available for all generators to reference. -Builds the middleware stack for the application, returning an object which has a +call+ method which takes a Rack environment object for the request. +*+ensure_autoload_once_paths_as_subset+* Ensures that the +config.autoload_once_paths+ only contains paths from +config.autoload_paths+. If it contains extra paths, then an exception will be raised. -h4. +eager_load!+ +*+add_to_prepare_blocks+* The block for every +config.to_prepare+ call in the application, a railtie or engine is added to the +to_prepare+ callbacks for Action Dispatch which will be ran per request in development, or before the first request in production. -If +config.cache_classes+ is +true+, runs the +config.before_eager_load+ hooks and then calls +eager_load!+ which will load all the Ruby files from +config.eager_load_paths+. +*+add_builtin_route+* If the application is running under the development environment then this will append the route for +rails/info/properties+ to the application routes. This route provides the detailed information such as Rails and Ruby version for +public/index.html+ in a default Rails application. -h4. +finisher_hook+ +*+build_middleware_stack+* Builds the middleware stack for the application, returning an object which has a +call+ method which takes a Rack environment object for the request. -Provides a hook for after the initialization of process of the application is complete, as well as running all the +config.after_initialize+ blocks for the application, railties and engines. +*+eager_load!+* If +config.cache_classes+ is +true+, runs the +config.before_eager_load+ hooks and then calls +eager_load!+ which will load all the Ruby files from +config.eager_load_paths+. -h4. +set_routes_reloader+ +*+finisher_hook+* Provides a hook for after the initialization of process of the application is complete, as well as running all the +config.after_initialize+ blocks for the application, railties and engines. -Configures Action Dispatch to reload the routes file using +ActionDispatch::Callbacks.to_prepare+. +*+set_routes_reloader+* Configures Action Dispatch to reload the routes file using +ActionDispatch::Callbacks.to_prepare+. -h4. +disable_dependency_loading+ +*+disable_dependency_loading+* h3. Changelog -- cgit v1.2.3 From f572a02b94ce85bb8db64e838aa159cf3ef4b1fa Mon Sep 17 00:00:00 2001 From: Mike Dvorkin Date: Wed, 8 Dec 2010 20:39:46 -0800 Subject: Take into account current time zone when serializing datetime values [#6096 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activemodel/lib/active_model/serializers/xml.rb | 1 + activerecord/test/cases/xml_serialization_test.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index 26a134568c..b897baa614 100644 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -17,6 +17,7 @@ module ActiveModel def initialize(name, serializable, raw_value=nil) @name, @serializable = name, serializable + raw_value = raw_value.in_time_zone if raw_value.respond_to?(:in_time_zone) @value = raw_value || @serializable.send(name) @type = compute_type end diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb index b11b340e94..2003e25e35 100644 --- a/activerecord/test/cases/xml_serialization_test.rb +++ b/activerecord/test/cases/xml_serialization_test.rb @@ -4,6 +4,7 @@ require 'models/post' require 'models/author' require 'models/comment' require 'models/company_in_module' +require 'models/toy' class XmlSerializationTest < ActiveRecord::TestCase def test_should_serialize_default_root @@ -83,6 +84,26 @@ class DefaultXmlSerializationTest < ActiveRecord::TestCase end end +class DefaultXmlSerializationTimezoneTest < ActiveRecord::TestCase + def test_should_serialize_datetime_with_timezone + timezone, Time.zone = Time.zone, "Pacific Time (US & Canada)" + + toy = Toy.create(:name => 'Mickey', :updated_at => Time.utc(2006, 8, 1)) + assert_match %r{2006-07-31T17:00:00-07:00}, toy.to_xml + ensure + Time.zone = timezone + end + + def test_should_serialize_datetime_with_timezone_reloaded + timezone, Time.zone = Time.zone, "Pacific Time (US & Canada)" + + toy = Toy.create(:name => 'Minnie', :updated_at => Time.utc(2006, 8, 1)).reload + assert_match %r{2006-07-31T17:00:00-07:00}, toy.to_xml + ensure + Time.zone = timezone + end +end + class NilXmlSerializationTest < ActiveRecord::TestCase def setup @xml = Contact.new.to_xml(:root => 'xml_contact') -- cgit v1.2.3 From b4ec6f0d3964fb550dbd42a9cd23925cff5f552e Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 9 Dec 2010 18:16:18 +1000 Subject: Move ActiveModel::Lint::Tests documentation to be above module declaration so it appears in the API docs for this module. --- activemodel/lib/active_model/lint.rb | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb index d7a6da48ca..957d1b9d70 100644 --- a/activemodel/lib/active_model/lint.rb +++ b/activemodel/lib/active_model/lint.rb @@ -1,19 +1,19 @@ -# == Active Model Lint Tests -# -# You can test whether an object is compliant with the Active Model API by -# including ActiveModel::Lint::Tests in your TestCase. It will include -# tests that tell you whether your object is fully compliant, or if not, -# which aspects of the API are not implemented. -# -# These tests do not attempt to determine the semantic correctness of the -# returned values. For instance, you could implement valid? to always -# return true, and the tests would pass. It is up to you to ensure that -# the values are semantically meaningful. -# -# Objects you pass in are expected to return a compliant object from a -# call to to_model. It is perfectly fine for to_model to return self. module ActiveModel module Lint + # == Active Model Lint Tests + # + # You can test whether an object is compliant with the Active Model API by + # including ActiveModel::Lint::Tests in your TestCase. It will include + # tests that tell you whether your object is fully compliant, or if not, + # which aspects of the API are not implemented. + # + # These tests do not attempt to determine the semantic correctness of the + # returned values. For instance, you could implement valid? to always + # return true, and the tests would pass. It is up to you to ensure that + # the values are semantically meaningful. + # + # Objects you pass in are expected to return a compliant object from a + # call to to_model. It is perfectly fine for to_model to return self. module Tests # == Responds to to_key -- cgit v1.2.3 From a47f31c59399204947dacb2b54fccc6987b137ee Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 9 Dec 2010 20:01:42 +1000 Subject: mapper.rb: add "options" to make the default-to-namespace-name line read better --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 5a38158e9f..11ac8fed20 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -563,7 +563,7 @@ module ActionDispatch # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"} # === Supported options # - # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ all default to the name of the namespace. + # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ options all default to the name of the namespace. # # [:path] # The path prefix for the routes. -- cgit v1.2.3 From d2f98e5b556815f7b9cf84d3702d93a0bfb7d433 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 9 Dec 2010 20:03:14 +1000 Subject: indent code samples for mount doc --- actionpack/lib/action_dispatch/routing/mapper.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 11ac8fed20..086366c740 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -269,18 +269,18 @@ module ActionDispatch # Mount a Rack-based application to be used within the application. # - # mount SomeRackApp, :at => "some_route" + # mount SomeRackApp, :at => "some_route" # # Alternatively: # - # mount(SomeRackApp => "some_route") + # mount(SomeRackApp => "some_route") # # All mounted applications come with routing helpers to access them. # These are named after the class specified, so for the above example # the helper is either +some_rack_app_path+ or +some_rack_app_url+. # To customize this helper's name, use the +:as+ option: # - # mount(SomeRackApp => "some_route", :as => "exciting") + # mount(SomeRackApp => "some_route", :as => "exciting") # # This will generate the +exciting_path+ and +exciting_url+ helpers # which can be used to navigate to this mounted app. -- cgit v1.2.3 From fe0e3880e8ce8311882f3856b689c2f79a13ced4 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Thu, 9 Dec 2010 20:21:08 +1000 Subject: root route should go at the *top* of the routes file, because it is the most popular route and should be matched first --- actionpack/lib/action_dispatch/routing/mapper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 086366c740..cf95f6fa53 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -247,7 +247,9 @@ module ActionDispatch # # root :to => 'pages#main' # - # You should put the root route at the end of config/routes.rb. + # You should put the root route at the top of config/routes.rb, + # because this means it will be matched first. As this is the most popular route + # of most Rails applications, this is beneficial. def root(options = {}) match '/', options.reverse_merge(:as => :root) end -- cgit v1.2.3 From 28cf77203b0b5af648e441b5d9cfb75863ba2f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 9 Dec 2010 13:15:25 +0100 Subject: Check if the routes object really responds to define_mount_prefix. --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- actionpack/test/dispatch/mount_test.rb | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 5a38158e9f..47de5848fa 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -327,7 +327,7 @@ module ActionDispatch end def define_generate_prefix(app, name) - return unless app.respond_to?(:routes) + return unless app.respond_to?(:routes) && app.routes.respond_to?(:define_mounted_helper) _route = @set.named_routes.routes[name.to_sym] _routes = @set diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index 0f584af31e..1a032539b9 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -2,6 +2,17 @@ require 'abstract_unit' class TestRoutingMount < ActionDispatch::IntegrationTest Router = ActionDispatch::Routing::RouteSet.new + + class FakeEngine + def self.routes + Object.new + end + + def self.call(env) + [200, {"Content-Type" => "text/html"}, ["OK"]] + end + end + Router.draw do SprocketsApp = lambda { |env| [200, {"Content-Type" => "text/html"}, ["#{env["SCRIPT_NAME"]} -- #{env["PATH_INFO"]}"]] @@ -10,6 +21,8 @@ class TestRoutingMount < ActionDispatch::IntegrationTest mount SprocketsApp, :at => "/sprockets" mount SprocketsApp => "/shorthand" + mount FakeEngine, :at => "/fakeengine" + scope "/its_a" do mount SprocketsApp, :at => "/sprocket" end @@ -33,4 +46,9 @@ class TestRoutingMount < ActionDispatch::IntegrationTest get "/shorthand/omg" assert_equal "/shorthand -- /omg", response.body end + + def test_with_fake_engine_does_not_call_invalid_method + get "/fakeengine" + assert_equal "OK", response.body + end end \ No newline at end of file -- cgit v1.2.3 From cc70ac9508dff6a4406f446d7a53d05f6c7d9f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 9 Dec 2010 13:15:40 +0100 Subject: Properly check the arity for template handlers. --- actionpack/lib/action_view/template.rb | 2 +- actionpack/test/template/render_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 831a19654e..0d8373ef14 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -275,7 +275,7 @@ module ActionView end arity = @handler.respond_to?(:arity) ? @handler.arity : @handler.method(:call).arity - code = arity == 1 ? @handler.call(self) : @handler.call(self, view) + code = arity.abs == 1 ? @handler.call(self) : @handler.call(self, view) # Make sure that the resulting String to be evalled is in the # encoding of the code diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 8087429d62..38bedd2e4e 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -225,6 +225,11 @@ module RenderTestCases %'"#{template.class} #{view.class}"' end + def test_render_inline_with_render_from_to_proc + ActionView::Template.register_template_handler :ruby_handler, :source.to_proc + assert_equal '3', @view.render(:inline => "(1 + 2).to_s", :type => :ruby_handler) + end + def test_render_inline_with_template_handler_with_view ActionView::Template.register_template_handler :with_view, WithViewHandler assert_equal 'ActionView::Template ActionView::Base', @view.render(:inline => "Hello, World!", :type => :with_view) -- cgit v1.2.3 From 8e6d27641cf213a13d51491cecfbfa1d3c8822fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 9 Dec 2010 13:40:45 +0100 Subject: Clean up asset_host and asset_path. --- actionmailer/lib/action_mailer/railtie.rb | 4 ++-- actionpack/lib/action_controller/railtie.rb | 4 ++-- railties/lib/rails/application/configuration.rb | 19 +++---------------- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb index d67a5b6521..4ec478067f 100644 --- a/actionmailer/lib/action_mailer/railtie.rb +++ b/actionmailer/lib/action_mailer/railtie.rb @@ -19,8 +19,8 @@ module ActionMailer options.stylesheets_dir ||= paths["public/stylesheets"].first # make sure readers methods get compiled - options.asset_path ||= nil - options.asset_host ||= nil + options.asset_path ||= app.config.asset_path + options.asset_host ||= app.config.asset_host ActiveSupport.on_load(:action_mailer) do include AbstractController::UrlFor diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index c5a661f2b0..f0c29825ba 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -27,8 +27,8 @@ module ActionController options.page_cache_directory ||= paths["public"].first # make sure readers methods get compiled - options.asset_path ||= nil - options.asset_host ||= nil + options.asset_path ||= app.config.asset_path + options.asset_host ||= app.config.asset_host ActiveSupport.on_load(:action_controller) do include app.routes.mounted_helpers diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 3505388479..8cd496781b 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -4,12 +4,12 @@ require 'rails/engine/configuration' module Rails class Application class Configuration < ::Rails::Engine::Configuration - attr_accessor :allow_concurrency, :cache_classes, :cache_store, + attr_accessor :allow_concurrency, :asset_host, :cache_classes, :cache_store, :encoding, :consider_all_requests_local, :dependency_loading, - :filter_parameters, :log_level, :logger, + :filter_parameters, :helpers_paths, :log_level, :logger, :preload_frameworks, :reload_plugins, :secret_token, :serve_static_assets, :session_options, - :time_zone, :whiny_nils, :helpers_paths + :time_zone, :whiny_nils def initialize(*) super @@ -24,22 +24,9 @@ module Rails @session_options = {} @time_zone = "UTC" @middleware = app_middleware - @asset_path = '/' @generators = app_generators end - def asset_path=(value) - action_mailer.asset_path = value if respond_to?(:action_mailer) && action_mailer - action_controller.asset_path = value if respond_to?(:action_controller) && action_controller - super(value) - end - - def asset_host=(value) - action_mailer.asset_host = value if action_mailer - action_controller.asset_host = value if action_controller - super(value) - end - def compiled_asset_path "/" end -- cgit v1.2.3 From d3f7e565d1a6ee15605971c3f3fea6e756a1ff99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 9 Dec 2010 14:06:44 +0100 Subject: Ensure resolvers backward compatibility. --- actionpack/lib/action_view/lookup_context.rb | 2 +- actionpack/lib/action_view/path_set.rb | 6 ++---- actionpack/lib/action_view/template/resolver.rb | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index 27f94a73a6..d524c68450 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -109,7 +109,7 @@ module ActionView def args_for_lookup(name, prefix, partial, keys) #:nodoc: name, prefix = normalize_name(name, prefix) - [name, prefix, partial || false, @details, keys, details_key] + [name, prefix, partial || false, @details, details_key, keys] end # Support legacy foo.erb names even though we now ignore .erb diff --git a/actionpack/lib/action_view/path_set.rb b/actionpack/lib/action_view/path_set.rb index dc26d75ff3..fa35120a0d 100644 --- a/actionpack/lib/action_view/path_set.rb +++ b/actionpack/lib/action_view/path_set.rb @@ -10,10 +10,8 @@ module ActionView #:nodoc: METHOD end - def find(path, prefix = nil, partial = false, details = {}, keys = [], key = nil) - template = find_all(path, prefix, partial, details, keys, key).first - raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template - template + def find(*args) + find_all(*args).first || raise(MissingTemplate.new(self, "#{args[1]}/#{args[0]}", args[3], args[2])) end def find_all(*args) diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 9f15661669..a17454da28 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -15,7 +15,7 @@ module ActionView end # Normalizes the arguments and passes it on to find_template. - def find_all(name, prefix=nil, partial=false, details={}, locals=[], key=nil) + def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[]) cached(key, [name, prefix, partial], details, locals) do find_templates(name, prefix, partial, details) end -- cgit v1.2.3 From 7b9f634e15bfc3f92f4ac7e18537443a55306c10 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 8 Dec 2010 23:29:25 +0100 Subject: Refactor db:load:config to automatically pick additional migrations if ENGINE_PATH is available --- activerecord/lib/active_record/railties/databases.rake | 6 ++++++ .../rails/generators/rails/plugin_new/plugin_new_generator.rb | 10 ---------- .../lib/rails/generators/rails/plugin_new/templates/Rakefile | 6 +----- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 2e73cd4f1d..a4fc18148e 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -3,6 +3,12 @@ db_namespace = namespace :db do require 'active_record' ActiveRecord::Base.configurations = Rails.application.config.database_configuration ActiveRecord::Migrator.migrations_paths = Rails.application.paths["db/migrate"].to_a + + if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH) + if engine.paths["db/migrate"].existent + ActiveRecord::Migrator.migrations_paths += engine.paths["db/migrate"].to_a + end + end end namespace :create do diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 77497f9e44..9c54b98238 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -70,16 +70,6 @@ task :default => :test if mountable? template "rails/routes.rb", "#{dummy_path}/config/routes.rb", :force => true end - - if full? && !options[:skip_active_record] - append_file "#{dummy_path}/Rakefile", <<-EOF - -task :"db:load_config" do - ActiveRecord::Migrator.migrations_paths = Rails.application.config.paths["db/migrate"].to_a + - <%= camelized %>::Engine.config.paths["db/migrate"].to_a -end - EOF - end end def test_dummy_clean diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile index b7cab3859a..25292f59ad 100755 --- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile @@ -17,12 +17,8 @@ end <% if full? && !options[:skip_active_record] -%> namespace :app do + ENGINE_PATH = File.expand_path("..", __FILE__) load File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__) - - task :"db:load_config" do - ActiveRecord::Migrator.migrations_paths = Rails.application.config.paths["db/migrate"].to_a + - <%= camelized %>::Engine.config.paths["db/migrate"].to_a - end end <% end -%> -- cgit v1.2.3 From 0027b654215cd6310d0d17daeaafec8ebbfb38d4 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 4 Dec 2010 17:40:54 -0500 Subject: Ensure that while caching a page rails takes into account the resolved mime type for the request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#6110 state:resolved] Signed-off-by: José Valim --- actionpack/lib/action_controller/caching/pages.rb | 20 +++++++++++++------- actionpack/test/controller/caching_test.rb | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index 104157d0b1..3e57d2c236 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -70,9 +70,9 @@ module ActionController #:nodoc: # Manually cache the +content+ in the key determined by +path+. Example: # cache_page "I'm the cached content", "/lists/show" - def cache_page(content, path) + def cache_page(content, path, extension = nil) return unless perform_caching - path = page_cache_path(path) + path = page_cache_path(path, extension) instrument_page_cache :write_page, path do FileUtils.makedirs(File.dirname(path)) @@ -97,14 +97,16 @@ module ActionController #:nodoc: end private - def page_cache_file(path) + def page_cache_file(path, extension) name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/')) - name << page_cache_extension unless (name.split('/').last || name).include? '.' + unless (name.split('/').last || name).include? '.' + name << (extension || self.page_cache_extension) + end return name end - def page_cache_path(path) - page_cache_directory + page_cache_file(path) + def page_cache_path(path, extension = nil) + page_cache_directory + page_cache_file(path, extension) end def instrument_page_cache(name, path) @@ -145,7 +147,11 @@ module ActionController #:nodoc: request.path end - self.class.cache_page(content || response.body, path) + if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present? + extension = ".#{type_symbol}" + end + + self.class.cache_page(content || response.body, path, extension) end end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 914ae56032..c7b54eb0ba 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -16,6 +16,7 @@ end class PageCachingTestController < CachingController caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? } caches_page :found, :not_found + caches_page :about_me def ok @@ -47,6 +48,14 @@ class PageCachingTestController < CachingController def trailing_slash render :text => "Sneak attack" end + + def about_me + respond_to do |format| + format.html {render :text => 'I am html'} + format.xml {render :text => 'I am xml'} + end + end + end class PageCachingTest < ActionController::TestCase @@ -111,6 +120,13 @@ class PageCachingTest < ActionController::TestCase assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html") end + def test_should_obey_http_accept_attribute + @request.env['HTTP_ACCEPT'] = 'text/xml' + get :about_me + assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/about_me.xml") + assert_equal 'I am xml', @response.body + end + def test_should_cache_with_trailing_slash_on_url @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash/' assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html") -- cgit v1.2.3 From 6f8958277b4ba96dc6185a476a7a00a6c1d99892 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 9 Dec 2010 18:02:09 +0100 Subject: Fix test on finding migrations from 2 paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/migration_test.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 95b7ce9f34..1a65045ded 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1279,11 +1279,13 @@ if ActiveRecord::Base.connection.supports_migrations? directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps'] migrations = ActiveRecord::Migrator.new(:up, directories).migrations - [[20090101010101, "PeopleHaveHobbies"], [20090101010202, "PeopleHaveDescriptions"], - [20100101010101, "PeopleHaveLastNames"], [20100201010101, "WeNeedReminders"], - [20100301010101, "InnocentJointable"]].each_with_index do |pair, i| - assert_equal migrations[i].version, pair.first - assert_equal migrations[i].name, pair.last + [[20090101010101, "PeopleHaveHobbies"], + [20090101010202, "PeopleHaveDescriptions"], + [20100101010101, "ValidWithTimestampsPeopleHaveLastNames"], + [20100201010101, "ValidWithTimestampsWeNeedReminders"], + [20100301010101, "ValidWithTimestampsInnocentJointable"]].each_with_index do |pair, i| + assert_equal pair.first, migrations[i].version + assert_equal pair.last, migrations[i].name end end -- cgit v1.2.3 From e12810178cebc0c40a90b4aba3976537852b71b4 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 9 Dec 2010 18:04:44 +0100 Subject: Use Array.wrap instead of using ternary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/connection_adapters/abstract/schema_statements.rb | 2 +- activerecord/lib/active_record/migration.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 3eb81b0dfa..d4aefada22 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -443,7 +443,7 @@ module ActiveRecord end def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths) - migrations_paths = [migrations_paths] unless migrations_paths.kind_of?(Array) + migrations_paths = Array.wrap(migrations_paths) version = version.to_i sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 130b43cc15..ea9950de08 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -573,7 +573,7 @@ module ActiveRecord def migrations_paths @migrations_paths ||= ['db/migrate'] # just to not break things if someone uses: migration_path = some_string - @migrations_paths.kind_of?(Array) ? @migrations_paths : [@migrations_paths] + Array.wrap(@migrations_paths) end def migrations_path @@ -581,7 +581,7 @@ module ActiveRecord end def migrations(paths) - paths = [paths] unless paths.kind_of?(Array) + paths = Array.wrap(paths) files = Dir[*paths.map { |p| "#{p}/[0-9]*_*.rb" }] -- cgit v1.2.3 From bba3dacc3dc6ac379209f2eda0da5d2dd93d6b04 Mon Sep 17 00:00:00 2001 From: "Robert Pankowecki (Gavdi)" Date: Fri, 26 Nov 2010 19:16:13 +0100 Subject: Simplifies observer implementation [#6065 state:resolved] --- activerecord/lib/active_record/observer.rb | 30 ++++-------------------------- activerecord/test/cases/lifecycle_test.rb | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index 022cf109af..f0da4202bf 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -89,51 +89,29 @@ module ActiveRecord # singletons and that call instantiates and registers them. # class Observer < ActiveModel::Observer - class_attribute :observed_methods - self.observed_methods = [].freeze def initialize super observed_descendants.each { |klass| add_observer!(klass) } end - def self.method_added(method) - method = method.to_sym - - if ActiveRecord::Callbacks::CALLBACKS.include?(method) - self.observed_methods += [method] - self.observed_methods.freeze - end - end - protected def observed_descendants observed_classes.sum([]) { |klass| klass.descendants } end - def observe_callbacks? - self.class.observed_methods.any? - end - def add_observer!(klass) super - define_callbacks klass if observe_callbacks? + define_callbacks klass end def define_callbacks(klass) - existing_methods = klass.instance_methods.map { |m| m.to_sym } observer = self - observer_name = observer.class.name.underscore.gsub('/', '__') - self.class.observed_methods.each do |method| - callback = :"_notify_#{observer_name}_for_#{method}" - unless existing_methods.include? callback - klass.send(:define_method, callback) do # def _notify_user_observer_for_before_save - observer.update(method, self) # observer.update(:before_save, self) - end # end - klass.send(method, callback) # before_save :_notify_user_observer_for_before_save - end + ActiveRecord::Callbacks::CALLBACKS.each do |callback| + next unless respond_to?(callback) + klass.send(callback){|record| observer.send(callback, record)} end end end diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb index 233338498f..b8c3ffb9cb 100644 --- a/activerecord/test/cases/lifecycle_test.rb +++ b/activerecord/test/cases/lifecycle_test.rb @@ -9,10 +9,19 @@ class SpecialDeveloper < Developer; end class SalaryChecker < ActiveRecord::Observer observe :special_developer + attr_accessor :last_saved def before_save(developer) return developer.salary > 80000 end + + module Implementation + def after_save(developer) + self.last_saved = developer + end + end + include Implementation + end class TopicaAuditor < ActiveRecord::Observer @@ -179,4 +188,11 @@ class LifecycleTest < ActiveRecord::TestCase developer = SpecialDeveloper.new :name => 'Rookie', :salary => 50000 assert !developer.save, "allowed to save a developer with too low salary" end + + test "able to call methods defined with included module" do # https://rails.lighthouseapp.com/projects/8994/tickets/6065-activerecordobserver-is-not-aware-of-method-added-by-including-modules + SalaryChecker.instance # activate + developer = SpecialDeveloper.create! :name => 'Roger', :salary => 100000 + assert_equal developer, SalaryChecker.instance.last_saved + end + end -- cgit v1.2.3 From 3cbf5d9794cc49783fcbcb160cdd908981ee8f84 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 9 Dec 2010 11:13:00 -0800 Subject: removing useless lasgn --- .../associations/class_methods/join_dependency/join_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index b063d927b9..024ce4114b 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -136,7 +136,7 @@ module ActiveRecord conditions << process_conditions(options[:conditions], aliased_table_name) end - relation = relation.on(*conditions) + relation.on(*conditions) end def join_has_and_belongs_to_many_to(relation) -- cgit v1.2.3 From 6110b0c54dde6251f7501e6421a7002a99779537 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 9 Dec 2010 11:14:54 -0800 Subject: fixing documentation, thanks Ivan Evtuhovich --- activerecord/lib/active_record/transactions.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 654c475aed..f8c6c7b53e 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -141,7 +141,8 @@ module ActiveRecord # end # end # - # User.find(:all) # => empty + # User.find(:all) # => Returns Kotori and Nemu, because + # AcriveRecord::Rollback do not re-raise # # It is also possible to requires a sub-transaction by passing # :requires_new => true. If anything goes wrong, the -- cgit v1.2.3 From 1860d873f23446ec9c442a9a8625b74f0dd42594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 9 Dec 2010 21:18:41 +0100 Subject: Ensure asset_path defaults to nil for application --- railties/lib/rails/application.rb | 4 ++++ railties/lib/rails/engine.rb | 7 ++++++- railties/test/application/configuration_test.rb | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 182068071d..b248bc737c 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -138,6 +138,10 @@ module Rails protected + def default_asset_path + nil + end + def default_middleware_stack ActionDispatch::MiddlewareStack.new.tap do |middleware| rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index b188fdfca1..cda0e0a135 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -499,7 +499,7 @@ module Rails end initializer :append_asset_paths do - config.asset_path ||= "/#{railtie_name}%s" + config.asset_path ||= default_asset_path public_path = paths["public"].first if config.compiled_asset_path && File.exist?(public_path) @@ -553,6 +553,11 @@ module Rails end protected + + def default_asset_path + "/#{railtie_name}%s" + end + def routes? defined?(@routes) end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index b8d0854286..c12c4a4660 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -95,6 +95,11 @@ module ApplicationTests assert AppTemplate::Application.config.allow_concurrency end + test "asset_path defaults to nil for application" do + require "#{app_path}/config/environment" + assert_equal nil, AppTemplate::Application.config.asset_path + end + test "the application can be marked as threadsafe when there are no frameworks" do FileUtils.rm_rf("#{app_path}/config/environments") add_to_config <<-RUBY -- cgit v1.2.3 From b2a042570cf55c716e44865cf6388f9079a86690 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 9 Dec 2010 13:26:41 -0800 Subject: just use core methods --- activerecord/lib/active_record/observer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index f0da4202bf..a09c392f9d 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -98,7 +98,7 @@ module ActiveRecord protected def observed_descendants - observed_classes.sum([]) { |klass| klass.descendants } + observed_classes.map { |klass| klass.descendants }.flatten end def add_observer!(klass) -- cgit v1.2.3 From 0e8280b19322bd3fdd7e18fc6ba35d45e986e611 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 9 Dec 2010 13:37:15 -0800 Subject: reuse the superclass methods for shorter codes --- activerecord/lib/active_record/observer.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index a09c392f9d..8b011ad9af 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -90,15 +90,11 @@ module ActiveRecord # class Observer < ActiveModel::Observer - def initialize - super - observed_descendants.each { |klass| add_observer!(klass) } - end - protected - def observed_descendants - observed_classes.map { |klass| klass.descendants }.flatten + def observed_classes + klasses = super + klasses + klasses.map { |klass| klass.descendants }.flatten end def add_observer!(klass) -- cgit v1.2.3 From 3e7c351b486356757631de9a89ea775c9bed658f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 9 Dec 2010 15:12:48 -0800 Subject: preheating cache so that tests can run in isolation --- activerecord/test/cases/associations/eager_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index ea86ac29d0..c96ca90750 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -24,6 +24,11 @@ class EagerAssociationTest < ActiveRecord::TestCase :owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books, :developers, :projects, :developers_projects + def setup + # preheat table existence caches + Comment.find_by_id(1) + end + def test_loading_with_one_association posts = Post.find(:all, :include => :comments) post = posts.find { |p| p.id == 1 } -- cgit v1.2.3 From 12f67a70697212b55cde66f89ccd4caf0e9f4dad Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 9 Dec 2010 15:31:09 -0800 Subject: use inject rather than lasgn --- .../class_methods/join_dependency/join_association.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 024ce4114b..5e37542416 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -121,14 +121,13 @@ module ActiveRecord # If the target table is an STI model then we must be sure to only include records of # its type and its sub-types. unless active_record.descends_from_active_record? - sti_column = target_table[active_record.inheritance_column] - + sti_column = target_table[active_record.inheritance_column] + subclasses = active_record.descendants sti_condition = sti_column.eq(active_record.sti_name) - active_record.descendants.each do |subclass| - sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) - end - conditions << sti_condition + conditions << subclasses.inject(sti_condition) { |attr,subclass| + attr.or(sti_column.eq(subclass.sti_name)) + } end # If the reflection has conditions, add them -- cgit v1.2.3 From f0d4b7f2a655ca2fb435af798714fee2de232d1e Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 10 Dec 2010 09:34:40 +1000 Subject: root method options are the same as the match method --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index cf95f6fa53..3039f88f71 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -247,6 +247,8 @@ module ActionDispatch # # root :to => 'pages#main' # + # For options, see the +match+ method's documentation, as +root+ uses it internally. + # # You should put the root route at the top of config/routes.rb, # because this means it will be matched first. As this is the most popular route # of most Rails applications, this is beneficial. -- cgit v1.2.3 From 06ba9eaa62f756b70217f4d1e277c37e835343d4 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 9 Dec 2010 15:54:07 -0800 Subject: move join creation a little closer to where it is used --- .../associations/class_methods/join_dependency/join_association.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 5e37542416..b0091c613d 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -116,8 +116,6 @@ module ActiveRecord end def join_target_table(relation, *conditions) - relation = relation.join(target_table, join_type) - # If the target table is an STI model then we must be sure to only include records of # its type and its sub-types. unless active_record.descends_from_active_record? @@ -135,7 +133,9 @@ module ActiveRecord conditions << process_conditions(options[:conditions], aliased_table_name) end - relation.on(*conditions) + join = relation.join(target_table, join_type) + + join.on(*conditions) end def join_has_and_belongs_to_many_to(relation) -- cgit v1.2.3 From dbf955c03b8d185b7977d90281389cf52d546c5d Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Fri, 10 Dec 2010 15:55:46 +1000 Subject: Action Controller Overview: Remove dead link to the API docs for filters --- actionpack/lib/action_dispatch/routing/mapper.rb | 1 + railties/guides/source/action_controller_overview.textile | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 3039f88f71..494cdbf308 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1089,6 +1089,7 @@ module ActionDispatch end end + # See ActionDispatch::Routing::Mapper::Scoping#namespace def namespace(path, options = {}) if resource_scope? nested { super } diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile index 0d6c66f168..0d6919a205 100644 --- a/railties/guides/source/action_controller_overview.textile +++ b/railties/guides/source/action_controller_overview.textile @@ -478,8 +478,6 @@ end Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets the controller passed as an argument. The filter class has a class method +filter+ which gets run before or after the action, depending on if it's a before or after filter. Classes used as around filters can also use the same +filter+ method, which will get run in the same way. The method must +yield+ to execute the action. Alternatively, it can have both a +before+ and an +after+ method that are run before and after the action. -The Rails API documentation has "more information on using filters":http://ap.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html. - h3. Verification Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using +XMLHttpRequest+ (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://ap.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter". -- cgit v1.2.3 From 3d01ef6dd1ed83b44702d117b4c1c658dfa8ba75 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Fri, 10 Dec 2010 02:14:58 +0100 Subject: require core_ext/array/wrap in active_record/migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/migration.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index ea9950de08..640111096d 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/array/wrap" + module ActiveRecord # Exception that can be raised to stop migrations from going backwards. class IrreversibleMigration < ActiveRecordError -- cgit v1.2.3 From 61817d26301e7a89e3438042640213125c4d235a Mon Sep 17 00:00:00 2001 From: vijay Date: Fri, 10 Dec 2010 23:34:48 +0530 Subject: fixed typos and rephrased few sentences in routing --- railties/guides/source/routing.textile | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 2f5c88b8c3..bc38e4a6e5 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -3,7 +3,7 @@ h2. Rails Routing from the Outside In This guide covers the user-facing features of Rails routing. By referring to this guide, you will be able to: * Understand the code in +routes.rb+ -* Construct your own routes, using either the preferred resourceful style or with the match method +* Construct your own routes, using either the preferred resourceful style or the match method * Identify what parameters to expect an action to receive * Automatically create paths and URLs using route helpers * Use advanced techniques such as constraints and Rack endpoints @@ -50,7 +50,7 @@ Resource routing allows you to quickly declare all of the common routes for a gi h4. Resources on the Web -Browsers request pages from Rails by making a request for a URL using a specific HTTP method, such as +GET+, +POST+, +PUT+ and +DELETE+. Each method is a request to perform an operation on the resource. A resource route maps a number of related request to the actions in a single controller. +Browsers request pages from Rails by making a request for a URL using a specific HTTP method, such as +GET+, +POST+, +PUT+ and +DELETE+. Each method is a request to perform an operation on the resource. A resource route maps a number of related requests to actions in a single controller. When your Rails application receives an incoming request for @@ -470,7 +470,7 @@ This route would match paths such as +/photos/A12345+. You can more succinctly e match 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/ -+:constraints+ takes regular expression. However note that regexp anchors can't be used within constraints. For example following route will not work: ++:constraints+ takes regular expressions with the restriction that regexp anchors can't be used. For example, the following route will not work: match '/:id' => 'posts#show', :constraints => {:id => /^\d/} @@ -536,7 +536,7 @@ match 'photos/*other' => 'photos#unknown' This route would match +photos/12+ or +/photos/long/path/to/12+, setting +params[:other]+ to +"12"+ or +"long/path/to/12"+. -Wildcard segments do not need to be last in a route. For example +Wildcard segments can occur anywhere in a route. For example, match 'books/*section/:title' => 'books#show' @@ -544,7 +544,7 @@ match 'books/*section/:title' => 'books#show' would match +books/some/section/last-words-a-memoir+ with +params[:section]+ equals +"some/section"+, and +params[:title]+ equals +"last-words-a-memoir"+. -Techincally a route can have even more than one wildard segment indeed, the matcher assigns segments to parameters in an intuitive way. For instance +Technically a route can have even more than one wildcard segment. The matcher assigns segments to parameters in an intuitive way. For example, match '*a/foo/*b' => 'test#index' @@ -641,7 +641,7 @@ constraints(:id => /[A-Z][A-Z][0-9]+/) do end -NOTE: Of course, you can use the more advanced constraints available in non-resourceful routes in this context +NOTE: Of course, you can use the more advanced constraints available in non-resourceful routes in this context. h4. Overriding the Named Helpers @@ -651,7 +651,7 @@ The +:as+ option lets you override the normal naming for the named route helpers resources :photos, :as => "images" -will recognize incoming paths beginning with +/photos+ and route the requests to +PhotosController+: +will recognize incoming paths beginning with +/photos+ and route the requests to +PhotosController+, but use the value of the :as option to name the helpers. |_.HTTP verb|_.Path |_.action |_.named helper | |GET |/photos |index | images_path | @@ -679,7 +679,7 @@ This would cause the routing to recognize paths such as NOTE: The actual action names aren't changed by this option. The two paths shown would still route to the +new+ and +edit+ actions. -TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can use a scope: +TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can use a scope. scope :path_names => { :new => "make" } do @@ -715,7 +715,7 @@ NOTE: The +namespace+ scope will automatically add +:as+ as well as +:module+ an h4. Restricting the Routes Created -By default, Rails creates routes for all seven of the default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the +:only+ and +:except+ options to fine-tune this behavior. The +:only+ option tells Rails to create only the specified routes: +By default, Rails creates routes for the seven default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the +:only+ and +:except+ options to fine-tune this behavior. The +:only+ option tells Rails to create only the specified routes: resources :photos, :only => [:index, :show] @@ -816,7 +816,7 @@ Routes should be included in your testing strategy (just like the rest of your a h5. The +assert_generates+ Assertion -Use +assert_generates+ to assert that a particular set of options generate a particular path. You can use this with default routes or custom routes ++assert_generates+ asserts that a particular set of options generate a particular path and can be used with default routes or custom routes. assert_generates "/photos/1", { :controller => "photos", :action => "show", :id => "1" } @@ -825,7 +825,7 @@ assert_generates "/about", :controller => "pages", :action => "about" h5. The +assert_recognizes+ Assertion -The +assert_recognizes+ assertion is the inverse of +assert_generates+. It asserts that Rails recognizes the given path and routes it to a particular spot in your application. ++assert_recognizes+ is the inverse of +assert_generates+. It asserts that a given path is recognized and routes it to a particular spot in your application. assert_recognizes({ :controller => "photos", :action => "show", :id => "1" }, "/photos/1") -- cgit v1.2.3 From 88bc49f2f7560d1afb53f480720a809e101e8f7a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 11:17:59 -0800 Subject: dealing with an AST manager, not a relation, so fix the variable names --- .../lib/active_record/relation/query_methods.rb | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 6886c7538b..1e6a928da3 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -163,7 +163,7 @@ module ActiveRecord end def build_arel - arel = table + arel = table.from table arel = build_joins(arel, @joins_values) unless @joins_values.empty? @@ -247,7 +247,7 @@ module ActiveRecord end end - def build_joins(relation, joins) + def build_joins(manager, joins) joins = joins.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq association_joins = joins.find_all do |join| @@ -257,7 +257,7 @@ module ActiveRecord stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) non_association_joins = (joins - association_joins - stashed_association_joins) - join_ast = custom_join_ast(relation, non_association_joins) + join_ast = custom_join_ast(manager.froms.first, non_association_joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_ast) @@ -267,19 +267,19 @@ module ActiveRecord # FIXME: refactor this to build an AST join_dependency.join_associations.each do |association| - relation = association.join_to(relation) + manager = association.join_to(manager) end - if Arel::Table === relation - relation.from(join_ast || relation) + if Arel::Table === manager + manager.from(join_ast || manager) else - if relation.froms.length > 0 && join_ast - join_ast.left = relation.froms.first - relation.from join_ast - elsif relation.froms.length == 0 && join_ast - relation.from(join_ast) + if manager.froms.length > 0 && join_ast + join_ast.left = manager.froms.first + manager.from join_ast + elsif manager.froms.length == 0 && join_ast + manager.from(join_ast) else - relation + manager end end end -- cgit v1.2.3 From 82944b95a909965ddec189058fb2040dcf0aa54a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 11:24:05 -0800 Subject: eliminate lasgns --- .../lib/active_record/relation/query_methods.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1e6a928da3..b2f741026b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -165,23 +165,23 @@ module ActiveRecord def build_arel arel = table.from table - arel = build_joins(arel, @joins_values) unless @joins_values.empty? + build_joins(arel, @joins_values) unless @joins_values.empty? - arel = collapse_wheres(arel, (@where_values - ['']).uniq) + collapse_wheres(arel, (@where_values - ['']).uniq) - arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? + arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? - arel = arel.take(@limit_value) if @limit_value - arel = arel.skip(@offset_value) if @offset_value + arel.take(@limit_value) if @limit_value + arel.skip(@offset_value) if @offset_value - arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? + arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? - arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty? + arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty? - arel = build_select(arel, @select_values.uniq) + build_select(arel, @select_values.uniq) - arel = arel.from(@from_value) if @from_value - arel = arel.lock(@lock_value) if @lock_value + arel.from(@from_value) if @from_value + arel.lock(@lock_value) if @lock_value arel end -- cgit v1.2.3 From 4dec00a9d8262d85b733aa3bd5b68d2b7de950a7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 11:30:22 -0800 Subject: manager will always be a manager --- activerecord/lib/active_record/relation/query_methods.rb | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index b2f741026b..a98b650524 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -270,17 +270,13 @@ module ActiveRecord manager = association.join_to(manager) end - if Arel::Table === manager - manager.from(join_ast || manager) + if manager.froms.length > 0 && join_ast + join_ast.left = manager.froms.first + manager.from join_ast + elsif manager.froms.length == 0 && join_ast + manager.from(join_ast) else - if manager.froms.length > 0 && join_ast - join_ast.left = manager.froms.first - manager.from join_ast - elsif manager.froms.length == 0 && join_ast - manager.from(join_ast) - else - manager - end + manager end end -- cgit v1.2.3 From ec72b6b3ec1b78c22189ee475c1af2fdab317489 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 11:33:24 -0800 Subject: froms should never equal 0 --- activerecord/lib/active_record/relation/query_methods.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index a98b650524..145ae6ca97 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -273,8 +273,6 @@ module ActiveRecord if manager.froms.length > 0 && join_ast join_ast.left = manager.froms.first manager.from join_ast - elsif manager.froms.length == 0 && join_ast - manager.from(join_ast) else manager end -- cgit v1.2.3 From 7d8fd5723600c1290177e665cfd8136031f8abd1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 11:37:55 -0800 Subject: no more manager manipulation if there is no custom join ast --- activerecord/lib/active_record/relation/query_methods.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 145ae6ca97..08b61c9752 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -270,12 +270,11 @@ module ActiveRecord manager = association.join_to(manager) end - if manager.froms.length > 0 && join_ast - join_ast.left = manager.froms.first - manager.from join_ast - else - manager - end + return manager unless join_ast + + join_ast.left = manager.froms.first + manager.from join_ast + manager end def build_select(arel, selects) -- cgit v1.2.3 From 6e15a7fa3c42e183541a893196d83a83cfc20553 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 11:46:02 -0800 Subject: only quote the table name once per call --- .../associations/class_methods/join_dependency.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 1856395f2d..b6d85a7c7d 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -15,7 +15,7 @@ module ActiveRecord @associations = {} @reflections = [] @table_aliases = Hash.new do |h,name| - h[name] = count_aliases_from_table_joins(name) + h[name] = count_aliases_from_table_joins(name.downcase) end @table_aliases[base.table_name] = 1 build(associations) @@ -49,13 +49,16 @@ module ActiveRecord def count_aliases_from_table_joins(name) return 0 if !@table_joins || Arel::Table === @table_joins + # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase + quoted_name = active_record.connection.quote_table_name(name).downcase + @table_joins.grep(Arel::Nodes::Join).map { |join| right = join.right case right when Arel::Table right.name.downcase == name ? 1 : 0 when String - count_aliases_from_string(right.downcase, name) + count_aliases_from_string(right.downcase, quoted_name) else 0 end @@ -63,12 +66,10 @@ module ActiveRecord end def count_aliases_from_string(join_sql, name) - # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase - quoted_name = active_record.connection.quote_table_name(name.downcase).downcase # Table names - join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size + + join_sql.scan(/join(?:\s+\w+)?\s+#{name}\son/).size + # Table aliases - join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size + join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{name}\son/).size end def instantiate(rows) -- cgit v1.2.3 From 73d4d6704ec2a5b11e32d7f421f4614ac83d8384 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 13:20:03 -0800 Subject: avoid *args on join_target_table --- .../class_methods/join_dependency/join_association.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index b0091c613d..5c062e874e 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -115,7 +115,9 @@ module ActiveRecord active_record.send(:sanitize_sql, condition, table_name) end - def join_target_table(relation, *conditions) + def join_target_table(relation, condition) + conditions = [condition] + # If the target table is an STI model then we must be sure to only include records of # its type and its sub-types. unless active_record.descends_from_active_record? @@ -247,9 +249,9 @@ module ActiveRecord join_target_table( relation, target_table["#{reflection.options[:as]}_id"]. - eq(parent_table[parent.primary_key]), + eq(parent_table[parent.primary_key]).and( target_table["#{reflection.options[:as]}_type"]. - eq(parent.active_record.base_class.name) + eq(parent.active_record.base_class.name)) ) end -- cgit v1.2.3 From f528389a5580227f4d173bf4915a98b71befb32e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 13:26:58 -0800 Subject: use alias nodes to represent table aliases --- .../associations/class_methods/join_dependency/join_association.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 5c062e874e..ff6a9993b0 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -142,9 +142,8 @@ module ActiveRecord def join_has_and_belongs_to_many_to(relation) join_table = Arel::Table.new( - options[:join_table], :engine => arel_engine, - :as => @aliased_join_table_name - ) + options[:join_table] + ).alias(@aliased_join_table_name) fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key klass_fk = options[:association_foreign_key] || reflection.klass.to_s.foreign_key -- cgit v1.2.3 From e07772556a2167e44158f367beb3a45e6a55671f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 13:28:50 -0800 Subject: use table alias nodes rather than Table nodes --- .../associations/class_methods/join_dependency/join_association.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index ff6a9993b0..98536d23bc 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -181,9 +181,8 @@ module ActiveRecord def join_has_many_through_to(relation) join_table = Arel::Table.new( - through_reflection.klass.table_name, :engine => arel_engine, - :as => @aliased_join_table_name - ) + through_reflection.klass.table_name + ).alias @aliased_join_table_name jt_conditions = [] jt_foreign_key = first_key = second_key = nil -- cgit v1.2.3 From f0580bd84c33479edce9081dfc53d1fcf6d879a1 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 11 Dec 2010 00:19:14 +0100 Subject: testing guide: revises explanation of the test macro --- railties/guides/source/testing.textile | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index c9109a0ddf..d7088dc04c 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -182,21 +182,27 @@ class PostTest < ActiveSupport::TestCase The +PostTest+ class defines a _test case_ because it inherits from +ActiveSupport::TestCase+. +PostTest+ thus has all the methods available from +ActiveSupport::TestCase+. You'll see those methods a little later in this guide. - -def test_the_truth - - Any method defined within a +Test::Unit+ test case that begins with +test+ (case sensitive) is simply called a test. So, +test_password+, +test_valid_password+ and +testValidPassword+ all are legal test names and are run automatically when the test case is run. -Rails adds a +test+ method that takes a test name and a block. It generates a normal +Test::Unit+ test with method names prefixed with +test_+. +Rails adds a +test+ method that takes a test name and a block. It generates a normal +Test::Unit+ test with method names prefixed with +test_+. So, test "the truth" do - # ... + assert true end -This makes test names more readable by replacing underscores with regular language. +acts as if you had written + + +def test_the_truth + assert true +end + + +only the +test+ macro allows a more readable test name. You can still use regular method definitions though. + +NOTE: The method name is generated by replacing underscores with spaces. The result does not need to be a valid Ruby identifier though, the name may contain punctuation characters etc. That's because in Ruby technically any string may be a method name. Odd ones need +define_method+ and +send+ calls, but formally there's no restriction. assert true @@ -380,7 +386,7 @@ There are a bunch of different types of assertions you can use. Here's the compl |+assert( boolean, [msg] )+ |Ensures that the object/expression is true.| |+assert_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is true.| |+assert_not_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is false.| -|+assert_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is true.| +|+assert_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is true.| |+assert_not_same( obj1, obj2, [msg] )+ |Ensures that +obj1.equal?(obj2)+ is false.| |+assert_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is true.| |+assert_not_nil( obj, [msg] )+ |Ensures that +obj.nil?+ is false.| @@ -388,7 +394,7 @@ There are a bunch of different types of assertions you can use. Here's the compl |+assert_no_match( regexp, string, [msg] )+ |Ensures that a string doesn't matches the regular expression.| |+assert_in_delta( expecting, actual, delta, [msg] )+ |Ensures that the numbers +expecting+ and +actual+ are within +delta+ of each other.| |+assert_throws( symbol, [msg] ) { block }+ |Ensures that the given block throws the symbol.| -|+assert_raise( exception1, exception2, ... ) { block }+ |Ensures that the given block raises one of the given exceptions.| +|+assert_raise( exception1, exception2, ... ) { block }+ |Ensures that the given block raises one of the given exceptions.| |+assert_nothing_raised( exception1, exception2, ... ) { block }+ |Ensures that the given block doesn't raise one of the given exceptions.| |+assert_instance_of( class, obj, [msg] )+ |Ensures that +obj+ is of the +class+ type.| |+assert_kind_of( class, obj, [msg] )+ |Ensures that +obj+ is or descends from +class+.| -- cgit v1.2.3 From c8baefbca369be2c7ed85264809b52f3b40a0770 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 11 Dec 2010 00:22:51 +0100 Subject: spaces with underscores, I mean --- railties/guides/source/testing.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index d7088dc04c..733c8a755e 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -202,7 +202,7 @@ end only the +test+ macro allows a more readable test name. You can still use regular method definitions though. -NOTE: The method name is generated by replacing underscores with spaces. The result does not need to be a valid Ruby identifier though, the name may contain punctuation characters etc. That's because in Ruby technically any string may be a method name. Odd ones need +define_method+ and +send+ calls, but formally there's no restriction. +NOTE: The method name is generated by replacing spaces with underscores. The result does not need to be a valid Ruby identifier though, the name may contain punctuation characters etc. That's because in Ruby technically any string may be a method name. Odd ones need +define_method+ and +send+ calls, but formally there's no restriction. assert true -- cgit v1.2.3 From d38644f4a9eead2f9d9ed96e10fb67430de31789 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 11 Dec 2010 00:52:33 +0100 Subject: reviews commit 53bbbcc --- activerecord/lib/active_record/transactions.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 181280baaa..443f318067 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -131,7 +131,7 @@ module ActiveRecord # # +transaction+ calls can be nested. By default, this makes all database # statements in the nested transaction block become part of the parent - # transaction. For example: + # transaction. For example, the following behavior may be surprising: # # User.transaction do # User.create(:username => 'Kotori') @@ -141,13 +141,15 @@ module ActiveRecord # end # end # - # User.find(:all) # => Return both Kotori and Nemu, because inner transaction do not rollback - # # without :requiers_new => true option, and Rollback exception do not reraise + # creates both "Kotori" and "Nemu". Reason is the ActiveRecord::Rollback + # exception in the nested block does not issue a ROLLBACK. Since these exceptions + # are captured in transaction blocks, the parent block does not see it and the + # real transaction is committed. # - # It is also possible to requires a sub-transaction by passing - # :requires_new => true. If anything goes wrong, the - # database rolls back to the beginning of the sub-transaction - # without rolling back the parent transaction. For example: + # In order to get a ROLLBACK for the nested transaction you may ask for a real + # sub-transaction by passing :requires_new => true. If anything goes wrong, + # the database rolls back to the beginning of the sub-transaction without rolling + # back the parent transaction. If we add it to the previous example: # # User.transaction do # User.create(:username => 'Kotori') @@ -157,12 +159,12 @@ module ActiveRecord # end # end # - # User.find(:all) # => Returns only Kotori + # only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.) # # Most databases don't support true nested transactions. At the time of # writing, the only database that we're aware of that supports true nested # transactions, is MS-SQL. Because of this, Active Record emulates nested - # transactions by using savepoints. See + # transactions by using savepoints on MySQL and PostgreSQL. See # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html # for more information about savepoints. # -- cgit v1.2.3 From b9bb240a2b5abc0b6704b1a1552a31c4f1d96f34 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 14:02:14 -0800 Subject: just mutate the ast, fewer lasgns --- activerecord/lib/active_record/relation/query_methods.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 08b61c9752..dce9ac4808 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -225,14 +225,13 @@ module ActiveRecord test = eqls.inject(eqls.shift) do |memo, expr| memo.or(expr) end - arel = arel.where(test) + arel.where(test) end (wheres - equalities).each do |where| where = Arel.sql(where) if String === where - arel = arel.where(Arel::Nodes::Grouping.new(where)) + arel.where(Arel::Nodes::Grouping.new(where)) end - arel end def build_where(opts, other = []) -- cgit v1.2.3 From 4d5f9a07658f610c34851ceca2a0e25e5ad83503 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 14:44:42 -0800 Subject: remove lasgn since AST is mutated --- .../class_methods/join_dependency/join_association.rb | 10 ++++++++-- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 98536d23bc..4839068f6d 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -135,9 +135,15 @@ module ActiveRecord conditions << process_conditions(options[:conditions], aliased_table_name) end - join = relation.join(target_table, join_type) + ands = relation.create_and(conditions) - join.on(*conditions) + join = relation.create_join( + relation.froms.first, + target_table, + relation.create_on(ands), + join_type) + + relation.from join end def join_has_and_belongs_to_many_to(relation) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index dce9ac4808..51a39be065 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -266,7 +266,7 @@ module ActiveRecord # FIXME: refactor this to build an AST join_dependency.join_associations.each do |association| - manager = association.join_to(manager) + association.join_to(manager) end return manager unless join_ast -- cgit v1.2.3 From 64ba043544008a92a4fe1940e836a7dfa4ca3f11 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 15:13:51 -0800 Subject: explicitly set prefix --- .../associations/class_methods/join_dependency/join_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 4839068f6d..f1b923e98b 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -22,7 +22,7 @@ module ActiveRecord attr_reader :aliased_prefix, :aliased_table_name delegate :options, :through_reflection, :source_reflection, :to => :reflection - delegate :table, :table_name, :to => :parent, :prefix => true + delegate :table, :table_name, :to => :parent, :prefix => :parent def initialize(reflection, join_dependency, parent = nil) reflection.check_validity! -- cgit v1.2.3 From 8924018d5994cdc71df3647897462ff82ce7bf37 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 15:27:10 -0800 Subject: move ivar to initialize, use triple dot rather than minus --- .../associations/class_methods/join_dependency/join_association.rb | 2 +- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index f1b923e98b..c552603097 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -37,6 +37,7 @@ module ActiveRecord @join_dependency = join_dependency @parent = parent @join_type = Arel::InnerJoin + @aliased_prefix = "t#{ join_dependency.join_parts.size }" # This must be done eagerly upon initialisation because the alias which is produced # depends on the state of the join dependency, but we want it to work the same way @@ -97,7 +98,6 @@ module ActiveRecord private def allocate_aliases - @aliased_prefix = "t#{ join_dependency.join_parts.size }" @aliased_table_name = aliased_table_name_for(table_name) if reflection.macro == :has_and_belongs_to_many diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index d4aefada22..5b9c48bafa 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -12,7 +12,7 @@ module ActiveRecord # Truncates a table alias according to the limits of the current adapter. def table_alias_for(table_name) - table_name[0..table_alias_length-1].gsub(/\./, '_') + table_name[0...table_alias_length].gsub(/\./, '_') end # def tables(name = nil) end -- cgit v1.2.3 From 9e16254b4661f0ec55f035f62e70148827dcdf56 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 16:26:43 -0800 Subject: reduce method calls --- activerecord/lib/active_record/reflection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index a07c321960..fe4b518826 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -248,7 +248,7 @@ module ActiveRecord end def has_inverse? - !@options[:inverse_of].nil? + @options[:inverse_of] end def inverse_of -- cgit v1.2.3 From de708447f4bc2ae692440bbac235f9fe04f0702f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 17:01:15 -0800 Subject: combine regexp --- .../lib/active_record/associations/class_methods/join_dependency.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index b6d85a7c7d..6be947df41 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -66,10 +66,8 @@ module ActiveRecord end def count_aliases_from_string(join_sql, name) - # Table names - join_sql.scan(/join(?:\s+\w+)?\s+#{name}\son/).size + - # Table aliases - join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{name}\son/).size + # Table names + table aliases + join_sql.scan(/join(?:\s+\w+)?\s+(\S+\s+)?#{name}\son/).size end def instantiate(rows) -- cgit v1.2.3 From 9094cd2600ee0b4f6523dbb80a5fa9e43c81e0c0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 10 Dec 2010 17:03:32 -0800 Subject: just use the regexp directly --- .../associations/class_methods/join_dependency.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 6be947df41..5a0ff942ca 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -58,18 +58,16 @@ module ActiveRecord when Arel::Table right.name.downcase == name ? 1 : 0 when String - count_aliases_from_string(right.downcase, quoted_name) + # Table names + table aliases + right.downcase.scan( + /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ + ).size else 0 end }.sum end - def count_aliases_from_string(join_sql, name) - # Table names + table aliases - join_sql.scan(/join(?:\s+\w+)?\s+(\S+\s+)?#{name}\son/).size - end - def instantiate(rows) primary_key = join_base.aliased_primary_key parents = {} -- cgit v1.2.3 From c780cf2f024528da80c3b01a49ba9423add2e783 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 11 Dec 2010 01:50:21 -0200 Subject: Add named helper output to translated paths example --- railties/guides/source/routing.textile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index bc38e4a6e5..9dad6d3096 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -745,14 +745,14 @@ end Rails now creates routes to the +CategoriesController+. -|_.HTTP verb|_.Path |_.action | -|GET |/kategorien |index | -|GET |/kategorien/neu |new | -|POST |/kategorien |create | -|GET |/kategorien/1 |show | -|GET |/kategorien/:id/bearbeiten |edit | -|PUT |/kategorien/1 |update | -|DELETE |/kategorien/1 |destroy | +|_.HTTP verb|_.Path |_.action | .named helper | +|GET |/kategorien |index | categories_path | +|GET |/kategorien/neu |new | new_category_path | +|POST |/kategorien |create | categories_path | +|GET |/kategorien/1 |show | category_path | +|GET |/kategorien/:id/bearbeiten |edit | edit_category_path | +|PUT |/kategorien/1 |update | category_path | +|DELETE |/kategorien/1 |destroy | category_path | h4. Overriding the Singular Form -- cgit v1.2.3 From e4c252c119acb0e62a4832f904cdff3306094ee7 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 11 Dec 2010 01:54:59 -0200 Subject: Add underline to header --- railties/guides/source/routing.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 9dad6d3096..3a3d1e050c 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -745,7 +745,7 @@ end Rails now creates routes to the +CategoriesController+. -|_.HTTP verb|_.Path |_.action | .named helper | +|_.HTTP verb|_.Path |_.action |_.named helper | |GET |/kategorien |index | categories_path | |GET |/kategorien/neu |new | new_category_path | |POST |/kategorien |create | categories_path | -- cgit v1.2.3 From 93af9ecf35063a372604c4d0fd168c3fd425e396 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 11 Dec 2010 02:02:09 -0200 Subject: Add named helper to photo controller example --- railties/guides/source/routing.textile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 3a3d1e050c..d3f8e435ec 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -611,14 +611,14 @@ resources :photos, :controller => "images" will recognize incoming paths beginning with +/photos+ but route to the +Images+ controller: -|_. Verb |_.Path |_.action | -|GET |/photos |index | -|GET |/photos/new |new | -|POST |/photos |create | -|GET |/photos/1 |show | -|GET |/photos/1/edit |edit | -|PUT |/photos/1 |update | -|DELETE |/photos/1 |destroy | +|_. Verb |_.Path |_.action |_.named helper | +|GET |/photos |index | photos_path | +|GET |/photos/new |new | new_photo_path | +|POST |/photos |create | photos_path | +|GET |/photos/1 |show | photo_path | +|GET |/photos/1/edit |edit | edit_photo_path | +|PUT |/photos/1 |update | photo_path | +|DELETE |/photos/1 |destroy | photo_path | NOTE: Use +photos_path+, +new_photos_path+, etc. to generate paths for this resource. -- cgit v1.2.3 From f05238e1d2fe454e36ac9b90222395ae8c8cf48a Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 11 Dec 2010 02:24:07 -0200 Subject: Tables style unification --- railties/guides/source/routing.textile | 126 ++++++++++++++++----------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index d3f8e435ec..7af9779ac7 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -76,14 +76,14 @@ resources :photos creates seven different routes in your application, all mapping to the +Photos+ controller: -|_. Verb |_.Path |_.action |_.used for| -|GET |/photos |index |display a list of all photos| -|GET |/photos/new |new |return an HTML form for creating a new photo| -|POST |/photos |create |create a new photo| -|GET |/photos/:id |show |display a specific photo| -|GET |/photos/:id/edit |edit |return an HTML form for editing a photo| -|PUT |/photos/:id |update |update a specific photo| -|DELETE |/photos/:id |destroy |delete a specific photo| +|_. HTTP Verb |_.Path |_.action |_.used for | +|GET |/photos |index |display a list of all photos | +|GET |/photos/new |new |return an HTML form for creating a new photo | +|POST |/photos |create |create a new photo | +|GET |/photos/:id |show |display a specific photo | +|GET |/photos/:id/edit |edit |return an HTML form for editing a photo | +|PUT |/photos/:id |update |update a specific photo | +|DELETE |/photos/:id |destroy |delete a specific photo | h4. Paths and URLs @@ -130,13 +130,13 @@ resource :geocoder creates six different routes in your application, all mapping to the +Geocoders+ controller: -|_. Verb |_.Path |_.action |_.used for| -|GET |/geocoder/new |new |return an HTML form for creating the geocoder| -|POST |/geocoder |create |create the new geocoder| -|GET |/geocoder |show |display the one and only geocoder resource| -|GET |/geocoder/edit |edit |return an HTML form for editing the geocoder| -|PUT |/geocoder |update |update the one and only geocoder resource| -|DELETE |/geocoder |destroy |delete the geocoder resource| +|_.HTTP Verb |_.Path |_.action |_.used for | +|GET |/geocoder/new |new |return an HTML form for creating the geocoder | +|POST |/geocoder |create |create the new geocoder | +|GET |/geocoder |show |display the one and only geocoder resource | +|GET |/geocoder/edit |edit |return an HTML form for editing the geocoder | +|PUT |/geocoder |update |update the one and only geocoder resource | +|DELETE |/geocoder |destroy |delete the geocoder resource | NOTE: Because you might want to use the same controller for a singular route (+/account+) and a plural route (+/accounts/45+), singular resources map to plural controllers. @@ -160,14 +160,14 @@ end This will create a number of routes for each of the +posts+ and +comments+ controller. For +Admin::PostsController+, Rails will create: -|_. Verb |_.Path |_.action |_. helper | -|GET |/admin/posts |index | admin_posts_path | -|GET |/admin/posts/new |new | new_admin_posts_path | -|POST |/admin/posts |create | admin_posts_path | -|GET |/admin/posts/1 |show | admin_post_path(id) | -|GET |/admin/posts/1/edit |edit | edit_admin_post_path(id) | -|PUT |/admin/posts/1 |update | admin_post_path(id) | -|DELETE |/admin/posts/1 |destroy | admin_post_path(id) | +|_.HTTP Verb |_.Path |_.action |_.named helper | +|GET |/admin/posts |index | admin_posts_path | +|GET |/admin/posts/new |new | new_admin_posts_path | +|POST |/admin/posts |create | admin_posts_path | +|GET |/admin/posts/1 |show | admin_post_path(id) | +|GET |/admin/posts/1/edit |edit | edit_admin_post_path(id) | +|PUT |/admin/posts/1 |update | admin_post_path(id) | +|DELETE |/admin/posts/1 |destroy | admin_post_path(id) | If you want to route +/posts+ (without the prefix +/admin+) to +Admin::PostsController+, you could use @@ -199,14 +199,14 @@ resources :posts, :path => "/admin/posts" In each of these cases, the named routes remain the same as if you did not use +scope+. In the last case, the following paths map to +PostsController+: -|_. Verb |_.Path |_.action |_. helper | -|GET |/admin/posts |index | posts_path | -|GET |/admin/posts/new |new | posts_path | -|POST |/admin/posts |create | posts_path | -|GET |/admin/posts/1 |show | post_path(id) | -|GET |/admin/posts/1/edit |edit | edit_post_path(id) | -|PUT |/admin/posts/1 |update | post_path(id) | -|DELETE |/admin/posts/1 |destroy | post_path(id) | +|_.HTTP Verb |_.Path |_.action |_.named helper | +|GET |/admin/posts |index | posts_path | +|GET |/admin/posts/new |new | posts_path | +|POST |/admin/posts |create | posts_path | +|GET |/admin/posts/1 |show | post_path(id) | +|GET |/admin/posts/1/edit |edit | edit_post_path(id) | +|PUT |/admin/posts/1 |update | post_path(id) | +|DELETE |/admin/posts/1 |destroy | post_path(id) | h4. Nested Resources @@ -232,14 +232,14 @@ end In addition to the routes for magazines, this declaration will also route ads to an +AdsController+. The ad URLs require a magazine: -|_.Verb |_.Path |_.action |_.used for| -|GET |/magazines/1/ads |index |display a list of all ads for a specific magazine| -|GET |/magazines/1/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine| -|POST |/magazines/1/ads |create |create a new ad belonging to a specific magazine| -|GET |/magazines/1/ads/1 |show |display a specific ad belonging to a specific magazine| -|GET |/magazines/1/ads/1/edit |edit |return an HTML form for editing an ad belonging to a specific magazine| -|PUT |/magazines/1/ads/1 |update |update a specific ad belonging to a specific magazine| -|DELETE |/magazines/1/ads/1 |destroy |delete a specific ad belonging to a specific magazine| +|_.HTTP Verb |_.Path |_.action |_.used for | +|GET |/magazines/1/ads |index |display a list of all ads for a specific magazine | +|GET |/magazines/1/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine | +|POST |/magazines/1/ads |create |create a new ad belonging to a specific magazine | +|GET |/magazines/1/ads/1 |show |display a specific ad belonging to a specific magazine | +|GET |/magazines/1/ads/1/edit |edit |return an HTML form for editing an ad belonging to a specific magazine | +|PUT |/magazines/1/ads/1 |update |update a specific ad belonging to a specific magazine | +|DELETE |/magazines/1/ads/1 |destroy |delete a specific ad belonging to a specific magazine | This will also create routing helpers such as +magazine_ads_url+ and +edit_magazine_ad_path+. These helpers take an instance of Magazine as the first parameter (+magazine_ads_url(@magazine)+). @@ -611,14 +611,14 @@ resources :photos, :controller => "images" will recognize incoming paths beginning with +/photos+ but route to the +Images+ controller: -|_. Verb |_.Path |_.action |_.named helper | -|GET |/photos |index | photos_path | -|GET |/photos/new |new | new_photo_path | -|POST |/photos |create | photos_path | -|GET |/photos/1 |show | photo_path | -|GET |/photos/1/edit |edit | edit_photo_path | -|PUT |/photos/1 |update | photo_path | -|DELETE |/photos/1 |destroy | photo_path | +|_.HTTP Verb |_.Path |_.action |_.named helper | +|GET |/photos |index | photos_path | +|GET |/photos/new |new | new_photo_path | +|POST |/photos |create | photos_path | +|GET |/photos/1 |show | photo_path(id) | +|GET |/photos/1/edit |edit | edit_photo_path(id) | +|PUT |/photos/1 |update | photo_path(id) | +|DELETE |/photos/1 |destroy | photo_path(id) | NOTE: Use +photos_path+, +new_photos_path+, etc. to generate paths for this resource. @@ -653,14 +653,14 @@ resources :photos, :as => "images" will recognize incoming paths beginning with +/photos+ and route the requests to +PhotosController+, but use the value of the :as option to name the helpers. -|_.HTTP verb|_.Path |_.action |_.named helper | -|GET |/photos |index | images_path | -|GET |/photos/new |new | new_image_path | -|POST |/photos |create | images_path | -|GET |/photos/1 |show | image_path | -|GET |/photos/1/edit |edit | edit_image_path | -|PUT |/photos/1 |update | image_path | -|DELETE |/photos/1 |destroy | image_path | +|_.HTTP verb|_.Path |_.action |_.named helper | +|GET |/photos |index | images_path | +|GET |/photos/new |new | new_image_path | +|POST |/photos |create | images_path | +|GET |/photos/1 |show | image_path(id) | +|GET |/photos/1/edit |edit | edit_image_path(id) | +|PUT |/photos/1 |update | image_path(id) | +|DELETE |/photos/1 |destroy | image_path(id) | h4. Overriding the +new+ and +edit+ Segments @@ -745,14 +745,14 @@ end Rails now creates routes to the +CategoriesController+. -|_.HTTP verb|_.Path |_.action |_.named helper | -|GET |/kategorien |index | categories_path | -|GET |/kategorien/neu |new | new_category_path | -|POST |/kategorien |create | categories_path | -|GET |/kategorien/1 |show | category_path | -|GET |/kategorien/:id/bearbeiten |edit | edit_category_path | -|PUT |/kategorien/1 |update | category_path | -|DELETE |/kategorien/1 |destroy | category_path | +|_.HTTP verb|_.Path |_.action |_.named helper | +|GET |/kategorien |index | categories_path | +|GET |/kategorien/neu |new | new_category_path | +|POST |/kategorien |create | categories_path | +|GET |/kategorien/1 |show | category_path(id) | +|GET |/kategorien/1/bearbeiten |edit | edit_category_path(id) | +|PUT |/kategorien/1 |update | category_path(id) | +|DELETE |/kategorien/1 |destroy | category_path(id) | h4. Overriding the Singular Form -- cgit v1.2.3 From 751733ab963cce8780a71185344d8b31ba93c91d Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Sat, 11 Dec 2010 17:25:28 +1000 Subject: Provide an example for ActionDispatch::Routing::Mapper#scope --- actionpack/lib/action_dispatch/routing/mapper.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 430fcdbe07..45248cf34c 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -458,6 +458,18 @@ module ActionDispatch super end + # Used to scope a set of routes to particular constraints. + # + # Take the following route definition as an example: + # + # scope :path => ":account_id", :as => "account" do + # resources :projects + # end + # + # This generates helpers such as +account_projects_path+, just like +resources+ does. + # The difference here being that the routes generated are like /rails/projects/2, + # rather than /accounts/rails/projects/2. + # # === Supported options # [:module] # If you want to route /posts (without the prefix /admin) to -- cgit v1.2.3 From 7891de893951c780a1732747d430c33e998dd573 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Fri, 10 Dec 2010 14:42:10 +0100 Subject: Allow to generate namespaced generators [#6140 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../rails/generator/generator_generator.rb | 4 +-- .../test/generators/generator_generator_test.rb | 39 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb index 5b5d1884bc..3e0a442bda 100644 --- a/railties/lib/rails/generators/rails/generator/generator_generator.rb +++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb @@ -14,9 +14,9 @@ module Rails def generator_dir if options[:namespace] - File.join("lib", "generators", file_name) + File.join("lib", "generators", regular_class_path, file_name) else - File.join("lib", "generators") + File.join("lib", "generators", regular_class_path) end end diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb index 26f975a191..f4c975fc18 100644 --- a/railties/test/generators/generator_generator_test.rb +++ b/railties/test/generators/generator_generator_test.rb @@ -17,4 +17,43 @@ class GeneratorGeneratorTest < Rails::Generators::TestCase assert_file "lib/generators/awesome/awesome_generator.rb", /class AwesomeGenerator < Rails::Generators::NamedBase/ end + + def test_namespaced_generator_skeleton + run_generator ["rails/awesome"] + + %w( + lib/generators/rails/awesome + lib/generators/rails/awesome/USAGE + lib/generators/rails/awesome/templates + ).each{ |path| assert_file path } + + assert_file "lib/generators/rails/awesome/awesome_generator.rb", + /class Rails::AwesomeGenerator < Rails::Generators::NamedBase/ + end + + def test_generator_skeleton_is_created_without_file_name_namespace + run_generator ["awesome", "--namespace", "false"] + + %w( + lib/generators/ + lib/generators/USAGE + lib/generators/templates + ).each{ |path| assert_file path } + + assert_file "lib/generators/awesome_generator.rb", + /class AwesomeGenerator < Rails::Generators::NamedBase/ + end + + def test_namespaced_generator_skeleton_without_file_name_namespace + run_generator ["rails/awesome", "--namespace", "false"] + + %w( + lib/generators/rails + lib/generators/rails/USAGE + lib/generators/rails/templates + ).each{ |path| assert_file path } + + assert_file "lib/generators/rails/awesome_generator.rb", + /class Rails::AwesomeGenerator < Rails::Generators::NamedBase/ + end end -- cgit v1.2.3 From 307443972c5f6de959a5401eec76ca327484b10c Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Fri, 10 Dec 2010 22:16:34 +0100 Subject: Skip creating migration if --skip option is passed to model generator [#6144 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/generators/migration.rb | 2 +- railties/test/generators/model_generator_test.rb | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb index 8d98909055..0c5c4f6e09 100644 --- a/railties/lib/rails/generators/migration.rb +++ b/railties/lib/rails/generators/migration.rb @@ -52,7 +52,7 @@ module Rails destination = self.class.migration_exists?(migration_dir, @migration_file_name) - if behavior == :invoke + if !(destination && options[:skip]) && behavior == :invoke if destination && options.force? remove_file(destination) elsif destination diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 8a0f560bc8..552b7eb30a 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -147,10 +147,22 @@ class ModelGeneratorTest < Rails::Generators::TestCase end end - def test_migration_already_exists_error_message + def test_migration_is_skipped_with_skip_option run_generator - error = capture(:stderr){ run_generator ["Account"], :behavior => :skip } - assert_match /Another migration is already named create_accounts/, error + output = run_generator ["Account", "--skip"] + assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output + end + + def test_migration_is_ignored_as_identical_with_skip_option + run_generator ["Account"] + output = run_generator ["Account", "--skip"] + assert_match %r{identical\s+db/migrate/\d+_create_accounts.rb}, output + end + + def test_migration_is_skipped_on_skip_behavior + run_generator + output = run_generator ["Account"], :behavior => :skip + assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output end def test_migration_error_is_not_shown_on_revoke -- cgit v1.2.3 From 14b5c8b98515d9cc13fbdefb02800ad42f1070f5 Mon Sep 17 00:00:00 2001 From: Paul Odeon Date: Sat, 11 Dec 2010 14:30:12 +0000 Subject: Updated generator guide for rails commit 7891de893951c780a1732747d430c33e998dd573 --- railties/guides/source/generators.textile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile index ee3891c43b..6945f6f9bb 100644 --- a/railties/guides/source/generators.textile +++ b/railties/guides/source/generators.textile @@ -208,16 +208,16 @@ end If we generate another resource with the scaffold generator, we can see that neither stylesheets nor fixtures are created anymore. If you want to customize it further, for example to use DataMapper and RSpec instead of Active Record and TestUnit, it's just a matter of adding their gems to your application and configuring your generators. -To demonstrate this, we are going to create a new helper generator that simply adds some instance variable readers. First, we create a generator: +To demonstrate this, we are going to create a new helper generator that simply adds some instance variable readers. First, we create a generator within the rails namespace, as this is where rails searches for generators used as hooks: -$ rails generate generator my_helper +$ rails generate generator rails/my_helper After that, we can delete both the +templates+ directory and the +source_root+ class method from our new generators, because we are not going to need them. So our new generator looks like the following: -class MyHelperGenerator < Rails::Generators::NamedBase +class Rails::MyHelperGenerator < Rails::Generators::NamedBase def create_helper_file create_file "app/helpers/#{file_name}_helper.rb", <<-FILE module #{class_name}Helper @@ -270,7 +270,7 @@ Since Rails 3.0, this is easy to do due to the hooks concept. Our new helper doe To do that, we can change the generator this way: -class MyHelperGenerator < Rails::Generators::NamedBase +class Rails::MyHelperGenerator < Rails::Generators::NamedBase def create_helper_file create_file "app/helpers/#{file_name}_helper.rb", <<-FILE module #{class_name}Helper @@ -283,7 +283,7 @@ end end -Now, when the helper generator is invoked and TestUnit is configured as the test framework, it will try to invoke both +MyHelper::Generators::TestUnitGenerator+ and +TestUnit::Generators::MyHelperGenerator+. Since none of those are defined, we can tell our generator to invoke +TestUnit::Generators::HelperGenerator+ instead, which is defined since it's a Rails generator. To do that, we just need to add: +Now, when the helper generator is invoked and TestUnit is configured as the test framework, it will try to invoke both +Rails::TestUnitGenerator+ and +TestUnit::MyHelperGenerator+. Since none of those are defined, we can tell our generator to invoke +TestUnit::Generators::HelperGenerator+ instead, which is defined since it's a Rails generator. To do that, we just need to add: # Search for :helper instead of :my_helper -- cgit v1.2.3 From 3efe1b738686482730e05ad50e8d0e667686d2e8 Mon Sep 17 00:00:00 2001 From: vijay Date: Sat, 11 Dec 2010 23:06:54 +0530 Subject: fixed typos and rephrased a few sentences; also removed reference to status codes being located in action_controller/status_codes.rb --- .../guides/source/layouts_and_rendering.textile | 24 +++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile index 80a1fdd38d..4cdd4f2709 100644 --- a/railties/guides/source/layouts_and_rendering.textile +++ b/railties/guides/source/layouts_and_rendering.textile @@ -106,7 +106,7 @@ Perhaps the simplest thing you can do with +render+ is to render nothing at all: render :nothing => true -If you look at the response for this using Curl you will see the following: +If you look at the response for this using cURL, you will see the following: $ curl -i 127.0.0.1:3000/books @@ -123,7 +123,7 @@ Cache-Control: no-cache $ -We see there is an empty response (no data after the +Cache-Control+ line), but the request was successful because Rails has set the response to 200 OK. You can set the +:status+ options on render to change this response. Rendering nothing can be useful for AJAX requests where all you want to send back to the browser is an acknowledgement that the request was completed. +We see there is an empty response (no data after the +Cache-Control+ line), but the request was successful because Rails has set the response to 200 OK. You can set the +:status+ option on render to change this response. Rendering nothing can be useful for AJAX requests where all you want to send back to the browser is an acknowledgement that the request was completed. TIP: You should probably be using the +head+ method, discussed later in this guide, instead of +render :nothing+. This provides additional flexibility and makes it explicit that you're only generating HTTP headers. @@ -346,7 +346,7 @@ render :status => 500 render :status => :forbidden -Rails understands either numeric status codes or symbols for status codes. You can find its list of status codes in +actionpack/lib/action_controller/status_codes.rb+. You can also see there how Rails maps symbols to status codes. +Rails understands both numeric status codes and symbols for status codes. h6. The +:location+ Option @@ -604,7 +604,7 @@ Which would detect that there are no books, populate the +@books+ instance varia h4. Using +head+ To Build Header-Only Responses -The +head+ method exists to let you send back responses to the browser that have only headers. It provides a more obvious alternative to calling +render :nothing+. The +head+ method takes one response, which is interpreted as a hash of header names and values. For example, you can return only an error header: +The +head+ method can be used to send responses with only headers to the browser. It provides a more obvious alternative to calling +render :nothing+. The +head+ method takes one parameter, which is interpreted as a hash of header names and values. For example, you can return only an error header: head :bad_request @@ -651,11 +651,9 @@ When Rails renders a view as a response, it does so by combining the view with t * +yield+ and +content_for+ * Partials -I'll discuss each of these in turn. - h4. Asset Tags -Asset tags provide methods for generating HTML that links views to assets like images, videos, audio, JavaScript, stylesheets, and feeds. There are six types of include tag: +Asset tags provide methods for generating HTML that links views to feeds, JavaScript, stylesheets, images, videos and audios. These are the six asset tags available in Rails: * +auto_discovery_link_tag+ * +javascript_include_tag+ @@ -680,7 +678,7 @@ The +auto_discovery_link_tag+ helper builds HTML that most browsers and newsread There are three tag options available for +auto_discovery_link_tag+: * +:rel+ specifies the +rel+ value in the link (defaults to "alternate") -* +:type+ specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically +* +:type+ specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically. * +:title+ specifies the title of the link h5. Linking to Javascript Files with +javascript_include_tag+ @@ -829,7 +827,7 @@ You can also supply an alternate image to show on mouseover: <%= image_tag "home.gif", :onmouseover => "menu/home_highlight.gif" %> -Or alternate text if the user has rendering images turned off in their browser, if you do not specify an explicit alt tag, it defaults to the file name of the file, capitalized and with no extension, for example, these two image tags would return the same code: +You can supply alternate text for the image which will be used if the user has images turned off in their browser. If you do not specify an alt text explicitly, it defaults to the file name of the file, capitalized and with no extension. For example, these two image tags would return the same code: <%= image_tag "home.gif" %> @@ -939,7 +937,7 @@ The main body of the view will always render into the unnamed +yield+. To render h4. Using +content_for+ -The +content_for+ method allows you to insert content into a +yield+ block in your layout. You only use +content_for+ to insert content in named yields. For example, this view would work with the layout that you just saw: +The +content_for+ method allows you to insert content into a named +yield+ block in your layout. For example, this view would work with the layout that you just saw: <% content_for :head do %> @@ -966,7 +964,7 @@ The +content_for+ method is very helpful when your layout contains distinct regi h4. Using Partials -Partial templates - usually just called "partials" - are another device for breaking apart the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file. +Partial templates - usually just called "partials" - are another device for breaking the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file. h5. Naming Partials @@ -1086,15 +1084,13 @@ Partials are very useful in rendering collections. When you pass a collection to When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is +_product+, and within the +_product+ partial, you can refer to +product+ to get the instance that is being rendered. -In Rails 3.0, there is also a shorthand for this. Assuming +@products+ is a collection of +product+ instances, you can simply write this in the +index.html.erb+: +In Rails 3.0, there is also a shorthand for this. Assuming +@products+ is a collection of +product+ instances, you can simply write this in the +index.html.erb+ to produce the same result:

Products

<%= render @products %>
-To produce the same result. - Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection: * +index.html.erb+ -- cgit v1.2.3 From 2650742bd02e108bc4ccdc59efa54b4916e3a443 Mon Sep 17 00:00:00 2001 From: Samuel Kadolph Date: Thu, 9 Dec 2010 13:30:02 -0500 Subject: Add support for namespaced validators Includes test and documentation for new feature Signed-off-by: Santiago Pastorino --- activemodel/lib/active_model/validations/validates.rb | 8 +++++++- activemodel/test/cases/validations/validates_test.rb | 8 ++++++++ activemodel/test/validators/namespace/email_validator.rb | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100755 activemodel/test/validators/namespace/email_validator.rb diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index 77c5073c6e..0132f68282 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -55,6 +55,10 @@ module ActiveModel # validates :name, :title => true # end # + # Additionally validator classes may be in another namespace and still used within any class. + # + # validates :name, :'file/title' => true + # # The validators hash can also handle regular expressions, ranges, # arrays and strings in shortcut form, e.g. # @@ -86,8 +90,10 @@ module ActiveModel defaults.merge!(:attributes => attributes) validations.each do |key, options| + key = "#{key.to_s.camelize}Validator" + begin - validator = const_get("#{key.to_s.camelize}Validator") + validator = key.include?('::') ? key.constantize : const_get(key) rescue NameError raise ArgumentError, "Unknown validator: '#{key}'" end diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 666c48c8a0..3a9900939e 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -3,6 +3,7 @@ require 'cases/helper' require 'models/person' require 'models/person_with_validator' require 'validators/email_validator' +require 'validators/namespace/email_validator' class ValidatesTest < ActiveModel::TestCase setup :reset_callbacks @@ -34,6 +35,13 @@ class ValidatesTest < ActiveModel::TestCase assert_equal ['is not an email'], person.errors[:karma] end + def test_validates_with_namespaced_validator_class + Person.validates :karma, :'namespace/email' => true + person = Person.new + person.valid? + assert_equal ['is not an email'], person.errors[:karma] + end + def test_validates_with_if_as_local_conditions Person.validates :karma, :presence => true, :email => { :unless => :condition_is_true } person = Person.new diff --git a/activemodel/test/validators/namespace/email_validator.rb b/activemodel/test/validators/namespace/email_validator.rb new file mode 100755 index 0000000000..57e2793ce2 --- /dev/null +++ b/activemodel/test/validators/namespace/email_validator.rb @@ -0,0 +1,6 @@ +require 'validators/email_validator' + +module Namespace + class EmailValidator < ::EmailValidator + end +end -- cgit v1.2.3 From 32a2bf8d4e170ce68f895c007da852211892ba5e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 11 Dec 2010 16:40:05 -0200 Subject: This is not an executable file --- activemodel/test/validators/namespace/email_validator.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 activemodel/test/validators/namespace/email_validator.rb diff --git a/activemodel/test/validators/namespace/email_validator.rb b/activemodel/test/validators/namespace/email_validator.rb old mode 100755 new mode 100644 -- cgit v1.2.3 From a6faeffe59ce4160ddd10fd8ec8dca8d88c3a35d Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Sun, 12 Dec 2010 21:10:03 +0530 Subject: fixed typos in mailer --- railties/guides/source/action_mailer_basics.textile | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile index b75c528a33..b77a0be37b 100644 --- a/railties/guides/source/action_mailer_basics.textile +++ b/railties/guides/source/action_mailer_basics.textile @@ -144,7 +144,7 @@ This provides a much simpler implementation that does not require the registerin The method +welcome_email+ returns a Mail::Message object which can then just be told +deliver+ to send itself out. -NOTE: In previous versions of Rails, you would call +deliver_welcome_email+ or +create_welcome_email+ however in Rails 3.0 this has been deprecated in favour of just calling the method name itself. +NOTE: In previous versions of Rails, you would call +deliver_welcome_email+ or +create_welcome_email+. This has been deprecated in Rails 3.0 in favour of just calling the method name itself. WARNING: Sending out one email should only take a fraction of a second, if you are planning on sending out many emails, or you have a slow domain resolution service, you might want to investigate using a background process like delayed job. @@ -154,7 +154,7 @@ Action Mailer now handles the auto encoding of multibyte characters inside of he If you are using UTF-8 as your character set, you do not have to do anything special, just go ahead and send in UTF-8 data to the address fields, subject, keywords, filenames or body of the email and ActionMailer will auto encode it into quoted printable for you in the case of a header field or Base64 encode any body parts that are non US-ASCII. -For more complex examples, such as defining alternate character sets or self encoding text first, please refer to the Mail library. +For more complex examples such as defining alternate character sets or self encoding text first, please refer to the Mail library. h4. Complete List of Action Mailer Methods @@ -213,9 +213,7 @@ NOTE: If you specify an encoding, Mail will assume that your content is already h5. Making Inline Attachments -Inline attachments are now possible in ActionMailer. While previously in the pre 3.0 version of Rails, you could do inline attachments, it involved a lot of hacking and determination to pull it off. - -ActionMailer now makes inline attachments as trivial as they should be. +ActionMailer 3.0 makes inline attachments, which involved a lot of hacking in pre 3.0 versions, much simpler and trivial as they should be. * Firstly, to tell Mail to turn an attachment into an inline attachment, you just call #inline on the attachments method within your Mailer: @@ -274,7 +272,7 @@ to format the email address in the format "Name <email>". h4. Mailer Views -Mailer views are located in the +app/views/name_of_mailer_class+ directory. The specific mailer view is known to the class because it's name is the same as the mailer method. So for example, in our example from above, our mailer view for the +welcome_email+ method will be in +app/views/user_mailer/welcome_email.html.erb+ for the HTML version and +welcome_email.text.erb+ for the plain text version. +Mailer views are located in the +app/views/name_of_mailer_class+ directory. The specific mailer view is known to the class because its name is the same as the mailer method. In our example from above, our mailer view for the +welcome_email+ method will be in +app/views/user_mailer/welcome_email.html.erb+ for the HTML version and +welcome_email.text.erb+ for the plain text version. To change the default mailer view for your action you do something like: @@ -441,7 +439,7 @@ h3. Action Mailer Configuration The following configuration options are best made in one of the environment files (environment.rb, production.rb, etc...) |template_root|Determines the base from which template references will be made.| -|logger|The logger is used for generating information on the mailing run if available. Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.| +|logger|Generates information on the mailing run if available. Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.| |smtp_settings|Allows detailed configuration for :smtp delivery method:
  • :address - Allows you to use a remote mail server. Just change it from its default "localhost" setting.
  • :port - On the off chance that your mail server doesn't run on port 25, you can change it.
  • :domain - If you need to specify a HELO domain, you can do it here.
  • :user_name - If your mail server requires authentication, set the username in this setting.
  • :password - If your mail server requires authentication, set the password in this setting.
  • :authentication - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of :plain, :login, :cram_md5.
| |sendmail_settings|Allows you to override options for the :sendmail delivery method.
  • :location - The location of the sendmail executable. Defaults to /usr/sbin/sendmail.
  • :arguments - The command line arguments to be passed to sendmail. Defaults to -i -t.
| |raise_delivery_errors|Whether or not errors should be raised if the email fails to be delivered.| @@ -504,7 +502,7 @@ class UserMailerTest < ActionMailer::TestCase end
-In the test we send the email and store the returned object in the +email+ variable. We then ensure that it was sent (the first assert), then, in the second batch of assertions, we ensure that the email does indeed contain the what we expect. +In the test we send the email and store the returned object in the +email+ variable. We then ensure that it was sent (the first assert), then, in the second batch of assertions, we ensure that the email does indeed contain what we expect. h3. Changelog -- cgit v1.2.3 From a6bc1f9be48095e2126a3726b5016fb760a8fdb4 Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 13 Dec 2010 07:02:37 +1000 Subject: Remove nodoc from FormBuilder because the methods inside are public API methods --- actionpack/lib/action_view/helpers/form_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index ef5bbd8ae3..6f0e2c99ba 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1115,7 +1115,7 @@ module ActionView include InstanceTagMethods end - class FormBuilder #:nodoc: + class FormBuilder # The methods which wrap a form helper call. class_attribute :field_helpers self.field_helpers = (FormHelper.instance_method_names - ['form_for']) -- cgit v1.2.3 From a81f68e879198cb4e01f3cda2671c93f1ef0bc1a Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Mon, 13 Dec 2010 13:03:41 +1000 Subject: Fix indentation on the namespace method's documentation --- actionpack/lib/action_dispatch/routing/mapper.rb | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 45248cf34c..f65a294eca 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -570,13 +570,13 @@ module ActionDispatch # # This generates the following routes: # - # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"} - # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"} - # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"} - # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"} - # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"} - # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"} - # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"} + # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"} + # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"} + # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"} + # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"} + # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"} + # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"} + # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"} # === Supported options # # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ options all default to the name of the namespace. @@ -584,24 +584,24 @@ module ActionDispatch # [:path] # The path prefix for the routes. # - # namespace :admin, :path => "sekret" do - # resources :posts - # end + # namespace :admin, :path => "sekret" do + # resources :posts + # end # # All routes for the above +resources+ will be accessible through +/sekret/posts+, rather than +/admin/posts+ # # [:module] # The namespace for the controllers. # - # namespace :admin, :module => "sekret" do - # resources :posts - # end + # namespace :admin, :module => "sekret" do + # resources :posts + # end # # The +PostsController+ here should go in the +Sekret+ namespace and so it should be defined like this: # - # class Sekret::PostsController < ApplicationController - # # code go here - # end + # class Sekret::PostsController < ApplicationController + # # code go here + # end # # [:as] # Changes the name used in routing helpers for this namespace. -- cgit v1.2.3 From 0b5671ccca4a9f55379c913a211d669e241ad20c Mon Sep 17 00:00:00 2001 From: Umar Sheikh Date: Mon, 13 Dec 2010 11:18:49 +0500 Subject: for the root route to work, we need to delete the public/index.html.erb file as well --- railties/guides/source/routing.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index bc38e4a6e5..74dee60c32 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -595,7 +595,7 @@ You can specify what Rails should route +"/"+ to with the +root+ method: root :to => 'pages#main'
-You should put the +root+ route at the end of the file. +You should put the +root+ route at the end of the file. You also need to delete the public/index.html.erb file for the root route to take effect. h3. Customizing Resourceful Routes -- cgit v1.2.3 From 73dafba6ca484e1772fea8cd25ad27ff65c6c66b Mon Sep 17 00:00:00 2001 From: Ben Mills Date: Sun, 12 Dec 2010 23:20:06 -0700 Subject: Added :placeholder option to ActionView::Helpers::FormTagHelper text_field_tag --- actionpack/lib/action_view/helpers/form_tag_helper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 50f065f03d..c41271635d 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -112,6 +112,7 @@ module ActionView # * :disabled - If set to true, the user will not be able to use this input. # * :size - The number of visible characters that will fit in the input. # * :maxlength - The maximum number of characters that the browser will allow the user to enter. + # * :placeholder - The text contained in the field by default which is removed when the field receives focus. # * Any other key creates standard HTML attributes for the tag. # # ==== Examples @@ -121,6 +122,9 @@ module ActionView # text_field_tag 'query', 'Enter your search query here' # # => # + # text_field_tag 'search', nil, :placeholder => 'Enter search term...' + # # => + # # text_field_tag 'request', nil, :class => 'special_input' # # => # -- cgit v1.2.3 From 658bb4fa25db0b3f61bfb64028274f2365cad506 Mon Sep 17 00:00:00 2001 From: Chiel Wester Date: Mon, 13 Dec 2010 15:06:23 +0100 Subject: Only call save on belongs_to associations if the record has changed or any nested associations have changed (resolves #3353) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/autosave_association.rb | 2 +- activerecord/test/cases/autosave_association_test.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index c3dda29d03..4a18719551 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -363,7 +363,7 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy elsif autosave != false - saved = association.save(:validate => !autosave) if association.new_record? || autosave + saved = association.save(:validate => !autosave) if association.new_record? || (autosave && association.changed_for_autosave?) if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index fbf7121468..27aee400f9 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -667,10 +667,21 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end end + @ship.pirate.catchphrase = "Changed Catchphrase" + assert_raise(RuntimeError) { assert !@ship.save } assert_not_nil @ship.reload.pirate end + def test_should_save_changed_child_objects_if_parent_is_saved + @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?") + @parrot = @pirate.parrots.create!(:name => 'Posideons Killer') + @parrot.name = "NewName" + @ship.save + + assert_equal 'NewName', @parrot.reload.name + end + # has_many & has_and_belongs_to %w{ parrots birds }.each do |association_name| define_method("test_should_destroy_#{association_name}_as_part_of_the_save_transaction_if_they_were_marked_for_destroyal") do -- cgit v1.2.3 From 6ed6e4f9df7c08cbf053501346beb4b0fb42b348 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 13 Dec 2010 11:39:31 -0800 Subject: persisted? should be able to return a truthy object --- activerecord/test/cases/transactions_test.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index b0ccd71836..110a18772f 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -370,23 +370,23 @@ class TransactionTest < ActiveRecord::TestCase assert topic_2.save @first.save @second.destroy - assert_equal true, topic_1.persisted? + assert topic_1.persisted?, 'persisted' assert_not_nil topic_1.id - assert_equal true, topic_2.persisted? + assert topic_2.persisted?, 'persisted' assert_not_nil topic_2.id - assert_equal true, @first.persisted? + assert @first.persisted?, 'persisted' assert_not_nil @first.id - assert_equal true, @second.destroyed? + assert @second.destroyed?, 'destroyed' raise ActiveRecord::Rollback end - assert_equal false, topic_1.persisted? + assert !topic_1.persisted?, 'not persisted' assert_nil topic_1.id - assert_equal false, topic_2.persisted? + assert !topic_2.persisted?, 'not persisted' assert_nil topic_2.id - assert_equal true, @first.persisted? + assert @first.persisted?, 'persisted' assert_not_nil @first.id - assert_equal false, @second.destroyed? + assert !@second.destroyed?, 'not destroyed' end if current_adapter?(:PostgreSQLAdapter) && defined?(PGconn::PQTRANS_IDLE) -- cgit v1.2.3 From 79e6c7d2301d452c721d0b806071b0ebe2255eca Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 13 Dec 2010 13:21:18 -0800 Subject: stop delegating inserts to ARel, use the INSERT SQL ourselves --- activerecord/lib/active_record/relation.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3009bb70c1..7ecba1c43a 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -11,7 +11,6 @@ module ActiveRecord include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a - delegate :insert, :to => :arel attr_reader :table, :klass, :loaded attr_accessor :extensions @@ -28,6 +27,19 @@ module ActiveRecord @extensions = [] end + def insert(values) + im = arel.compile_insert values + im.into @table + primary_key_name = @klass.primary_key + primary_key_value = Hash === values ? values[primary_key_name] : nil + + @klass.connection.insert( + im.to_sql, + 'SQL', + primary_key_name, + primary_key_value) + end + def new(*args, &block) scoping { @klass.new(*args, &block) } end -- cgit v1.2.3 From 34d1e5d04d2e2583bf28fc2365f43d5917e2c648 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 13 Dec 2010 01:28:27 +0100 Subject: Fix indentation and newlines in generated engine --- railties/lib/rails/generators/named_base.rb | 2 +- .../rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index e0dde4360f..44a2639488 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -42,7 +42,7 @@ module Rails end def wrap_with_namespace(content) - content = indent(content) + content = indent(content).chomp "module #{namespace.name}\n#{content}\nend\n" end diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb b/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb index 9600ee0c3f..aa8ea77bae 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb +++ b/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb @@ -1,7 +1,7 @@ module <%= camelized %> class Engine < Rails::Engine <% if mountable? -%> - isolate_namespace <%= camelized %> + isolate_namespace <%= camelized %> <% end -%> end end -- cgit v1.2.3 From df20c9f1a9b1eed52390cb983e241206961beda0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 13 Dec 2010 13:49:39 -0800 Subject: fixing whitespace errors --- .../test/cases/associations/inner_join_association_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index 780eabc443..da2a81e98a 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -65,21 +65,21 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase authors_with_welcoming_post_titles = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true, :conditions => "posts.title like 'Welcome%'") assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'" end - + def test_find_with_sti_join scope = Post.joins(:special_comments).where(:id => posts(:sti_comments).id) - + # The join should match SpecialComment and its subclasses only assert scope.where("comments.type" => "Comment").empty? assert !scope.where("comments.type" => "SpecialComment").empty? assert !scope.where("comments.type" => "SubSpecialComment").empty? end - + def test_find_with_conditions_on_reflection assert !posts(:welcome).comments.empty? assert Post.joins(:nonexistant_comments).where(:id => posts(:welcome).id).empty? # [sic!] end - + def test_find_with_conditions_on_through_reflection assert !posts(:welcome).tags.empty? assert Post.joins(:misc_tags).where(:id => posts(:welcome).id).empty? -- cgit v1.2.3 From 90d9aa3b452354cddb2be5ce5ca2f6d0d0112431 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 14 Dec 2010 11:25:47 -0800 Subject: taking advantage of the JoinSource node in the SQL AST --- .../associations/class_methods/join_dependency.rb | 20 ++++++------------- .../join_dependency/join_association.rb | 1 - .../lib/active_record/relation/finder_methods.rb | 2 +- .../lib/active_record/relation/query_methods.rb | 23 +++++++--------------- 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 5a0ff942ca..a74d0a4ca8 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -47,24 +47,16 @@ module ActiveRecord end def count_aliases_from_table_joins(name) - return 0 if !@table_joins || Arel::Table === @table_joins + return 0 if Arel::Table === @table_joins # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase quoted_name = active_record.connection.quote_table_name(name).downcase - @table_joins.grep(Arel::Nodes::Join).map { |join| - right = join.right - case right - when Arel::Table - right.name.downcase == name ? 1 : 0 - when String - # Table names + table aliases - right.downcase.scan( - /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ - ).size - else - 0 - end + @table_joins.map { |join| + # Table names + table aliases + join.left.downcase.scan( + /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ + ).size }.sum end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index c552603097..694778008b 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -138,7 +138,6 @@ module ActiveRecord ands = relation.create_and(conditions) join = relation.create_join( - relation.froms.first, target_table, relation.create_on(ands), join_type) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 906ad7699c..8bc28c06cb 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -187,7 +187,7 @@ module ActiveRecord def find_with_associations including = (@eager_load_values + @includes_values).uniq - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, []) rows = construct_relation_for_association_find(join_dependency).to_a join_dependency.instantiate(rows) rescue ThrowResult diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 51a39be065..67a94cec85 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -191,27 +191,19 @@ module ActiveRecord def custom_join_ast(table, joins) joins = joins.reject { |join| join.blank? } - return if joins.empty? + return [] if joins.empty? @implicit_readonly = true - joins.map! do |join| + joins.map do |join| case join when Array join = Arel.sql(join.join(' ')) if array_of_strings?(join) when String join = Arel.sql(join) end - join + table.create_string_join(join) end - - head = table.create_string_join(table, joins.shift) - - joins.inject(head) do |ast, join| - ast.right = table.create_string_join(ast.right, join) - end - - head end def collapse_wheres(arel, wheres) @@ -256,9 +248,9 @@ module ActiveRecord stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) non_association_joins = (joins - association_joins - stashed_association_joins) - join_ast = custom_join_ast(manager.froms.first, non_association_joins) + join_list = custom_join_ast(manager, non_association_joins) - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_ast) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_list) join_dependency.graft(*stashed_association_joins) @@ -269,10 +261,9 @@ module ActiveRecord association.join_to(manager) end - return manager unless join_ast + return manager unless join_list - join_ast.left = manager.froms.first - manager.from join_ast + join_list.each { |j| manager.from j } manager end -- cgit v1.2.3 From fdabb8fe176921d65b0e056d6f41ed9a63650945 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 14 Dec 2010 12:34:04 -0800 Subject: bucketing based on join type --- .../lib/active_record/relation/query_methods.rb | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 67a94cec85..ef14f48a8f 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -239,16 +239,25 @@ module ActiveRecord end def build_joins(manager, joins) - joins = joins.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq - - association_joins = joins.find_all do |join| - [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join) + buckets = joins.group_by do |join| + case join + when String + 'string_join' + when Hash, Symbol, Array + 'association_join' + when ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation + 'stashed_join' + else + raise 'unknown class: %s' % join.class.name + end end - stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) - - non_association_joins = (joins - association_joins - stashed_association_joins) - join_list = custom_join_ast(manager, non_association_joins) + association_joins = buckets['association_join'] || [] + stashed_association_joins = buckets['stashed_join'] || [] + string_joins = (buckets['string_join'] || []).map { |x| + x.strip + }.uniq + join_list = custom_join_ast(manager, string_joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_list) -- cgit v1.2.3 From 6212ecaa0be6c0f035d9be26e8693bd1f09fdfb6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 14 Dec 2010 14:49:33 -0800 Subject: just copy the joins to the list --- .../lib/active_record/associations/has_many_through_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index d0c8af1801..6ad51e2fb4 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -54,7 +54,7 @@ module ActiveRecord end def construct_find_options!(options) - options[:joins] = construct_joins(options[:joins]) + options[:joins] = [construct_joins] + Array.wrap(options[:joins]) options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? && @reflection.source_reflection.options[:include] end -- cgit v1.2.3 From 2e7da54f770d1e076005c7670ab8d99431888faa Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 14 Dec 2010 14:57:03 -0800 Subject: supporting arel AST nodes when building join statements --- .../associations/through_association_scope.rb | 32 ++++++++++++---------- .../lib/active_record/relation/query_methods.rb | 18 ++++++++++-- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index acddfda924..48386ee124 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -55,33 +55,35 @@ module ActiveRecord end def construct_joins(custom_joins = nil) - polymorphic_join = nil + right = @reflection.through_reflection.klass.arel_table + left = @reflection.klass.arel_table + + conditions = [] + if @reflection.source_reflection.macro == :belongs_to reflection_primary_key = @reflection.klass.primary_key source_primary_key = @reflection.source_reflection.primary_key_name if @reflection.options[:source_type] - polymorphic_join = "AND %s.%s = %s" % [ - @reflection.through_reflection.quoted_table_name, "#{@reflection.source_reflection.options[:foreign_type]}", - @owner.class.quote_value(@reflection.options[:source_type]) - ] + column = @reflection.source_reflection.options[:foreign_type] + conditions << + right[column].eq(@reflection.options[:source_type]) end else reflection_primary_key = @reflection.source_reflection.primary_key_name source_primary_key = @reflection.through_reflection.klass.primary_key if @reflection.source_reflection.options[:as] - polymorphic_join = "AND %s.%s = %s" % [ - @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type", - @owner.class.quote_value(@reflection.through_reflection.klass.name) - ] + column = "#{@reflection.source_reflection.options[:as]}_type" + conditions << + left[column].eq(@reflection.through_reflection.klass.name) end end - "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [ - @reflection.through_reflection.quoted_table_name, - @reflection.quoted_table_name, reflection_primary_key, - @reflection.through_reflection.quoted_table_name, source_primary_key, - polymorphic_join - ] + conditions << + left[reflection_primary_key].eq(right[source_primary_key]) + + right.create_join( + right, + right.create_on(right.create_and(conditions))) end # Construct attributes for associate pointing to owner. diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index ef14f48a8f..8e50f4a9bc 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -247,6 +247,8 @@ module ActiveRecord 'association_join' when ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation 'stashed_join' + when Arel::Nodes::Join + 'join_node' else raise 'unknown class: %s' % join.class.name end @@ -254,12 +256,22 @@ module ActiveRecord association_joins = buckets['association_join'] || [] stashed_association_joins = buckets['stashed_join'] || [] + join_nodes = buckets['join_node'] || [] string_joins = (buckets['string_join'] || []).map { |x| x.strip }.uniq + join_list = custom_join_ast(manager, string_joins) - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_list) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new( + @klass, + association_joins, + join_list + ) + + join_nodes.each do |join| + join_dependency.table_aliases[join.left.name.downcase] = 1 + end join_dependency.graft(*stashed_association_joins) @@ -270,9 +282,9 @@ module ActiveRecord association.join_to(manager) end - return manager unless join_list + manager.join_sources.concat join_nodes + manager.join_sources.concat join_list - join_list.each { |j| manager.from j } manager end -- cgit v1.2.3 From 7af9ec8a99bbc044af1f08200b4aa350ac64a53d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 14 Dec 2010 15:03:06 -0800 Subject: construct_joins no longer needs an argument --- .../lib/active_record/associations/through_association_scope.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 48386ee124..f6b242168c 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -54,7 +54,7 @@ module ActiveRecord custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*" end - def construct_joins(custom_joins = nil) + def construct_joins right = @reflection.through_reflection.klass.arel_table left = @reflection.klass.arel_table -- cgit v1.2.3 From 820582883a301ad813534492f8f3223a582824f1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 14 Dec 2010 15:24:25 -0800 Subject: build SQL AST nodes rather than generate strings --- .../associations/through_association_scope.rb | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index f6b242168c..8d701248af 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -23,25 +23,23 @@ module ActiveRecord # Build SQL conditions from attributes, qualified by table name. def construct_conditions - table_name = @reflection.through_reflection.quoted_table_name + table = @reflection.through_reflection.klass.arel_table conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| - "#{table_name}.#{attr} = #{value}" + table[attr].eq(value) end - conditions << sql_conditions if sql_conditions - "(" + conditions.join(') AND (') + ")" + conditions << Arel.sql(sql_conditions) if sql_conditions + table.create_and(conditions) end # Associate attributes pointing to owner, quoted. def construct_quoted_owner_attributes(reflection) if as = reflection.options[:as] - { "#{as}_id" => owner_quoted_id, - "#{as}_type" => reflection.klass.quote_value( - @owner.class.base_class.name.to_s, - reflection.klass.columns_hash["#{as}_type"]) } + { "#{as}_id" => @owner.id, + "#{as}_type" => @owner.class.base_class.name } elsif reflection.macro == :belongs_to - { reflection.klass.primary_key => @owner.class.quote_value(@owner[reflection.primary_key_name]) } + { reflection.klass.primary_key => @owner[reflection.primary_key_name] } else - { reflection.primary_key_name => owner_quoted_id } + { reflection.primary_key_name => @owner.id } end end -- cgit v1.2.3 From f2234a5dd3a1d891e585a703f1a432153e4d2c18 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 14 Dec 2010 15:27:06 -0800 Subject: class names are already strings, so we do not need to call to_s on the strings that are already strings --- .../lib/active_record/associations/through_association_scope.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 8d701248af..6b7faa9fc3 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -88,7 +88,7 @@ module ActiveRecord def construct_owner_attributes(reflection) if as = reflection.options[:as] { "#{as}_id" => @owner.id, - "#{as}_type" => @owner.class.base_class.name.to_s } + "#{as}_type" => @owner.class.base_class.name } else { reflection.primary_key_name => @owner.id } end @@ -102,7 +102,7 @@ module ActiveRecord join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) if @reflection.options[:source_type] - join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s) + join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name) end if @reflection.through_reflection.options[:conditions].is_a?(Hash) -- cgit v1.2.3 From 00c893d3b86be3a141c21ed065e085adbb26062a Mon Sep 17 00:00:00 2001 From: Joe Hannon Date: Sun, 2 May 2010 16:26:02 -0700 Subject: add test which fails for has_many through self join [#4361 state:open] --- .../test/cases/associations/has_many_through_associations_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 52432b0428..fe60e676d6 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -388,6 +388,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) } end + def test_has_many_association_through_a_has_many_association_to_self + sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1) + john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1) + assert_equal sarah.agents, [john] + assert_equal people(:susan).agents_of_agents, [john] + end + def test_collection_singular_ids_getter_with_string_primary_keys book = books(:awdr) assert_equal 2, book.subscriber_ids.size -- cgit v1.2.3 From c81af00a7ab64ec1159e8b319f8498ea5b9037c7 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Mon, 3 May 2010 20:47:42 -0400 Subject: Fix hm:t to self table aliasing in construct_scope --- .../active_record/associations/through_association_scope.rb | 12 ++++++++++-- .../cases/associations/has_many_through_associations_test.rb | 2 +- activerecord/test/models/person.rb | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 6b7faa9fc3..a55bf6323e 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -21,9 +21,17 @@ module ActiveRecord construct_owner_attributes(@reflection) end + def aliased_through_table + name = @reflection.through_reflection.table_name + + @reflection.table_name == name ? + @reflection.through_reflection.klass.arel_table.alias(name + "_join") : + @reflection.through_reflection.klass.arel_table + end + # Build SQL conditions from attributes, qualified by table name. def construct_conditions - table = @reflection.through_reflection.klass.arel_table + table = aliased_through_table conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| table[attr].eq(value) end @@ -53,7 +61,7 @@ module ActiveRecord end def construct_joins - right = @reflection.through_reflection.klass.arel_table + right = aliased_through_table left = @reflection.klass.arel_table conditions = [] diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index fe60e676d6..34ae297275 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -392,7 +392,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1) john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1) assert_equal sarah.agents, [john] - assert_equal people(:susan).agents_of_agents, [john] + assert_equal people(:susan).agents.map(&:agents).flatten, people(:susan).agents_of_agents end def test_collection_singular_ids_getter_with_string_primary_keys diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 951ec93c53..bee89de042 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -12,6 +12,7 @@ class Person < ActiveRecord::Base belongs_to :primary_contact, :class_name => 'Person' has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id' + has_many :agents_of_agents, :through => :agents, :source => :agents belongs_to :number1_fan, :class_name => 'Person' scope :males, :conditions => { :gender => 'M' } -- cgit v1.2.3 From de4f9acad8b7014b29352a747926ee2db6f71643 Mon Sep 17 00:00:00 2001 From: Elben Shira Date: Tue, 14 Dec 2010 20:35:08 -0600 Subject: It should be ActiveModel::Validator, not ActiveRecord::Validator. --- railties/guides/source/active_record_validations_callbacks.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 0824ba450c..a15571fe58 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -414,7 +414,7 @@ class Person < ActiveRecord::Base validates_with GoodnessValidator end -class GoodnessValidator < ActiveRecord::Validator +class GoodnessValidator < ActiveModel::Validator def validate if record.first_name == "Evil" record.errors[:base] << "This person is evil" -- cgit v1.2.3 From 003fce430ab3991ef19cba5eef902510968b2ebd Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 15 Dec 2010 14:08:08 +1000 Subject: WIP: Reviewing the initialization guide for Rails 3.1 --- railties/guides/source/initialization.textile | 514 ++++++++++++++++++++------ 1 file changed, 393 insertions(+), 121 deletions(-) diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 4cc5f3843f..72e10191f9 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -1,79 +1,140 @@ h2. The Rails Initialization Process -This guide explains how the initialization process in Rails works as of Rails 3. +This guide explains the internals of the initialization process in Rails works as of Rails 3.1. It is an extremely in-depth guide and recommended for advanced Rails developers. * Using +rails server+ * Using Passenger endprologue. -This guide first describes the process of +rails server+ then explains the Passenger + Rack method, before delving into the common initialize pattern these two go through. +This guide goes through every single file, class and method call that is required to boot up the Ruby on Rails stack for a default Rails 3.1 application, explaining each part in detail a long the way. For this guide, we will be focusing on how the two most common methods (+rails server+ and Passenger) boot a Rails application. + +NOTE: Paths in this guide are relative to Rails or a Rails application unless otherwise specified. h3. Launch! As of Rails 3, +script/server+ has become +rails server+. This was done to centralize all rails related commands to one common file. -The actual +rails+ command is kept in _railties/bin/rails_ and goes like this: +h4. +bin/rails+ + +The actual +rails+ command is kept in _bin/rails_ at the and goes like this: - require 'rbconfig' + #!/usr/bin/env ruby - module Rails - module ScriptRailsLoader - RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"] - SCRIPT_RAILS = File.join('script', 'rails') - - def self.exec_script_rails! - cwd = Dir.pwd - exec RUBY, SCRIPT_RAILS, *ARGV if File.exists?(SCRIPT_RAILS) - Dir.chdir("..") do - # Recurse in a chdir block: if the search fails we want to be sure - # the application is generated in the original working directory. - exec_script_rails! unless cwd == Dir.pwd - end - rescue SystemCallError - # could not chdir, no problem just return - end - end + begin + require "rails/cli" + rescue LoadError + railties_path = File.expand_path('../../railties/lib', __FILE__) + $:.unshift(railties_path) + require "rails/cli" end + - Rails::ScriptRailsLoader.exec_script_rails! +This file will attempt to load +rails/cli+ and if it cannot find it then add the +railties/lib+ path to the load path (+$:+) and will then try to require it again. - railties_path = File.expand_path('../../lib', __FILE__) - $:.unshift(railties_path) if File.directory?(railties_path) && !$:.include?(railties_path) +h4. +railites/lib/rails/cli.rb+ + +This file looks like this: + + + require 'rbconfig' + require 'rails/script_rails_loader' + + # If we are inside a Rails application this method performs an exec and thus + # the rest of this script is not run. + Rails::ScriptRailsLoader.exec_script_rails! require 'rails/ruby_version_check' Signal.trap("INT") { puts; exit } - require 'rails/commands/application' + if ARGV.first == 'plugin' + ARGV.shift + require 'rails/commands/plugin_new' + else + require 'rails/commands/application' + end -The +Rails::ScriptRailsLoader+ module here defines two constants: +RUBY+ and +SCRIPT_RAILS+. +RUBY+ is the full path to your ruby executable, on a Snow Leopard system it's _/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby_. +SCRIPT_RAILS+ is simply _script/rails_. When +exec_script_rails+ is invoked, this will attempt to +exec+ the _rails_ file in the _script_ directory using the path to your Ruby executable, +RUBY+. If +exec+ is invoked, the program will stop at this point. If the _script/rails_ file doesn't exist in the current directory, Rails will recurse upwards until it finds it by calling +exec_script_rails+ from inside the +Dir.chdir("..")+. This is handy if you're currently in one of the sub-directories of the rails application and wish to launch a server or a console. +The +rbconfig+ file here is out of Ruby's standard library and provides us with the +RbConfig+ class which contains useful information dependent on how Ruby was compiled. We'll see this in use in +railties/lib/rails/script_rails_loader+. + + +require 'pathname' + +module Rails + module ScriptRailsLoader + RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"] + SCRIPT_RAILS = File.join('script', 'rails') + ... -If Rails cannot execute _script/rails_ then it will fall back to the standard +rails+ command task of generating an application. + end +end + + +The +rails/script_rails_loader+ file uses +RbConfig::Config+ to gather up the +bin_dir+ and +ruby_install_name+ values for the configuration which will result in a path such as +/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby+, which is the default path on Mac OS X. If you're running Windows the path may be something such as +C:/Ruby192/bin/ruby+. Anyway, the path on your system may be different, but the point of this is that it will point at the known ruby executable location for your install. The +RbConfig::CONFIG["EXEEXT"]+ will suffix this path with ".exe" if the script is running on Windows. This constant is used later on in +exec_script_rails!+. As for the +SCRIPT_RAILS+ console, we'll see that when we get to the +in_rails_application?+ method. -In +script/rails+ we see the following: +Back in +rails/cli+, the next line is this: - #!/usr/bin/env ruby - # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. + Rails::ScriptRailsLoader.exec_script_rails! + - APP_PATH = File.expand_path('../../config/application', __FILE__) - require File.expand_path('../../config/boot', __FILE__) - require 'rails/commands' +This method is defined in +rails/script_rails_loader+ like this: + + + def self.exec_script_rails! + cwd = Dir.pwd + return unless in_rails_application? || in_rails_application_subdirectory? + exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application? + Dir.chdir("..") do + # Recurse in a chdir block: if the search fails we want to be sure + # the application is generated in the original working directory. + exec_script_rails! unless cwd == Dir.pwd + end + rescue SystemCallError + # could not chdir, no problem just return + end -This obviously defines a couple of constants to some pretty important files, _config/environment.rb_, _config/boot.rb_ and _config/application.rb_ all within the context of +__FILE__+ which is of course +script/rails+ in the root of your application. Then it goes on to +require BOOT_PATH+ which leads us onto _config/boot.rb_. +This method will first check if the current working directory (+cwd+) is a Rails application or is a subdirectory of one. The way to determine this is defined in the +in_rails_application?+ method like this: -h3. Passenger + + def self.in_rails_application? + File.exists?(SCRIPT_RAILS) + end + -Before we dive into what _config/boot.rb_ encompasses, we'll just glimpse at what Passenger does enough to get an understanding of how it requires a Rails application. +The +SCRIPT_RAILS+ constant defined earlier is used here, with +File.exists?+ checking for its presence in the current directory. If this method returns +false+, then +in_rails_application_subdirectory?+ will be used: + + + def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd)) + File.exists?(File.join(path, SCRIPT_RAILS)) || !path.root? && in_rails_application_subdirectory?(path.parent) + end + + +This climbs the directory tree until it reaches a path which contains a +script/rails+ file. If a directory is reached which contains this file then this line will run: + + + exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application? + + +This is effectively the same as doing +ruby script/rails [arguments]+. Where +[arguments]+ at this point in time is simply "server". + +h4. +script/rails+ + +This file looks like this: + + + APP_PATH = File.expand_path('../../config/application', __FILE__) + require File.expand_path('../../config/boot', __FILE__) + require 'rails/commands' + -Passenger will require _config/environment.rb_ by way of its +PhusionPassenger::Railz::ApplicationSpawner#preload_application+ method. _config/environment.rb_ requires _config/application.rb_ which requires _config/boot.rb_. That's how the Rails boot process begins with Passenger in a nutshell. +The +APP_PATH+ constant here will be used later in +rails/commands+. The +config/boot+ file that +script/rails+ references is the +config/boot.rb+ file in our application which is responsible for loading Bundler and setting it up. -h3. _config/boot.rb_ +h4. +config/boot.rb+ -_config/boot.rb_ is the first stop for everything for initializing your application. This boot process does quite a bit of work for you and so this section attempts to go in-depth enough to explain what each of the pieces does. ++config/boot.rb+ contains this: require 'rubygems' @@ -91,134 +152,345 @@ _config/boot.rb_ is the first stop for everything for initializing your applicat end if File.exist?(gemfile) -h3. Bundled Rails (3.x) +In a standard Rails application, there's a +Gemfile+ which declares all dependencies of the application. +config/boot.rb+ sets +ENV["BUNDLE_GEMFILE"]+ to the location of this file, then requires Bundler and calls +Bundler.setup+ which adds the dependencies of the application (including all the Rails parts) to the load path, making them available for the application to load. The gems that a Rails 3.1 application depends on are as follows: + +* abstract (1.0.0) +* actionmailer (3.1.0.beta) +* actionpack (3.1.0.beta) +* activemodel (3.1.0.beta) +* activerecord (3.1.0.beta) +* activeresource (3.1.0.beta) +* activesupport (3.1.0.beta) +* arel (2.0.7) +* builder (3.0.0) +* bundler (1.0.6) +* erubis (2.6.6) +* i18n (0.5.0) +* mail (2.2.12) +* mime-types (1.16) +* polyglot (0.3.1) +* rack (1.2.1) +* rack-cache (0.5.3) +* rack-mount (0.6.13) +* rack-test (0.5.6) +* rails (3.1.0.beta) +* railties (3.1.0.beta) +* rake (0.8.7) +* sqlite3-ruby (1.3.2) +* thor (0.14.6) +* treetop (1.4.9) +* tzinfo (0.3.23) + +h4. +rails/commands.rb+ + +Once +config/boot.rb+ has finished, the next file that is required is +rails/commands+ which will execute a command based on the arguments passed in. In this case, the +ARGV+ array simply contains +server+ which is extracted into the +command+ variable using these lines: + + + aliases = { + "g" => "generate", + "c" => "console", + "s" => "server", + "db" => "dbconsole" + } + + command = ARGV.shift + command = aliases[command] || command + + +If we used s rather than +server+, Rails will use the +aliases+ defined in the file and match them to their respective commands. With the +server+ command, Rails will run this code: + + + when 'server' + # Change to the application's path if there is no config.ru file in current dir. + # This allows us to run script/rails server from other directories, but still get + # the main config.ru and properly set the tmp directory. + Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru")) + + require 'rails/commands/server' + Rails::Server.new.tap { |server| + # We need to require application after the server sets environment, + # otherwise the --environment option given to the server won't propagate. + require APP_PATH + Dir.chdir(Rails.application.root) + server.start + } + + +This file will change into the root of the directory (a path two directories back from +APP_PATH+ which points at +config/application.rb+), but only if the +config.ru+ file isn't found. This then requires +rails/commands/server+ which requires +action_dispatch+ and sets up the +Rails::Server+ class. + +h4. +actionpack/lib/action_dispatch.rb+ + +Action Dispatch is the routing component of the Rails framework. It depends on Active Support, +actionpack/lib/action_pack.rb+ and +Rack+ being available. The first thing required here is +active_support+. + +h4. +active_support/lib/active_support.rb+ + +This file begins with requiring +active_support/lib/active_support/dependencies/autoload.rb+ which redefines Ruby's +autoload+ method to have a little more extra behaviour especially in regards to eager autoloading. Eager autoloading is the loading of all required classes and will happen when the +config.cache_classes+ setting is +true+. +The +active_support/lib/active_support/version.rb+ that is also required here simply defines an +ActiveSupport::VERSION+ constant which defines a couple of constants inside this module, the main constant of this is +ActiveSupport::VERSION::STRING+ which returns the current version of ActiveSupport. -Rails 3 now uses Bundler and the README for the project explains it better than I could: +The +active_support/lib/active_support.rb+ file simply defines the +ActiveSupport+ module and some autoloads (eager and of the normal variety) for it. -> "Bundler is a tool that manages gem dependencies for your ruby application. It takes a gem manifest file and is able to fetch, download, and install the gems and all child dependencies specified in this manifest. It can manage any update to the gem manifest file and update the bundle's gems accordingly. It also lets you run any ruby code in context of the bundle's gem environment." +h4. +actionpack/lib/action_dispatch.rb+ cont'd. -Now with Rails 3 we have a Gemfile which defines the basics our application needs to get going: +Now back to +action_pack/lib/action_dispatch.rb+. The next +require+ in this file is one for +action_pack+, which simply calls +action_pack/version.rb+ which defines +ActionPack::VERSION+ and the constants, much like +ActiveSpport+ does. + +After this line, there's a require to +active_model+ which simply defines autoloads for the +ActiveModel+ part of Rails and sets up the +ActiveModel+ module which is used later on. + +The last of the requires is to +rack+, which like the +active_model+ and +active_support+ requires before it, sets up the +Rack+ module as well as the autoloads for constants within it. + +Finally in +action_dispatch.rb+ the +ActionDispatch+ module and *its* autoloads are declared. + +h4. +rails/commands/server.rb+ + +The +Rails::Server+ class is defined in this file as inheriting from +Rack::Server+. When +Rails::Server.new+ is called, this calls the +initialize+ method in +rails/commands/server.rb+: - source 'http://rubygems.org' + def initialize(*) + super + set_environment + end + - gem 'rails', '3.0.0' +Firstly, +super+ is called which calls the +initialize+ method on +Rack::Server+. - # Bundle edge Rails instead: - # gem 'rails', :git => 'git://github.com/rails/rails.git' +h4. Rack: +lib/rack/server.rb+ - gem 'sqlite3-ruby', :require => 'sqlite3' ++Rack::Server+ is responsible for providing a common server interface for all Rack-based applications, which Rails is now a part of. - # Use unicorn as the web server - # gem 'unicorn' +The +initialize+ method in +Rack::Server+ simply sets a couple of variables: - # Deploy with Capistrano - # gem 'capistrano' + + def initialize(options = nil) + @options = options + @app = options[:app] if options && options[:app] + end + - # Bundle the extra gems: - # gem 'bj' - # gem 'nokogiri' - # gem 'sqlite3-ruby', :require => 'sqlite3' - # gem 'aws-s3', :require => 'aws/s3' +In this case, +options+ will be +nil+ so nothing happens in this method. - # Bundle gems for certain environments: - # gem 'rspec', :group => :test - # group :test do - # gem 'webrat' - # end +After +super+ has finished in +Rack::Server+, we jump back to +rails/commands/server.rb+. At this point, +set_environment+ is called within the context of the +Rails::Server+ object and this method doesn't appear to do much at first glance: + + def set_environment + ENV["RAILS_ENV"] ||= options[:environment] + end -Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This is until you run +bundle pack+. This command freezes all the gems required by your application into _vendor/cache_. The gems installed by default are: +In fact, the +options+ method here does quite a lot. This method is defined in +Rack::Server+ like this: -* abstract-1.0.0.gem -* actionmailer-3.0.0.gem -* actionpack-3.0.0.gem -* activemodel-3.0.0.gem -* activerecord-3.0.0.gem -* activeresource-3.0.0.gem -* activesupport-3.0.0.gem -* arel-0.4.0.gem -* builder-2.1.2.gem -* bundler-1.0.3.gem -* erubis-2.6.6.gem -* i18n-0.4.1.gem -* mail-2.2.5.gem -* memcache-client-1.8.5.gem -* mime-types-1.16.gem -* nokogiri-1.4.3.1.gem -* polyglot-0.3.1.gem -* rack-1.2.1.gem -* rack-mount-0.6.12.gem -* rack-test-0.5.4.gem -* rails-3.0.0.gem -* railties-3.0.0.gem -* rake-0.8.7.gem -* sqlite3-ruby-1.3.1.gem -* text-format-1.0.0.gem -* text-hyphen-1.0.0.gem -* thor-0.13.7.gem -* treetop-1.4.8.gem -* tzinfo-0.3.23.gem + + def options + @options ||= parse_options(ARGV) + end + -TODO: Prettify when it becomes more stable. +Then +parse_options+ is defined like this: -I won't go into what each of these gems are, as that is really something that needs covering on a case-by-case basis. We will however just dig a little under the surface of Bundler. + + def parse_options(args) + options = default_options -Back in _config/boot.rb_, we call +Bundler.setup+ which will load and parse the +Gemfile+ and add the _lib_ directory of the gems mentioned **and** their dependencies (**and** their dependencies' dependencies, and so on) to the +$LOAD_PATH+. + # Don't evaluate CGI ISINDEX parameters. + # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html + args.clear if ENV.include?("REQUEST_METHOD") -h3. Requiring Rails + options.merge! opt_parser.parse! args + options[:config] = ::File.expand_path(options[:config]) + ENV["RACK_ENV"] = options[:environment] + options + end + -After _config/boot.rb_ is loaded, there's this +require+: +With the +default_options+ set to this: - require 'rails/commands' + def default_options + { + :environment => ENV['RACK_ENV'] || "development", + :pid => nil, + :Port => 9292, + :Host => "0.0.0.0", + :AccessLog => [], + :config => "config.ru" + } + end -In this file, _railties/lib/rails/commands.rb_, there is a case statement for +ARGV.shift+: +There is no +REQUEST_METHOD+ key in +ENV+ so we can skip over that line. The next line merges in the options from +opt_parser+ which is defined plainly in +Rack::Server+ - case ARGV.shift - ... - when 's', 'server' - require 'rails/commands/server' - # Initialize the server first, so environment options are set - server = Rails::Server.new - require APP_PATH + def opt_parser + Options.new + end + + +The class *is* defined in +Rack::Server+, but is overwritten in +Rails::Server+ to take different arguments. Its +parse!+ method begins like this: + + + def parse!(args) + args, options = args.dup, {} + + opt_parser = OptionParser.new do |opts| + opts.banner = "Usage: rails server [mongrel, thin, etc] [options]" + opts.on("-p", "--port=port", Integer, + "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v } ... + + +This method will set up keys for the +options+ which Rails will then be able to use to determine how its server should run. After +initialize+ has finished, then the +start+ method will launch the server. + +h4. +Rails::Server#start+ + +This method is defined like this: + + + def start + puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" + puts "=> Rails #{Rails.version} application starting in #{Rails.env} on http://#{options[:Host]}:#{options[:Port]}" + puts "=> Call with -d to detach" unless options[:daemonize] + trap(:INT) { exit } + puts "=> Ctrl-C to shutdown server" unless options[:daemonize] + + #Create required tmp directories if not found + %w(cache pids sessions sockets).each do |dir_to_make| + FileUtils.mkdir_p(Rails.root.join('tmp', dir_to_make)) + end + + super + ensure + # The '-h' option calls exit before @options is set. + # If we call 'options' with it unset, we get double help banners. + puts 'Exiting' unless @options && options[:daemonize] end -We're running +rails server+ and this means it will make a require out to _rails/commands/server_ (_railties/lib/rails/commands/server.rb_). Firstly, this file makes a couple of requires of its own: +This is where the first output of the Rails initialization happens. This method creates a trap for +INT+ signals, so if you +CTRL+C+ the server, it will exit the process. As we can see from the code here, it will create the +tmp/cache+, +tmp/pids+, +tmp/sessions+ and +tmp/sockets+ directories if they don't already exist prior to calling +super+. The +super+ method will call +Rack::Server.start+ which begins its definition like this: + + + def start + if options[:warn] + $-w = true + end + + if includes = options[:include] + $LOAD_PATH.unshift(*includes) + end + + if library = options[:require] + require library + end + + if options[:debug] + $DEBUG = true + require 'pp' + p options[:server] + pp wrapped_app + pp app + end + + +In a Rails application, these options are not set at all and therefore aren't used at all. The first line of code that's executed in this method is a call to this method: - require 'fileutils' - require 'optparse' - require 'action_dispatch' + wrapped_app -The first two are Ruby core and this guide does not cover what they do, but _action_dispatch_ (_actionpack/lib/action_dispatch.rb_) is important. This file firstly make a require to _active_support_ (_activesupport/lib/active_support.rb_) which defines the +ActiveSupport+ module. +This method calls another method: -h4. +require 'active_support'+ + + @wrapped_app ||= build_app app + -_activesupport/lib/active_support.rb_ sets up +module ActiveSupport+: +Then the +app+ method here is defined like so: - module ActiveSupport - class << self - attr_accessor :load_all_hooks - def on_load_all(&hook) load_all_hooks << hook end - def load_all!; load_all_hooks.each { |hook| hook.call } end + def app + @app ||= begin + if !::File.exist? options[:config] + abort "configuration #{options[:config]} not found" + end + + app, options = Rack::Builder.parse_file(self.options[:config], opt_parser) + self.options.merge! options + app end - self.load_all_hooks = [] + end + + +The +options[:config]+ value defaults to +config.ru+ which contains this: + + + # This file is used by Rack-based servers to start the application. + + require ::File.expand_path('../config/environment', __FILE__) + run YourApp::Application + + + +The +Rack::Builder.parse_file+ method here takes the content from this +config.ru+ file and parses it using this code: + + + app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", + TOPLEVEL_BINDING, config + + +The initialize method will take the block here and execute it within an instance of +Rack::Builder+. This is where the majority of the initialization process of Rails happens. The chain of events that this simple line sets off will be the focus of a large majority of this guide. The +require+ line for +config/environment.rb+ in +config.ru+ is the first to run: + + + require ::File.expand_path('../config/environment', __FILE__) + + +h4. +config/environment.rb+ + +This file is the common file required by +config.ru+ (+rails server+) and Passenger. This is where these two ways to run the server meet; everything before this point has been Rack and Rails setup. + +This file begins with requiring +config/application.rb+. + +h4. +config/application.rb+ + +This file requires +config/boot.rb+, but only if it hasn't been required before, which would be the case in +rails server+ but *wouldn't* be the case with Passenger. + +Then the fun begins! The next line is: - on_load_all do - [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte, SecureRandom] + + require 'rails/all' + + +h4 +railties/lib/rails/all.rb+ + + +This file is responsible for requiring all the individual parts of Rails like so: + + + require "rails" + + %w( + active_record + action_controller + action_mailer + active_resource + rails/test_unit + ).each do |framework| + begin + require "#{framework}/railtie" + rescue LoadError end end +First off the line is the +rails+ require itself. + +h4. +railties/lib/rails.rb+ + +This file is responsible for the initial definition of the +Rails+ module and, rather than defining the autoloads like +ActiveSupport+, +ActionDispatch+ and so on, it actually defines other functionality. Such as the +root+, +env+ and +application+ methods which are extremely useful in Rails 3 applications. + +However, before all that takes place the +rails/ruby_version_check+ file is required first. + + + + +**** REVIEW IS HERE **** + This defines two methods on the module itself by using the familiar +class << self+ syntax. This allows you to call them as if they were class methods: +ActiveSupport.on_load_all+ and +ActiveSupport.load_all!+ respectively. The first method simply adds loading hooks to save them up for loading later on when +load_all!+ is called. By +call+'ing the block, the classes will be loaded. (NOTE: kind of guessing, I feel 55% about this). The +on_load_all+ method is called later with the +Dependencies+, +Deprecation+, +Gzip+, +MessageVerifier+, +Multibyte+ and +SecureRandom+. What each of these modules do will be covered later. -- cgit v1.2.3 From a7791c375228472f2d51705bcee01eced982df3a Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 15 Dec 2010 16:17:55 +1000 Subject: Active Support coreext guide: Logger core extension documentation --- .../source/active_support_core_extensions.textile | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index 7333a81cf9..2e295aec3d 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -3359,6 +3359,49 @@ The auxiliary file is written in a standard directory for temporary files, but y NOTE: Defined in +active_support/core_ext/file/atomic.rb+. +h3. Extensions to +Logger+ + +h4. +around_[level]+ + +Takes two arguments, a +before_message+ and +after_message+ and calls the current level method on the +Logger+ instance, passing in the +before_message+, then the specified message, then the +after_message+: + + + logger = Logger.new("log/development.log") + logger.around_info("before", "after") { |logger| logger.info("during") } + + +h4. +silence+ + +Silences every log level lesser to the specified one for the duration of the given block. Log level orders are: debug, info, error and fatal. + + + logger = Logger.new("log/development.log") + logger.silence(Logger::INFO) do + logger.debug("In space, no one can hear you scream.") + logger.info("Scream all you want, small mailman!") + end + + +h4. +datetime_format=+ + +Modifies the datetime format output by the formatter class associated with this logger. If the formatter class does not have a +datetime_format+ method then this is ignored. + + + class Logger::FormatWithTime < Logger::Formatter + cattr_accessor(:datetime_format) { "%Y%m%d%H%m%S" } + + def self.call(severity, timestamp, progname, msg) + "#{timestamp.strftime(datetime_format)} -- #{String === msg ? msg : msg.inspect}\n" + end + end + + logger = Logger.new("log/development.log") + logger.formatter = Logger::FormatWithTime + logger.info("<- is the current time") + + +NOTE: Defined in +active_support/core_ext/logger.rb+. + h3. Extensions to +NameError+ Active Support adds +missing_name?+ to +NameError+, which tests whether the exception was raised because of the name passed as argument. -- cgit v1.2.3 From 5e3b853aee86b74e68c9c3f95201c54af5502d2f Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 15 Dec 2010 09:09:29 +0100 Subject: Should be isolated engine instead of namespaced engine --- railties/lib/rails/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index cda0e0a135..1d81e08cd3 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -207,7 +207,7 @@ module Rails # end # end # - # == Namespaced Engine + # == Isolated Engine # # Normally when you create controllers, helpers and models inside engine, they are treated # as they were created inside the application. This means all applications helpers and named routes -- cgit v1.2.3 From 0e5ee9af48f11201a3b06fb3f5a50144224ec9e8 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 15 Dec 2010 09:01:18 +0100 Subject: Set proper engine's asset directories when assets are served from engine. When using stylesheet_link_tag(:all) or javascript_include_tag(:all), assets directories are searched for css or js files. When config.serve_static_assets is set to true, those files can be served directly from engine's directories. That's why assets paths should be set individually for controllers inside engine if we want to serve static assets with ActionDispatch::Static --- actionpack/lib/action_controller/railties/paths.rb | 8 +++ railties/test/railties/engine_test.rb | 64 ++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb index 699c44c62c..dce3c2fe88 100644 --- a/actionpack/lib/action_controller/railties/paths.rb +++ b/actionpack/lib/action_controller/railties/paths.rb @@ -16,6 +16,14 @@ module ActionController if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers klass.helper :all end + + if app.config.serve_static_assets && namespace + paths = namespace._railtie.config.paths + + klass.config.assets_dir = paths["public"].first + klass.config.javascripts_dir = paths["public/javascripts"].first + klass.config.stylesheets_dir = paths["public/stylesheets"].first + end end end end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 05bd0c36cd..6b64a19741 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -721,5 +721,69 @@ module RailtiesTest engine_path = File.join(@plugin.path, '..', engine_dir) assert_equal Bukkits::Engine.instance, Rails::Engine.find(engine_path) end + + test "ensure that engine properly sets assets directories" do + add_to_config("config.action_dispatch.show_exceptions = false") + add_to_config("config.serve_static_assets = true") + + @plugin.write "lib/bukkits.rb", <<-RUBY + module Bukkits + class Engine < ::Rails::Engine + isolate_namespace Bukkits + end + end + RUBY + + @plugin.write "public/stylesheets/foo.css", "" + @plugin.write "public/javascripts/foo.js", "" + + @plugin.write "app/views/layouts/bukkits/application.html.erb", <<-RUBY + <%= stylesheet_link_tag :all %> + <%= javascript_include_tag :all %> + <%= yield %> + RUBY + + @plugin.write "app/controllers/bukkits/home_controller.rb", <<-RUBY + module Bukkits + class HomeController < ActionController::Base + def index + render :text => "Good morning!", :layout => "bukkits/application" + end + end + end + RUBY + + @plugin.write "config/routes.rb", <<-RUBY + Bukkits::Engine.routes.draw do + match "/home" => "home#index" + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + mount Bukkits::Engine => "/bukkits" + end + RUBY + + require 'rack/test' + extend Rack::Test::Methods + + boot_rails + + require "#{rails_root}/config/environment" + + assert_equal File.join(@plugin.path, "public"), Bukkits::HomeController.assets_dir + assert_equal File.join(@plugin.path, "public/stylesheets"), Bukkits::HomeController.stylesheets_dir + assert_equal File.join(@plugin.path, "public/javascripts"), Bukkits::HomeController.javascripts_dir + + assert_equal File.join(app_path, "public"), ActionController::Base.assets_dir + assert_equal File.join(app_path, "public/stylesheets"), ActionController::Base.stylesheets_dir + assert_equal File.join(app_path, "public/javascripts"), ActionController::Base.javascripts_dir + + get "/bukkits/home" + + assert_match %r{bukkits/stylesheets/foo.css}, last_response.body + assert_match %r{bukkits/javascripts/foo.js}, last_response.body + end end end -- cgit v1.2.3 From 6fc3750471ff9642395c4e0462d5059c2133d9aa Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Wed, 15 Dec 2010 16:13:36 +0530 Subject: eternal confusion! fixed doco to inform correctly --- actionpack/lib/action_view/helpers/form_tag_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index c41271635d..9500e85e8b 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -68,7 +68,7 @@ module ActionView # * Any other key creates standard HTML attributes for the tag. # # ==== Examples - # select_tag "people", options_from_collection_for_select(@people, "name", "id") + # select_tag "people", options_from_collection_for_select(@people, "id", "name") # # # # select_tag "people", "" -- cgit v1.2.3 From 5d63a36ae8c2a839d0a197b10250184a035064e7 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 15 Dec 2010 12:20:20 +0100 Subject: Generate javascripts in engine with --mountable option --- railties/lib/rails/generators/app_base.rb | 6 ++++ .../rails/generators/rails/app/app_generator.rb | 5 ---- .../rails/plugin_new/plugin_new_generator.rb | 35 ++++++++++++++++++++++ .../test/generators/plugin_new_generator_test.rb | 29 ++++++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 2d0c10efca..7766050632 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -171,6 +171,12 @@ gem 'rails', '#{Rails::VERSION::STRING}' def dev_or_edge? options.dev? || options.edge? end + + def empty_directory_with_gitkeep(destination, config = {}) + empty_directory(destination, config) + create_file("#{destination}/.gitkeep") unless options[:skip_git] + end + end end end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index ef1eb8d237..f833b5d041 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -317,11 +317,6 @@ module Rails ].find { |f| File.exist?(f) } unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end - def empty_directory_with_gitkeep(destination, config = {}) - empty_directory(destination, config) - create_file("#{destination}/.gitkeep") unless options[:skip_git] - end - def get_builder_class defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder end diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 9c54b98238..3cd4fa4ba1 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -88,6 +88,29 @@ task :default => :test end end + def stylesheets + empty_directory_with_gitkeep "public/stylesheets" if options[:mountable] + end + + def javascripts + return unless options[:mountable] + + empty_directory "#{app_templates_dir}/public/javascripts" + + unless options[:skip_javascript] + copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}.js", "public/javascripts/#{options[:javascript]}.js" + copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}_ujs.js", "public/javascripts/rails.js" + + if options[:prototype] + copy_file "#{app_templates_dir}/public/javascripts/controls.js", "public/javascripts/controls.js" + copy_file "#{app_templates_dir}/public/javascripts/dragdrop.js", "public/javascripts/dragdrop.js" + copy_file "#{app_templates_dir}/public/javascripts/effects.js", "public/javascripts/effects.js" + end + end + + copy_file "#{app_templates_dir}/public/javascripts/application.js", "public/javascripts/application.js" + end + def script(force = false) directory "script", :force => force do |content| "#{shebang}\n" + content @@ -143,6 +166,14 @@ task :default => :test build(:lib) end + def create_public_stylesheets_files + build(:stylesheets) + end + + def create_javascript_files + build(:javascripts) + end + def create_script_files build(:script) end @@ -163,6 +194,10 @@ task :default => :test public_task :apply_rails_template, :bundle_if_dev_or_edge protected + def app_templates_dir + "../../app/templates" + end + def create_dummy_app(path = nil) dummy_path(path) if path diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index 0d24821ff6..e146cb2bb3 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -91,6 +91,35 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase assert_file "test/dummy/config/database.yml", /postgres/ end + def test_skipping_javascripts_without_mountable_option + run_generator + assert_no_file "public/javascripts/prototype.js" + assert_no_file "public/javascripts/rails.js" + assert_no_file "public/javascripts/controls.js" + assert_no_file "public/javascripts/dragdrop.js" + assert_no_file "public/javascripts/dragdrop.js" + assert_no_file "public/javascripts/application.js" + end + + def test_javascripts_generation + run_generator [destination_root, "--mountable"] + assert_file "public/javascripts/rails.js" + assert_file "public/javascripts/prototype.js" + assert_file "public/javascripts/controls.js" + assert_file "public/javascripts/dragdrop.js" + assert_file "public/javascripts/dragdrop.js" + assert_file "public/javascripts/application.js" + end + + def test_skip_javascripts + run_generator [destination_root, "--skip-javascript", "--mountable"] + assert_no_file "public/javascripts/prototype.js" + assert_no_file "public/javascripts/rails.js" + assert_no_file "public/javascripts/controls.js" + assert_no_file "public/javascripts/dragdrop.js" + assert_no_file "public/javascripts/dragdrop.js" + end + def test_ensure_that_javascript_option_is_passed_to_app_generator run_generator [destination_root, "--javascript", "jquery"] assert_file "test/dummy/public/javascripts/jquery.js" -- cgit v1.2.3 From 779a60dee553bda9a5238ac658d68f5022cf9810 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 15 Dec 2010 12:56:29 +0100 Subject: Fix generation of prototype files, it should work with --javascript prototype, not --prototype --- railties/lib/rails/generators/rails/app/app_generator.rb | 2 +- railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb | 2 +- railties/test/generators/app_generator_test.rb | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index f833b5d041..d25372ea8e 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -109,7 +109,7 @@ module Rails copy_file "public/javascripts/#{@options[:javascript]}.js" copy_file "public/javascripts/#{@options[:javascript]}_ujs.js", "public/javascripts/rails.js" - if options[:prototype] + if options[:javascript] == "prototype" copy_file "public/javascripts/controls.js" copy_file "public/javascripts/dragdrop.js" copy_file "public/javascripts/effects.js" diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 3cd4fa4ba1..47a84e228c 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -101,7 +101,7 @@ task :default => :test copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}.js", "public/javascripts/#{options[:javascript]}.js" copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}_ujs.js", "public/javascripts/rails.js" - if options[:prototype] + if options[:javascript] == "prototype" copy_file "#{app_templates_dir}/public/javascripts/controls.js", "public/javascripts/controls.js" copy_file "#{app_templates_dir}/public/javascripts/dragdrop.js", "public/javascripts/dragdrop.js" copy_file "#{app_templates_dir}/public/javascripts/effects.js", "public/javascripts/effects.js" diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 7faa674a81..02c49ab241 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -135,6 +135,9 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "public/javascripts/application.js" assert_file "public/javascripts/prototype.js" assert_file "public/javascripts/rails.js" + assert_file "public/javascripts/controls.js" + assert_file "public/javascripts/dragdrop.js" + assert_file "public/javascripts/dragdrop.js" assert_file "test" end -- cgit v1.2.3 From 232e37952d90a115273c798b8208c1c563a2c9d4 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 15 Dec 2010 12:20:48 +0100 Subject: Generate default layout in engine with --mountable option --- railties/lib/rails/generators/rails/app/app_generator.rb | 1 + .../rails/app/templates/app/views/layouts/application.html.erb.tt | 2 +- .../lib/rails/generators/rails/plugin_new/plugin_new_generator.rb | 6 +++++- railties/test/generators/plugin_new_generator_test.rb | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index d25372ea8e..d842797883 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -284,6 +284,7 @@ module Rails def app_const_base @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, '_').squeeze('_').camelize end + alias :camelized :app_const_base def app_const @app_const ||= "#{app_const_base}::Application" diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt index 1de78eecae..6d56c331c1 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt @@ -1,7 +1,7 @@ - <%= app_const_base %> + <%= camelized %> <%%= stylesheet_link_tag :all %> <%%= javascript_include_tag :defaults %> <%%= csrf_meta_tags %> diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 47a84e228c..9461589ff5 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -8,7 +8,11 @@ module Rails end def app - directory "app" if options[:mountable] + if options[:mountable] + directory "app" + template "#{app_templates_dir}/app/views/layouts/application.html.erb.tt", + "app/views/layouts/#{name}/application.html.erb" + end end def readme diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index e146cb2bb3..2a9e8046b8 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -166,6 +166,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase assert_file "test/dummy/config/routes.rb", /mount Bukkits::Engine => "\/bukkits"/ assert_file "app/controllers/bukkits/application_controller.rb", /module Bukkits\n class ApplicationController < ActionController::Base/ assert_file "app/helpers/bukkits/application_helper.rb", /module Bukkits\n module ApplicationHelper/ + assert_file "app/views/layouts/bukkits/application.html.erb", /Bukkits<\/title>/ end def test_passing_dummy_path_as_a_parameter -- cgit v1.2.3 From 2dafb14c287af2970ceb45ded9ab4e5ce109738f Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 12:21:46 +0100 Subject: Kill whitespace! --- railties/lib/rails/generators/rails/app/app_generator.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index d842797883..94af9fe1e9 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -104,18 +104,18 @@ module Rails def javascripts empty_directory "public/javascripts" - + unless options[:skip_javascript] copy_file "public/javascripts/#{@options[:javascript]}.js" copy_file "public/javascripts/#{@options[:javascript]}_ujs.js", "public/javascripts/rails.js" - + if options[:javascript] == "prototype" copy_file "public/javascripts/controls.js" copy_file "public/javascripts/dragdrop.js" copy_file "public/javascripts/effects.js" end end - + copy_file "public/javascripts/application.js" end -- cgit v1.2.3 From 17afec0ae3fe9d8895a7e030f6bfb8443ae83be1 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 13:08:49 +0100 Subject: AppGenerator: use options instead of @options --- railties/lib/rails/generators/rails/app/app_generator.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 94af9fe1e9..3f6ff35a86 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -63,7 +63,7 @@ module Rails end def database_yml - template "config/databases/#{@options[:database]}.yml", "config/database.yml" + template "config/databases/#{options[:database]}.yml", "config/database.yml" end def db @@ -106,8 +106,8 @@ module Rails empty_directory "public/javascripts" unless options[:skip_javascript] - copy_file "public/javascripts/#{@options[:javascript]}.js" - copy_file "public/javascripts/#{@options[:javascript]}_ujs.js", "public/javascripts/rails.js" + copy_file "public/javascripts/#{options[:javascript]}.js" + copy_file "public/javascripts/#{options[:javascript]}_ujs.js", "public/javascripts/rails.js" if options[:javascript] == "prototype" copy_file "public/javascripts/controls.js" -- cgit v1.2.3 From 439c23dce33148064c258eaf6e79f9d4563c88a4 Mon Sep 17 00:00:00 2001 From: brainopia <ravwar@gmail.com> Date: Thu, 9 Dec 2010 18:38:52 +0300 Subject: Fix edge cases for domain :all option on cookie store Dont set explicit domain for cookies if host is not a domain name [#6002 state:committed] Signed-off-by: Santiago Pastorino <santiago@wyeworks.com> --- .../lib/action_dispatch/middleware/cookies.rb | 7 +++++-- actionpack/test/dispatch/cookies_test.rb | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index b0a4e3d949..f369d2d3c2 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -131,8 +131,11 @@ module ActionDispatch options[:path] ||= "/" if options[:domain] == :all - @host =~ DOMAIN_REGEXP - options[:domain] = ".#{$1}.#{$2}" + # if host is not ip and matches domain regexp + # (ip confirms to domain regexp so we explicitly check for ip) + options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ DOMAIN_REGEXP) + ".#{$1}.#{$2}" + end end end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 5ec7f12cc1..e2040401c7 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -295,6 +295,27 @@ class CookiesTest < ActionController::TestCase assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/" end + def test_cookie_with_all_domain_option_using_localhost + @request.host = "localhost" + get :set_cookie_with_domain + assert_response :success + assert_cookie_header "user_name=rizwanreza; path=/" + end + + def test_cookie_with_all_domain_option_using_ipv4_address + @request.host = "192.168.1.1" + get :set_cookie_with_domain + assert_response :success + assert_cookie_header "user_name=rizwanreza; path=/" + end + + def test_cookie_with_all_domain_option_using_ipv6_address + @request.host = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + get :set_cookie_with_domain + assert_response :success + assert_cookie_header "user_name=rizwanreza; path=/" + end + def test_deleting_cookie_with_all_domain_option get :delete_cookie_with_domain assert_response :success -- cgit v1.2.3 From e15a55c862c98d05ff95e7fa3162ed1298ba8be7 Mon Sep 17 00:00:00 2001 From: Vijay Dev <vijaydev.cse@gmail.com> Date: Wed, 15 Dec 2010 22:35:09 +0530 Subject: filter_parameter_logging is deprecated in Rails 3. Changed it to config.filter_parameters --- railties/guides/source/security.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 528c8861d4..9963106ff3 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -524,10 +524,10 @@ h4. Logging -- _Tell Rails not to put passwords in the log files._ -By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers et cetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can _(highlight)filter certain request parameters from your log files_ by the filter_parameter_logging method in a controller. These parameters will be marked [FILTERED] in the log. +By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers et cetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can _(highlight)filter certain request parameters from your log files_ by appending them to config.filter_parameters in the application configuration. These parameters will be marked [FILTERED] in the log. <ruby> -filter_parameter_logging :password +config.filter_parameters << :password </ruby> h4. Good Passwords -- cgit v1.2.3 From 7a028eae7914f8099b4369b51c2d06a47ecdab9e Mon Sep 17 00:00:00 2001 From: Vijay Dev <vijaydev.cse@gmail.com> Date: Wed, 15 Dec 2010 23:13:22 +0530 Subject: fixed font --- railties/guides/source/security.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 9963106ff3..fbafc40d93 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -524,7 +524,7 @@ h4. Logging -- _Tell Rails not to put passwords in the log files._ -By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers et cetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can _(highlight)filter certain request parameters from your log files_ by appending them to config.filter_parameters in the application configuration. These parameters will be marked [FILTERED] in the log. +By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers et cetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can _(highlight)filter certain request parameters from your log files_ by appending them to <tt>config.filter_parameters</tt> in the application configuration. These parameters will be marked [FILTERED] in the log. <ruby> config.filter_parameters << :password -- cgit v1.2.3 From 80382c7465af27194dc9644b8ba4c4ad458b7ef7 Mon Sep 17 00:00:00 2001 From: Vijay Dev <vijaydev.cse@gmail.com> Date: Thu, 16 Dec 2010 00:16:28 +0530 Subject: fixed link to ruby-prof --- railties/guides/source/performance_testing.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index 41bdd27e9b..b9401f3559 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -398,7 +398,7 @@ $ rails benchmarker 'Item.first' 'Item.last' h4. +profiler+ -+profiler+ is a wrapper around http://ruby-prof.rubyforge.org/[ruby-prof] gem. ++profiler+ is a wrapper around the "ruby-prof":http://ruby-prof.rubyforge.org gem. Usage: -- cgit v1.2.3 From 16e93f2c3c3ca37d2bae9801b680b05f75c48c18 Mon Sep 17 00:00:00 2001 From: Jon Leighton <j@jonathanleighton.com> Date: Sun, 12 Dec 2010 16:35:27 +0000 Subject: Respect the default_scope on a join model when reading a through association --- .../active_record/associations/has_many_through_association.rb | 2 +- .../active_record/associations/has_one_through_association.rb | 2 +- .../active_record/associations/through_association_scope.rb | 7 +++++++ .../cases/associations/has_many_through_associations_test.rb | 4 ++++ .../cases/associations/has_one_through_associations_test.rb | 10 +++++++++- activerecord/test/models/author.rb | 4 ++++ activerecord/test/models/contract.rb | 2 +- activerecord/test/models/post.rb | 7 +++++++ 8 files changed, 34 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 6ad51e2fb4..781aa7ef62 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -81,7 +81,7 @@ module ActiveRecord def find_target return [] unless target_reflection_has_associated_record? - with_scope(@scope) { @reflection.klass.find(:all) } + scoped.all end def has_cached_counter? diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb index 7f28abf464..e8cf73976b 100644 --- a/activerecord/lib/active_record/associations/has_one_through_association.rb +++ b/activerecord/lib/active_record/associations/has_one_through_association.rb @@ -33,7 +33,7 @@ module ActiveRecord private def find_target - with_scope(@scope) { @reflection.klass.find(:first) } + scoped.first end end end diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index a55bf6323e..2ecd0f054a 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -3,6 +3,13 @@ module ActiveRecord module Associations module ThroughAssociationScope + def scoped + with_scope(@scope) do + @reflection.klass.scoped & + @reflection.through_reflection.klass.scoped + end + end + protected def construct_find_scope diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 34ae297275..113d0f6d73 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -462,4 +462,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase post.people << people(:michael) assert_equal readers + 1, post.readers.size end + + def test_has_many_through_with_default_scope_on_join_model + assert_equal posts(:welcome).comments, authors(:david).comments_on_first_posts + end end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index ac43e571cb..93a4f498d0 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -9,9 +9,13 @@ require 'models/member_detail' require 'models/minivan' require 'models/dashboard' require 'models/speedometer' +require 'models/author' +require 'models/post' +require 'models/comment' class HasOneThroughAssociationsTest < ActiveRecord::TestCase - fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans, :dashboards, :speedometers + fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans, + :dashboards, :speedometers, :authors, :posts, :comments def setup @member = members(:groucho) @@ -229,4 +233,8 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase minivan.dashboard end end + + def test_has_one_through_with_default_scope_on_join_model + assert_equal posts(:welcome).comments.first, authors(:david).comment_on_first_posts + end end diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 34bfd2d881..29ee50e801 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -26,6 +26,10 @@ class Author < ActiveRecord::Base has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'" has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post + has_many :first_posts + has_many :comments_on_first_posts, :through => :first_posts, :source => :comments, :order => 'posts.id desc, comments.id asc' + has_one :comment_on_first_posts, :through => :first_posts, :source => :comments, :order => 'posts.id desc, comments.id asc' + has_many :thinking_posts, :class_name => 'Post', :conditions => { :title => 'So I was thinking' }, :dependent => :delete_all has_many :welcome_posts, :class_name => 'Post', :conditions => { :title => 'Welcome to the weblog' } diff --git a/activerecord/test/models/contract.rb b/activerecord/test/models/contract.rb index 606c99cd4e..94fd48e12a 100644 --- a/activerecord/test/models/contract.rb +++ b/activerecord/test/models/contract.rb @@ -1,4 +1,4 @@ class Contract < ActiveRecord::Base belongs_to :company belongs_to :developer -end \ No newline at end of file +end diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 61e782ff14..164b499bf0 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -119,3 +119,10 @@ class PostForAuthor < ActiveRecord::Base cattr_accessor :selected_author default_scope lambda { where(:author_id => PostForAuthor.selected_author) } end + +class FirstPost < ActiveRecord::Base + self.table_name = 'posts' + default_scope where(:id => 1) + has_many :comments, :foreign_key => :post_id + has_one :comment, :foreign_key => :post_id +end -- cgit v1.2.3 From a04e1036691ca98579dd8b8b6cb19ffb23f85c72 Mon Sep 17 00:00:00 2001 From: Jon Leighton <j@jonathanleighton.com> Date: Sun, 12 Dec 2010 17:03:41 +0000 Subject: Verify that creating a has_many through record where there is a default_scope on the join model works correctly (creates the join record with the default scope applied) --- .../cases/associations/has_many_through_associations_test.rb | 7 +++++++ activerecord/test/models/author.rb | 4 ++++ activerecord/test/models/categorization.rb | 11 ++++++++++- activerecord/test/schema/schema.rb | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 113d0f6d73..44ff01ddc0 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -17,6 +17,8 @@ require 'models/developer' require 'models/subscriber' require 'models/book' require 'models/subscription' +require 'models/categorization' +require 'models/category' class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, @@ -466,4 +468,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_with_default_scope_on_join_model assert_equal posts(:welcome).comments, authors(:david).comments_on_first_posts end + + def test_create_has_many_through_with_default_scope_on_join_model + category = authors(:david).special_categories.create(:name => "Foo") + assert_equal 1, category.categorizations.where(:special => true).count + end end diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 29ee50e801..fd6d2b384a 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -77,6 +77,10 @@ class Author < ActiveRecord::Base has_many :categorizations has_many :categories, :through => :categorizations + has_many :special_categorizations + has_many :special_categories, :through => :special_categorizations, :source => :category + has_one :special_category, :through => :special_categorizations, :source => :category + has_many :categories_like_general, :through => :categorizations, :source => :category, :class_name => 'Category', :conditions => { :name => 'General' } has_many :categorized_posts, :through => :categorizations, :source => :post diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb index 10594323ff..b3fc29fa15 100644 --- a/activerecord/test/models/categorization.rb +++ b/activerecord/test/models/categorization.rb @@ -2,4 +2,13 @@ class Categorization < ActiveRecord::Base belongs_to :post belongs_to :category belongs_to :author -end \ No newline at end of file +end + +class SpecialCategorization < ActiveRecord::Base + self.table_name = 'categorizations' + + default_scope where(:special => true) + + belongs_to :author + belongs_to :category +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index d4eb56c4d7..177045ac51 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -112,6 +112,7 @@ ActiveRecord::Schema.define do t.column :category_id, :integer t.column :post_id, :integer t.column :author_id, :integer + t.column :special, :boolean end create_table :citations, :force => true do |t| -- cgit v1.2.3 From 5d78b4c6f7829498e5d2a8cd4fceca0e24a3f64e Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Wed, 15 Dec 2010 11:12:33 -0800 Subject: make sure that join nodes are uniq --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 8e50f4a9bc..6660df302b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -282,7 +282,7 @@ module ActiveRecord association.join_to(manager) end - manager.join_sources.concat join_nodes + manager.join_sources.concat join_nodes.uniq manager.join_sources.concat join_list manager -- cgit v1.2.3 From 6df88f0d2c5fb65f5b94b6c553b000098b9f2eee Mon Sep 17 00:00:00 2001 From: Pivotal Labs <pivotal@lapidge.flood.pivotallabs.com> Date: Fri, 9 Jan 2009 12:13:17 -0800 Subject: test for eager load of has_one association with condition on the through table --- activerecord/test/cases/associations/eager_test.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index c96ca90750..34a1cdeebe 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -17,18 +17,26 @@ require 'models/subscription' require 'models/book' require 'models/developer' require 'models/project' +require 'models/member' +require 'models/membership' +require 'models/club' class EagerAssociationTest < ActiveRecord::TestCase fixtures :posts, :comments, :authors, :author_addresses, :categories, :categories_posts, :companies, :accounts, :tags, :taggings, :people, :readers, :owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books, - :developers, :projects, :developers_projects + :developers, :projects, :developers_projects, :members, :memberships, :clubs def setup # preheat table existence caches Comment.find_by_id(1) end + def test_eager_with_has_one_through_join_model_with_conditions_on_the_through + member = Member.find(members(:some_other_guy).id, :include => :favourite_club) + assert_nil member.favourite_club + end + def test_loading_with_one_association posts = Post.find(:all, :include => :comments) post = posts.find { |p| p.id == 1 } -- cgit v1.2.3 From ae7910fad44ff05cc9db6e1d13cd57c844e53e13 Mon Sep 17 00:00:00 2001 From: Vijay Dev <vijaydev.cse@gmail.com> Date: Thu, 16 Dec 2010 00:54:15 +0530 Subject: minor formatting fixes --- railties/guides/source/configuring.textile | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 44f7a61a77..33319fa40a 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -42,9 +42,9 @@ h4. Rails General Configuration * +config.after_initialize+ takes a block which will be ran _after_ Rails has finished initializing. Useful for configuring values set up by other initializers: <ruby> - config.after_initialize do - ActionView::Base.sanitized_allowed_tags.delete 'div' - end +config.after_initialize do + ActionView::Base.sanitized_allowed_tags.delete 'div' +end </ruby> * +config.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Can also be enabled with +threadsafe!+. @@ -115,7 +115,7 @@ WARNING: Threadsafe operation is incompatible with the normal workings of develo * +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record. -* +config.whiny_nils+ enables or disabled warnings when an methods of nil are invoked. Defaults to _false_. +* +config.whiny_nils+ enables or disables warnings when any methods of nil are invoked. Defaults to _true_ in development and test environments. h4. Configuring Generators @@ -131,12 +131,12 @@ Rails 3 allows you to alter what generators are used with the +config.generators The full set of methods that can be used in this block are as follows: * +force_plural+ allows pluralized model names. Defaults to _false_. -* +helper+ defines whether or not to generate helpers. Defaults to _true_ +* +helper+ defines whether or not to generate helpers. Defaults to _true_. * +orm+ defines which orm to use. Defaults to _nil_, so will use Active Record by default. -* +integration_tool+ defines which integration tool to use. Defaults to _nil_ -* +performance_tool+ defines which performance tool to use. Defaults to _nil_ +* +integration_tool+ defines which integration tool to use. Defaults to _nil_. +* +performance_tool+ defines which performance tool to use. Defaults to _nil_. * +resource_controller+ defines which generator to use for generating a controller when using +rails generate resource+. Defaults to +:controller+. -* +scaffold_controller+ different from +resource_controller+, defines which generator to use for generating a _scaffolded_ controller when using +rails generate scaffold+. Defaults to +:scaffold_controller+ +* +scaffold_controller+ different from +resource_controller+, defines which generator to use for generating a _scaffolded_ controller when using +rails generate scaffold+. Defaults to +:scaffold_controller+. * +stylesheets+ turns on the hook for stylesheets in generators. Used in Rails for when the +scaffold+ generator is ran, but this hook can be used in other generates as well. * +test_framework+ defines which test framework to use. Defaults to _nil_, so will use Test::Unit by default. * +template_engine+ defines which template engine to use, such as ERB or Haml. Defaults to +:erb+. @@ -234,7 +234,7 @@ h4. Configuring Action Controller * +config.action_controller.asset_path+ takes a block which configures where assets can be found. Shorter version of +config.action_controller.asset_path+. -* +config.action_controller.page_cache_directory+ should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>. For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>Rails.root + "/public"</tt>). Changing this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your web server to look in the new location for cached files. +* +config.action_controller.page_cache_directory+ should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>. For Rails, this directory has already been set to +Rails.public_path+ (which is usually set to <tt>Rails.root + "/public"</tt>). Changing this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your web server to look in the new location for cached files. * +config.action_controller.page_cache_extension+ configures the extension used for cached pages saved to +page_cache_directory+. Defaults to +.html+ @@ -252,7 +252,7 @@ h4. Configuring Action Controller The caching code adds two additional settings: -* +ActionController::Base.page_cache_directory+ sets the directory where Rails will create cached pages for your web server. The default is +Rails.public_path+ (which is usually set to +Rails.root + "/public"+). +* +ActionController::Base.page_cache_directory+ sets the directory where Rails will create cached pages for your web server. The default is +Rails.public_path+ (which is usually set to <tt>Rails.root + "/public"</tt>). * +ActionController::Base.page_cache_extension+ sets the extension to be used when generating pages for the cache (this is ignored if the incoming request already has an extension). The default is +.html+. @@ -280,7 +280,7 @@ h4. Configuring Action View There are only a few configuration options for Action View, starting with four on +ActionView::Base+: -* +config.action_view.debug_rjs+ specifies whether RJS responses should be wrapped in a try/catch block that alert()s the caught exception (and then re-raises it). The default is +false+. +* +config.action_view.debug_rjs+ specifies whether RJS responses should be wrapped in a try/catch block that alerts the caught exception (and then re-raises it). The default is +false+. * +config.action_view.field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is <tt>Proc.new{ |html_tag, instance| %Q(%<div class="field_with_errors">#{html_tag}</div>).html_safe }</tt> @@ -290,7 +290,7 @@ There are only a few configuration options for Action View, starting with four o * +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information. -* +config.action_view.javascript_expansions+ a hash containining expansions that can be used for javascript include tag. By default, this is defined as: +* +config.action_view.javascript_expansions+ is a hash containing expansions that can be used for the JavaScript include tag. By default, this is defined as: <ruby> config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] } @@ -302,7 +302,7 @@ However, you may add to this by defining others: config.action_view.javascript_expansions[:jquery] = ["jquery", "jquery-ui"] </ruby> -Then this can be referenced in the view with the following code: +And can reference in the view with the following code: <ruby> <%= javascript_include_tag :jquery %> @@ -374,9 +374,9 @@ h3. Rails Environment Settings Some parts of Rails can also be configured externally by supplying environment variables. The following environment variables are recognized by various parts of Rails: -* +ENV['RAILS_ENV']+ defines the Rails environment (production, development, test, and so on) that Rails will run under. +* +ENV["RAILS_ENV"]+ defines the Rails environment (production, development, test, and so on) that Rails will run under. -* +ENV['RAILS_RELATIVE_URL_ROOT']+ is used by the routing code to recognize URLs when you deploy your application to a subdirectory. +* +ENV["RAILS_RELATIVE_URL_ROOT"]+ is used by the routing code to recognize URLs when you deploy your application to a subdirectory. * +ENV["RAILS_ASSET_ID"]+ will override the default cache-busting timestamps that Rails generates for downloadable assets. @@ -404,9 +404,9 @@ h4. +Rails::Railtie#initializer+ Rails has several initializers that run on startup that are all defined by using the +initializer+ method from +Rails::Railtie+. Here's an example of the +initialize_whiny_nils+ initializer from Active Support: <ruby> - initializer "active_support.initialize_whiny_nils" do |app| - require 'active_support/whiny_nil' if app.config.whiny_nils - end +initializer "active_support.initialize_whiny_nils" do |app| + require 'active_support/whiny_nil' if app.config.whiny_nils +end </ruby> The +initializer+ method takes three arguments with the first being the name for the initializer and the second being an options hash (not shown here) and the third being a block. The +:before+ key in the options hash can be specified to specify which initializer this new initializer must run before, and the +:after+ key will specify which initializer to run this initializer _after_. @@ -442,7 +442,7 @@ Serves as a placeholder so that +:load_environment_config+ can be defined to run *+i18n.callbacks+* In the development environment, sets up a +to_prepare+ callback which will call +I18n.reload!+ if any of the locales have changed since the last request. In production mode this callback will only run on the first request. -*+active_support.initialize_whiny_nils+* Will require +active_support/whiny_nil+ if +config.whiny_nil+ is set to +true+. This file will output errors such as: +*+active_support.initialize_whiny_nils+* Will require +active_support/whiny_nil+ if +config.whiny_nils+ is set to +true+. This file will output errors such as: <plain> Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id -- cgit v1.2.3 From e9732c75a819bd36ae6a47dd2b19f6cb0db4d238 Mon Sep 17 00:00:00 2001 From: Vijay Dev <vijaydev.cse@gmail.com> Date: Thu, 16 Dec 2010 00:56:02 +0530 Subject: fixed typos --- railties/guides/source/debugging_rails_applications.textile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile index 6613fad406..2c8a6619c2 100644 --- a/railties/guides/source/debugging_rails_applications.textile +++ b/railties/guides/source/debugging_rails_applications.textile @@ -48,7 +48,7 @@ Title: Rails debugging guide h4. +to_yaml+ -Displaying an instance variable, or any other object or method, in yaml format can be achieved this way: +Displaying an instance variable, or any other object or method, in YAML format can be achieved this way: <html> <%= simple_format @post.to_yaml %> @@ -122,7 +122,7 @@ It can also be useful to save information to log files at runtime. Rails maintai h4. What is the Logger? -Rails makes use of Ruby's standard +logger+ to write log information. You can also substitute another logger such as +Log4R+ if you wish. +Rails makes use of Ruby's standard +logger+ to write log information. You can also substitute another logger such as +Log4r+ if you wish. You can specify an alternative logger in your +environment.rb+ or any environment file: @@ -178,7 +178,7 @@ class PostsController < ApplicationController if @post.save flash[:notice] = 'Post was successfully created.' - logger.debug "The post was saved and now is the user is going to be redirected..." + logger.debug "The post was saved and now the user is going to be redirected..." redirect_to(@post) else render :action => "new" @@ -204,7 +204,7 @@ Post should be valid: true Post Create (0.000443) INSERT INTO "posts" ("updated_at", "title", "body", "published", "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails', 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54') -The post was saved and now is the user is going to be redirected... +The post was saved and now the user is going to be redirected... Redirected to #<Post:0x20af760> Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/posts] </shell> @@ -601,7 +601,7 @@ There are some settings that can be configured in ruby-debug to make it easier t You can see the full list by using +help set+. Use +help set _subcommand_+ to learn about a particular +set+ command. -TIP: You can include any number of these configuration lines inside a +.rdebugrc+ file in your HOME directory. ruby-debug will read this file every time it is loaded. and configure itself accordingly. +TIP: You can include any number of these configuration lines inside a +.rdebugrc+ file in your HOME directory. ruby-debug will read this file every time it is loaded and configure itself accordingly. Here's a good start for an +.rdebugrc+: @@ -615,7 +615,7 @@ h3. Debugging Memory Leaks A Ruby application (on Rails or not), can leak memory - either in the Ruby code or at the C code level. -In this section, you will learn how to find and fix such leaks by using Bleak House and Valgrind debugging tools. +In this section, you will learn how to find and fix such leaks by using tools such as BleakHouse and Valgrind. h4. BleakHouse -- cgit v1.2.3 From cea73f82ad65bf8bca707f6f8f3afd86f3e82bdb Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Fri, 10 Dec 2010 14:02:14 -0800 Subject: just mutate the ast, fewer lasgns --- activerecord/lib/active_record/relation/query_methods.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 08b61c9752..dce9ac4808 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -225,14 +225,13 @@ module ActiveRecord test = eqls.inject(eqls.shift) do |memo, expr| memo.or(expr) end - arel = arel.where(test) + arel.where(test) end (wheres - equalities).each do |where| where = Arel.sql(where) if String === where - arel = arel.where(Arel::Nodes::Grouping.new(where)) + arel.where(Arel::Nodes::Grouping.new(where)) end - arel end def build_where(opts, other = []) -- cgit v1.2.3 From 0042054ea873a8d25d9bbdc4c7fe0febe1b76042 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Fri, 10 Dec 2010 14:44:42 -0800 Subject: remove lasgn since AST is mutated --- .../class_methods/join_dependency/join_association.rb | 10 ++++++++-- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 98536d23bc..4839068f6d 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -135,9 +135,15 @@ module ActiveRecord conditions << process_conditions(options[:conditions], aliased_table_name) end - join = relation.join(target_table, join_type) + ands = relation.create_and(conditions) - join.on(*conditions) + join = relation.create_join( + relation.froms.first, + target_table, + relation.create_on(ands), + join_type) + + relation.from join end def join_has_and_belongs_to_many_to(relation) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index dce9ac4808..51a39be065 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -266,7 +266,7 @@ module ActiveRecord # FIXME: refactor this to build an AST join_dependency.join_associations.each do |association| - manager = association.join_to(manager) + association.join_to(manager) end return manager unless join_ast -- cgit v1.2.3 From b1447d902c39a4b324cb0dc54b80fecc0fe7e34e Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Fri, 10 Dec 2010 15:13:51 -0800 Subject: explicitly set prefix --- .../associations/class_methods/join_dependency/join_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index 4839068f6d..f1b923e98b 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -22,7 +22,7 @@ module ActiveRecord attr_reader :aliased_prefix, :aliased_table_name delegate :options, :through_reflection, :source_reflection, :to => :reflection - delegate :table, :table_name, :to => :parent, :prefix => true + delegate :table, :table_name, :to => :parent, :prefix => :parent def initialize(reflection, join_dependency, parent = nil) reflection.check_validity! -- cgit v1.2.3 From 59ec13155581918fe934890ff7d9e6c2c03dc92e Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Fri, 10 Dec 2010 15:27:10 -0800 Subject: move ivar to initialize, use triple dot rather than minus --- .../associations/class_methods/join_dependency/join_association.rb | 2 +- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index f1b923e98b..c552603097 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -37,6 +37,7 @@ module ActiveRecord @join_dependency = join_dependency @parent = parent @join_type = Arel::InnerJoin + @aliased_prefix = "t#{ join_dependency.join_parts.size }" # This must be done eagerly upon initialisation because the alias which is produced # depends on the state of the join dependency, but we want it to work the same way @@ -97,7 +98,6 @@ module ActiveRecord private def allocate_aliases - @aliased_prefix = "t#{ join_dependency.join_parts.size }" @aliased_table_name = aliased_table_name_for(table_name) if reflection.macro == :has_and_belongs_to_many diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index d4aefada22..5b9c48bafa 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -12,7 +12,7 @@ module ActiveRecord # Truncates a table alias according to the limits of the current adapter. def table_alias_for(table_name) - table_name[0..table_alias_length-1].gsub(/\./, '_') + table_name[0...table_alias_length].gsub(/\./, '_') end # def tables(name = nil) end -- cgit v1.2.3 From b3ae67dbb91a79f0f58528ba0ba4ffe592db1924 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Fri, 10 Dec 2010 16:26:43 -0800 Subject: reduce method calls --- activerecord/lib/active_record/reflection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index a07c321960..fe4b518826 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -248,7 +248,7 @@ module ActiveRecord end def has_inverse? - !@options[:inverse_of].nil? + @options[:inverse_of] end def inverse_of -- cgit v1.2.3 From 59bd9c2a3793febffb919f1555156208825d4a9c Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Fri, 10 Dec 2010 17:01:15 -0800 Subject: combine regexp --- .../lib/active_record/associations/class_methods/join_dependency.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index b6d85a7c7d..6be947df41 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -66,10 +66,8 @@ module ActiveRecord end def count_aliases_from_string(join_sql, name) - # Table names - join_sql.scan(/join(?:\s+\w+)?\s+#{name}\son/).size + - # Table aliases - join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{name}\son/).size + # Table names + table aliases + join_sql.scan(/join(?:\s+\w+)?\s+(\S+\s+)?#{name}\son/).size end def instantiate(rows) -- cgit v1.2.3 From ad47b7bcc11aae7745b39354292b089ae18a8cfc Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Fri, 10 Dec 2010 17:03:32 -0800 Subject: just use the regexp directly --- .../associations/class_methods/join_dependency.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 6be947df41..5a0ff942ca 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -58,18 +58,16 @@ module ActiveRecord when Arel::Table right.name.downcase == name ? 1 : 0 when String - count_aliases_from_string(right.downcase, quoted_name) + # Table names + table aliases + right.downcase.scan( + /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ + ).size else 0 end }.sum end - def count_aliases_from_string(join_sql, name) - # Table names + table aliases - join_sql.scan(/join(?:\s+\w+)?\s+(\S+\s+)?#{name}\son/).size - end - def instantiate(rows) primary_key = join_base.aliased_primary_key parents = {} -- cgit v1.2.3 From edf5b57451796ba40a330748aa7f4c77048b564f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sat, 11 Dec 2010 01:50:21 -0200 Subject: Add named helper output to translated paths example --- railties/guides/source/routing.textile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 74dee60c32..a51f7475ea 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -745,14 +745,14 @@ end Rails now creates routes to the +CategoriesController+. -|_.HTTP verb|_.Path |_.action | -|GET |/kategorien |index | -|GET |/kategorien/neu |new | -|POST |/kategorien |create | -|GET |/kategorien/1 |show | -|GET |/kategorien/:id/bearbeiten |edit | -|PUT |/kategorien/1 |update | -|DELETE |/kategorien/1 |destroy | +|_.HTTP verb|_.Path |_.action | .named helper | +|GET |/kategorien |index | categories_path | +|GET |/kategorien/neu |new | new_category_path | +|POST |/kategorien |create | categories_path | +|GET |/kategorien/1 |show | category_path | +|GET |/kategorien/:id/bearbeiten |edit | edit_category_path | +|PUT |/kategorien/1 |update | category_path | +|DELETE |/kategorien/1 |destroy | category_path | h4. Overriding the Singular Form -- cgit v1.2.3 From 6f8cc0776bfabac12208a5134c6feb26813754dc Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sat, 11 Dec 2010 01:54:59 -0200 Subject: Add underline to header --- railties/guides/source/routing.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index a51f7475ea..759aa7bb90 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -745,7 +745,7 @@ end Rails now creates routes to the +CategoriesController+. -|_.HTTP verb|_.Path |_.action | .named helper | +|_.HTTP verb|_.Path |_.action |_.named helper | |GET |/kategorien |index | categories_path | |GET |/kategorien/neu |new | new_category_path | |POST |/kategorien |create | categories_path | -- cgit v1.2.3 From 4c5805dfabad7de5bc10c9e4a76c260d6e47ba92 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sat, 11 Dec 2010 02:02:09 -0200 Subject: Add named helper to photo controller example --- railties/guides/source/routing.textile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 759aa7bb90..9c1ed74804 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -611,14 +611,14 @@ resources :photos, :controller => "images" will recognize incoming paths beginning with +/photos+ but route to the +Images+ controller: -|_. Verb |_.Path |_.action | -|GET |/photos |index | -|GET |/photos/new |new | -|POST |/photos |create | -|GET |/photos/1 |show | -|GET |/photos/1/edit |edit | -|PUT |/photos/1 |update | -|DELETE |/photos/1 |destroy | +|_. Verb |_.Path |_.action |_.named helper | +|GET |/photos |index | photos_path | +|GET |/photos/new |new | new_photo_path | +|POST |/photos |create | photos_path | +|GET |/photos/1 |show | photo_path | +|GET |/photos/1/edit |edit | edit_photo_path | +|PUT |/photos/1 |update | photo_path | +|DELETE |/photos/1 |destroy | photo_path | NOTE: Use +photos_path+, +new_photos_path+, etc. to generate paths for this resource. -- cgit v1.2.3 From 692b621d2a134ac784719abcb4fd24f1710cd786 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sat, 11 Dec 2010 02:24:07 -0200 Subject: Tables style unification --- railties/guides/source/routing.textile | 126 ++++++++++++++++----------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 9c1ed74804..32346e8f2b 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -76,14 +76,14 @@ resources :photos creates seven different routes in your application, all mapping to the +Photos+ controller: -|_. Verb |_.Path |_.action |_.used for| -|GET |/photos |index |display a list of all photos| -|GET |/photos/new |new |return an HTML form for creating a new photo| -|POST |/photos |create |create a new photo| -|GET |/photos/:id |show |display a specific photo| -|GET |/photos/:id/edit |edit |return an HTML form for editing a photo| -|PUT |/photos/:id |update |update a specific photo| -|DELETE |/photos/:id |destroy |delete a specific photo| +|_. HTTP Verb |_.Path |_.action |_.used for | +|GET |/photos |index |display a list of all photos | +|GET |/photos/new |new |return an HTML form for creating a new photo | +|POST |/photos |create |create a new photo | +|GET |/photos/:id |show |display a specific photo | +|GET |/photos/:id/edit |edit |return an HTML form for editing a photo | +|PUT |/photos/:id |update |update a specific photo | +|DELETE |/photos/:id |destroy |delete a specific photo | h4. Paths and URLs @@ -130,13 +130,13 @@ resource :geocoder creates six different routes in your application, all mapping to the +Geocoders+ controller: -|_. Verb |_.Path |_.action |_.used for| -|GET |/geocoder/new |new |return an HTML form for creating the geocoder| -|POST |/geocoder |create |create the new geocoder| -|GET |/geocoder |show |display the one and only geocoder resource| -|GET |/geocoder/edit |edit |return an HTML form for editing the geocoder| -|PUT |/geocoder |update |update the one and only geocoder resource| -|DELETE |/geocoder |destroy |delete the geocoder resource| +|_.HTTP Verb |_.Path |_.action |_.used for | +|GET |/geocoder/new |new |return an HTML form for creating the geocoder | +|POST |/geocoder |create |create the new geocoder | +|GET |/geocoder |show |display the one and only geocoder resource | +|GET |/geocoder/edit |edit |return an HTML form for editing the geocoder | +|PUT |/geocoder |update |update the one and only geocoder resource | +|DELETE |/geocoder |destroy |delete the geocoder resource | NOTE: Because you might want to use the same controller for a singular route (+/account+) and a plural route (+/accounts/45+), singular resources map to plural controllers. @@ -160,14 +160,14 @@ end This will create a number of routes for each of the +posts+ and +comments+ controller. For +Admin::PostsController+, Rails will create: -|_. Verb |_.Path |_.action |_. helper | -|GET |/admin/posts |index | admin_posts_path | -|GET |/admin/posts/new |new | new_admin_posts_path | -|POST |/admin/posts |create | admin_posts_path | -|GET |/admin/posts/1 |show | admin_post_path(id) | -|GET |/admin/posts/1/edit |edit | edit_admin_post_path(id) | -|PUT |/admin/posts/1 |update | admin_post_path(id) | -|DELETE |/admin/posts/1 |destroy | admin_post_path(id) | +|_.HTTP Verb |_.Path |_.action |_.named helper | +|GET |/admin/posts |index | admin_posts_path | +|GET |/admin/posts/new |new | new_admin_posts_path | +|POST |/admin/posts |create | admin_posts_path | +|GET |/admin/posts/1 |show | admin_post_path(id) | +|GET |/admin/posts/1/edit |edit | edit_admin_post_path(id) | +|PUT |/admin/posts/1 |update | admin_post_path(id) | +|DELETE |/admin/posts/1 |destroy | admin_post_path(id) | If you want to route +/posts+ (without the prefix +/admin+) to +Admin::PostsController+, you could use @@ -199,14 +199,14 @@ resources :posts, :path => "/admin/posts" In each of these cases, the named routes remain the same as if you did not use +scope+. In the last case, the following paths map to +PostsController+: -|_. Verb |_.Path |_.action |_. helper | -|GET |/admin/posts |index | posts_path | -|GET |/admin/posts/new |new | posts_path | -|POST |/admin/posts |create | posts_path | -|GET |/admin/posts/1 |show | post_path(id) | -|GET |/admin/posts/1/edit |edit | edit_post_path(id) | -|PUT |/admin/posts/1 |update | post_path(id) | -|DELETE |/admin/posts/1 |destroy | post_path(id) | +|_.HTTP Verb |_.Path |_.action |_.named helper | +|GET |/admin/posts |index | posts_path | +|GET |/admin/posts/new |new | posts_path | +|POST |/admin/posts |create | posts_path | +|GET |/admin/posts/1 |show | post_path(id) | +|GET |/admin/posts/1/edit |edit | edit_post_path(id) | +|PUT |/admin/posts/1 |update | post_path(id) | +|DELETE |/admin/posts/1 |destroy | post_path(id) | h4. Nested Resources @@ -232,14 +232,14 @@ end In addition to the routes for magazines, this declaration will also route ads to an +AdsController+. The ad URLs require a magazine: -|_.Verb |_.Path |_.action |_.used for| -|GET |/magazines/1/ads |index |display a list of all ads for a specific magazine| -|GET |/magazines/1/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine| -|POST |/magazines/1/ads |create |create a new ad belonging to a specific magazine| -|GET |/magazines/1/ads/1 |show |display a specific ad belonging to a specific magazine| -|GET |/magazines/1/ads/1/edit |edit |return an HTML form for editing an ad belonging to a specific magazine| -|PUT |/magazines/1/ads/1 |update |update a specific ad belonging to a specific magazine| -|DELETE |/magazines/1/ads/1 |destroy |delete a specific ad belonging to a specific magazine| +|_.HTTP Verb |_.Path |_.action |_.used for | +|GET |/magazines/1/ads |index |display a list of all ads for a specific magazine | +|GET |/magazines/1/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine | +|POST |/magazines/1/ads |create |create a new ad belonging to a specific magazine | +|GET |/magazines/1/ads/1 |show |display a specific ad belonging to a specific magazine | +|GET |/magazines/1/ads/1/edit |edit |return an HTML form for editing an ad belonging to a specific magazine | +|PUT |/magazines/1/ads/1 |update |update a specific ad belonging to a specific magazine | +|DELETE |/magazines/1/ads/1 |destroy |delete a specific ad belonging to a specific magazine | This will also create routing helpers such as +magazine_ads_url+ and +edit_magazine_ad_path+. These helpers take an instance of Magazine as the first parameter (+magazine_ads_url(@magazine)+). @@ -611,14 +611,14 @@ resources :photos, :controller => "images" will recognize incoming paths beginning with +/photos+ but route to the +Images+ controller: -|_. Verb |_.Path |_.action |_.named helper | -|GET |/photos |index | photos_path | -|GET |/photos/new |new | new_photo_path | -|POST |/photos |create | photos_path | -|GET |/photos/1 |show | photo_path | -|GET |/photos/1/edit |edit | edit_photo_path | -|PUT |/photos/1 |update | photo_path | -|DELETE |/photos/1 |destroy | photo_path | +|_.HTTP Verb |_.Path |_.action |_.named helper | +|GET |/photos |index | photos_path | +|GET |/photos/new |new | new_photo_path | +|POST |/photos |create | photos_path | +|GET |/photos/1 |show | photo_path(id) | +|GET |/photos/1/edit |edit | edit_photo_path(id) | +|PUT |/photos/1 |update | photo_path(id) | +|DELETE |/photos/1 |destroy | photo_path(id) | NOTE: Use +photos_path+, +new_photos_path+, etc. to generate paths for this resource. @@ -653,14 +653,14 @@ resources :photos, :as => "images" will recognize incoming paths beginning with +/photos+ and route the requests to +PhotosController+, but use the value of the :as option to name the helpers. -|_.HTTP verb|_.Path |_.action |_.named helper | -|GET |/photos |index | images_path | -|GET |/photos/new |new | new_image_path | -|POST |/photos |create | images_path | -|GET |/photos/1 |show | image_path | -|GET |/photos/1/edit |edit | edit_image_path | -|PUT |/photos/1 |update | image_path | -|DELETE |/photos/1 |destroy | image_path | +|_.HTTP verb|_.Path |_.action |_.named helper | +|GET |/photos |index | images_path | +|GET |/photos/new |new | new_image_path | +|POST |/photos |create | images_path | +|GET |/photos/1 |show | image_path(id) | +|GET |/photos/1/edit |edit | edit_image_path(id) | +|PUT |/photos/1 |update | image_path(id) | +|DELETE |/photos/1 |destroy | image_path(id) | h4. Overriding the +new+ and +edit+ Segments @@ -745,14 +745,14 @@ end Rails now creates routes to the +CategoriesController+. -|_.HTTP verb|_.Path |_.action |_.named helper | -|GET |/kategorien |index | categories_path | -|GET |/kategorien/neu |new | new_category_path | -|POST |/kategorien |create | categories_path | -|GET |/kategorien/1 |show | category_path | -|GET |/kategorien/:id/bearbeiten |edit | edit_category_path | -|PUT |/kategorien/1 |update | category_path | -|DELETE |/kategorien/1 |destroy | category_path | +|_.HTTP verb|_.Path |_.action |_.named helper | +|GET |/kategorien |index | categories_path | +|GET |/kategorien/neu |new | new_category_path | +|POST |/kategorien |create | categories_path | +|GET |/kategorien/1 |show | category_path(id) | +|GET |/kategorien/1/bearbeiten |edit | edit_category_path(id) | +|PUT |/kategorien/1 |update | category_path(id) | +|DELETE |/kategorien/1 |destroy | category_path(id) | h4. Overriding the Singular Form -- cgit v1.2.3 From e6928e8c35bfc22425916f334e9482f955bc8907 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Fri, 10 Dec 2010 14:42:10 +0100 Subject: Allow to generate namespaced generators [#6140 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim <jose.valim@gmail.com> --- .../rails/generator/generator_generator.rb | 4 +-- .../test/generators/generator_generator_test.rb | 39 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb index 5b5d1884bc..3e0a442bda 100644 --- a/railties/lib/rails/generators/rails/generator/generator_generator.rb +++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb @@ -14,9 +14,9 @@ module Rails def generator_dir if options[:namespace] - File.join("lib", "generators", file_name) + File.join("lib", "generators", regular_class_path, file_name) else - File.join("lib", "generators") + File.join("lib", "generators", regular_class_path) end end diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb index 26f975a191..f4c975fc18 100644 --- a/railties/test/generators/generator_generator_test.rb +++ b/railties/test/generators/generator_generator_test.rb @@ -17,4 +17,43 @@ class GeneratorGeneratorTest < Rails::Generators::TestCase assert_file "lib/generators/awesome/awesome_generator.rb", /class AwesomeGenerator < Rails::Generators::NamedBase/ end + + def test_namespaced_generator_skeleton + run_generator ["rails/awesome"] + + %w( + lib/generators/rails/awesome + lib/generators/rails/awesome/USAGE + lib/generators/rails/awesome/templates + ).each{ |path| assert_file path } + + assert_file "lib/generators/rails/awesome/awesome_generator.rb", + /class Rails::AwesomeGenerator < Rails::Generators::NamedBase/ + end + + def test_generator_skeleton_is_created_without_file_name_namespace + run_generator ["awesome", "--namespace", "false"] + + %w( + lib/generators/ + lib/generators/USAGE + lib/generators/templates + ).each{ |path| assert_file path } + + assert_file "lib/generators/awesome_generator.rb", + /class AwesomeGenerator < Rails::Generators::NamedBase/ + end + + def test_namespaced_generator_skeleton_without_file_name_namespace + run_generator ["rails/awesome", "--namespace", "false"] + + %w( + lib/generators/rails + lib/generators/rails/USAGE + lib/generators/rails/templates + ).each{ |path| assert_file path } + + assert_file "lib/generators/rails/awesome_generator.rb", + /class Rails::AwesomeGenerator < Rails::Generators::NamedBase/ + end end -- cgit v1.2.3 From 0dc39cd8ee24466f204a292ae61643ce3b3b4c32 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Fri, 10 Dec 2010 22:16:34 +0100 Subject: Skip creating migration if --skip option is passed to model generator [#6144 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim <jose.valim@gmail.com> --- railties/lib/rails/generators/migration.rb | 2 +- railties/test/generators/model_generator_test.rb | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb index 8d98909055..0c5c4f6e09 100644 --- a/railties/lib/rails/generators/migration.rb +++ b/railties/lib/rails/generators/migration.rb @@ -52,7 +52,7 @@ module Rails destination = self.class.migration_exists?(migration_dir, @migration_file_name) - if behavior == :invoke + if !(destination && options[:skip]) && behavior == :invoke if destination && options.force? remove_file(destination) elsif destination diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 8a0f560bc8..552b7eb30a 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -147,10 +147,22 @@ class ModelGeneratorTest < Rails::Generators::TestCase end end - def test_migration_already_exists_error_message + def test_migration_is_skipped_with_skip_option run_generator - error = capture(:stderr){ run_generator ["Account"], :behavior => :skip } - assert_match /Another migration is already named create_accounts/, error + output = run_generator ["Account", "--skip"] + assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output + end + + def test_migration_is_ignored_as_identical_with_skip_option + run_generator ["Account"] + output = run_generator ["Account", "--skip"] + assert_match %r{identical\s+db/migrate/\d+_create_accounts.rb}, output + end + + def test_migration_is_skipped_on_skip_behavior + run_generator + output = run_generator ["Account"], :behavior => :skip + assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output end def test_migration_error_is_not_shown_on_revoke -- cgit v1.2.3 From 972011a2e58be5caf84bf06dd954ba095c823984 Mon Sep 17 00:00:00 2001 From: Samuel Kadolph <samuel@kadolph.com> Date: Thu, 9 Dec 2010 13:30:02 -0500 Subject: Add support for namespaced validators Includes test and documentation for new feature Signed-off-by: Santiago Pastorino <santiago@wyeworks.com> --- activemodel/lib/active_model/validations/validates.rb | 8 +++++++- activemodel/test/cases/validations/validates_test.rb | 8 ++++++++ activemodel/test/validators/namespace/email_validator.rb | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100755 activemodel/test/validators/namespace/email_validator.rb diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index 77c5073c6e..0132f68282 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -55,6 +55,10 @@ module ActiveModel # validates :name, :title => true # end # + # Additionally validator classes may be in another namespace and still used within any class. + # + # validates :name, :'file/title' => true + # # The validators hash can also handle regular expressions, ranges, # arrays and strings in shortcut form, e.g. # @@ -86,8 +90,10 @@ module ActiveModel defaults.merge!(:attributes => attributes) validations.each do |key, options| + key = "#{key.to_s.camelize}Validator" + begin - validator = const_get("#{key.to_s.camelize}Validator") + validator = key.include?('::') ? key.constantize : const_get(key) rescue NameError raise ArgumentError, "Unknown validator: '#{key}'" end diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 666c48c8a0..3a9900939e 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -3,6 +3,7 @@ require 'cases/helper' require 'models/person' require 'models/person_with_validator' require 'validators/email_validator' +require 'validators/namespace/email_validator' class ValidatesTest < ActiveModel::TestCase setup :reset_callbacks @@ -34,6 +35,13 @@ class ValidatesTest < ActiveModel::TestCase assert_equal ['is not an email'], person.errors[:karma] end + def test_validates_with_namespaced_validator_class + Person.validates :karma, :'namespace/email' => true + person = Person.new + person.valid? + assert_equal ['is not an email'], person.errors[:karma] + end + def test_validates_with_if_as_local_conditions Person.validates :karma, :presence => true, :email => { :unless => :condition_is_true } person = Person.new diff --git a/activemodel/test/validators/namespace/email_validator.rb b/activemodel/test/validators/namespace/email_validator.rb new file mode 100755 index 0000000000..57e2793ce2 --- /dev/null +++ b/activemodel/test/validators/namespace/email_validator.rb @@ -0,0 +1,6 @@ +require 'validators/email_validator' + +module Namespace + class EmailValidator < ::EmailValidator + end +end -- cgit v1.2.3 From 9cfbada93a1e838f6d07e46ea5ab2c3da6e08e78 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sat, 11 Dec 2010 16:40:05 -0200 Subject: This is not an executable file --- activemodel/test/validators/namespace/email_validator.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 activemodel/test/validators/namespace/email_validator.rb diff --git a/activemodel/test/validators/namespace/email_validator.rb b/activemodel/test/validators/namespace/email_validator.rb old mode 100755 new mode 100644 -- cgit v1.2.3 From bda16eb0920780509467bcf051d1a01639357903 Mon Sep 17 00:00:00 2001 From: Chiel Wester <chiel.wester@holder.nl> Date: Mon, 13 Dec 2010 15:06:23 +0100 Subject: Only call save on belongs_to associations if the record has changed or any nested associations have changed (resolves #3353) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim <jose.valim@gmail.com> --- activerecord/lib/active_record/autosave_association.rb | 2 +- activerecord/test/cases/autosave_association_test.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index c3dda29d03..4a18719551 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -363,7 +363,7 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy elsif autosave != false - saved = association.save(:validate => !autosave) if association.new_record? || autosave + saved = association.save(:validate => !autosave) if association.new_record? || (autosave && association.changed_for_autosave?) if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index fbf7121468..27aee400f9 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -667,10 +667,21 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end end + @ship.pirate.catchphrase = "Changed Catchphrase" + assert_raise(RuntimeError) { assert !@ship.save } assert_not_nil @ship.reload.pirate end + def test_should_save_changed_child_objects_if_parent_is_saved + @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?") + @parrot = @pirate.parrots.create!(:name => 'Posideons Killer') + @parrot.name = "NewName" + @ship.save + + assert_equal 'NewName', @parrot.reload.name + end + # has_many & has_and_belongs_to %w{ parrots birds }.each do |association_name| define_method("test_should_destroy_#{association_name}_as_part_of_the_save_transaction_if_they_were_marked_for_destroyal") do -- cgit v1.2.3 From a1ca1e85a9ccefd72db4dc700a894c5bd7db53de Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Mon, 13 Dec 2010 11:39:31 -0800 Subject: persisted? should be able to return a truthy object --- activerecord/test/cases/transactions_test.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index b0ccd71836..110a18772f 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -370,23 +370,23 @@ class TransactionTest < ActiveRecord::TestCase assert topic_2.save @first.save @second.destroy - assert_equal true, topic_1.persisted? + assert topic_1.persisted?, 'persisted' assert_not_nil topic_1.id - assert_equal true, topic_2.persisted? + assert topic_2.persisted?, 'persisted' assert_not_nil topic_2.id - assert_equal true, @first.persisted? + assert @first.persisted?, 'persisted' assert_not_nil @first.id - assert_equal true, @second.destroyed? + assert @second.destroyed?, 'destroyed' raise ActiveRecord::Rollback end - assert_equal false, topic_1.persisted? + assert !topic_1.persisted?, 'not persisted' assert_nil topic_1.id - assert_equal false, topic_2.persisted? + assert !topic_2.persisted?, 'not persisted' assert_nil topic_2.id - assert_equal true, @first.persisted? + assert @first.persisted?, 'persisted' assert_not_nil @first.id - assert_equal false, @second.destroyed? + assert !@second.destroyed?, 'not destroyed' end if current_adapter?(:PostgreSQLAdapter) && defined?(PGconn::PQTRANS_IDLE) -- cgit v1.2.3 From 63480d234df401d678bb7c5a8cef662e3222d841 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Mon, 13 Dec 2010 13:21:18 -0800 Subject: stop delegating inserts to ARel, use the INSERT SQL ourselves --- activerecord/lib/active_record/relation.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3009bb70c1..7ecba1c43a 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -11,7 +11,6 @@ module ActiveRecord include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a - delegate :insert, :to => :arel attr_reader :table, :klass, :loaded attr_accessor :extensions @@ -28,6 +27,19 @@ module ActiveRecord @extensions = [] end + def insert(values) + im = arel.compile_insert values + im.into @table + primary_key_name = @klass.primary_key + primary_key_value = Hash === values ? values[primary_key_name] : nil + + @klass.connection.insert( + im.to_sql, + 'SQL', + primary_key_name, + primary_key_value) + end + def new(*args, &block) scoping { @klass.new(*args, &block) } end -- cgit v1.2.3 From f0f978a4f31930777d402965cad55086ebaa46ba Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Mon, 13 Dec 2010 01:28:27 +0100 Subject: Fix indentation and newlines in generated engine --- railties/lib/rails/generators/named_base.rb | 2 +- .../rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index e0dde4360f..44a2639488 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -42,7 +42,7 @@ module Rails end def wrap_with_namespace(content) - content = indent(content) + content = indent(content).chomp "module #{namespace.name}\n#{content}\nend\n" end diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb b/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb index 9600ee0c3f..aa8ea77bae 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb +++ b/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb @@ -1,7 +1,7 @@ module <%= camelized %> class Engine < Rails::Engine <% if mountable? -%> - isolate_namespace <%= camelized %> + isolate_namespace <%= camelized %> <% end -%> end end -- cgit v1.2.3 From 08ccca764498909faa2e4f9c69cee911e6395602 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Mon, 13 Dec 2010 13:49:39 -0800 Subject: fixing whitespace errors --- .../test/cases/associations/inner_join_association_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index 780eabc443..da2a81e98a 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -65,21 +65,21 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase authors_with_welcoming_post_titles = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true, :conditions => "posts.title like 'Welcome%'") assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'" end - + def test_find_with_sti_join scope = Post.joins(:special_comments).where(:id => posts(:sti_comments).id) - + # The join should match SpecialComment and its subclasses only assert scope.where("comments.type" => "Comment").empty? assert !scope.where("comments.type" => "SpecialComment").empty? assert !scope.where("comments.type" => "SubSpecialComment").empty? end - + def test_find_with_conditions_on_reflection assert !posts(:welcome).comments.empty? assert Post.joins(:nonexistant_comments).where(:id => posts(:welcome).id).empty? # [sic!] end - + def test_find_with_conditions_on_through_reflection assert !posts(:welcome).tags.empty? assert Post.joins(:misc_tags).where(:id => posts(:welcome).id).empty? -- cgit v1.2.3 From c02fd2acc575f3e90da12d4915390a80a48b7c8e Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Tue, 14 Dec 2010 11:25:47 -0800 Subject: taking advantage of the JoinSource node in the SQL AST --- .../associations/class_methods/join_dependency.rb | 20 ++++++------------- .../join_dependency/join_association.rb | 1 - .../lib/active_record/relation/finder_methods.rb | 2 +- .../lib/active_record/relation/query_methods.rb | 23 +++++++--------------- 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb index 5a0ff942ca..a74d0a4ca8 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb @@ -47,24 +47,16 @@ module ActiveRecord end def count_aliases_from_table_joins(name) - return 0 if !@table_joins || Arel::Table === @table_joins + return 0 if Arel::Table === @table_joins # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase quoted_name = active_record.connection.quote_table_name(name).downcase - @table_joins.grep(Arel::Nodes::Join).map { |join| - right = join.right - case right - when Arel::Table - right.name.downcase == name ? 1 : 0 - when String - # Table names + table aliases - right.downcase.scan( - /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ - ).size - else - 0 - end + @table_joins.map { |join| + # Table names + table aliases + join.left.downcase.scan( + /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ + ).size }.sum end diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb index c552603097..694778008b 100644 --- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb @@ -138,7 +138,6 @@ module ActiveRecord ands = relation.create_and(conditions) join = relation.create_join( - relation.froms.first, target_table, relation.create_on(ands), join_type) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 906ad7699c..8bc28c06cb 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -187,7 +187,7 @@ module ActiveRecord def find_with_associations including = (@eager_load_values + @includes_values).uniq - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, []) rows = construct_relation_for_association_find(join_dependency).to_a join_dependency.instantiate(rows) rescue ThrowResult diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 51a39be065..67a94cec85 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -191,27 +191,19 @@ module ActiveRecord def custom_join_ast(table, joins) joins = joins.reject { |join| join.blank? } - return if joins.empty? + return [] if joins.empty? @implicit_readonly = true - joins.map! do |join| + joins.map do |join| case join when Array join = Arel.sql(join.join(' ')) if array_of_strings?(join) when String join = Arel.sql(join) end - join + table.create_string_join(join) end - - head = table.create_string_join(table, joins.shift) - - joins.inject(head) do |ast, join| - ast.right = table.create_string_join(ast.right, join) - end - - head end def collapse_wheres(arel, wheres) @@ -256,9 +248,9 @@ module ActiveRecord stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) non_association_joins = (joins - association_joins - stashed_association_joins) - join_ast = custom_join_ast(manager.froms.first, non_association_joins) + join_list = custom_join_ast(manager, non_association_joins) - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_ast) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_list) join_dependency.graft(*stashed_association_joins) @@ -269,10 +261,9 @@ module ActiveRecord association.join_to(manager) end - return manager unless join_ast + return manager unless join_list - join_ast.left = manager.froms.first - manager.from join_ast + join_list.each { |j| manager.from j } manager end -- cgit v1.2.3 From b68407f7f013ce3b08d1273ac3c2ffd7a8a510c9 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Tue, 14 Dec 2010 12:34:04 -0800 Subject: bucketing based on join type --- .../lib/active_record/relation/query_methods.rb | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 67a94cec85..ef14f48a8f 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -239,16 +239,25 @@ module ActiveRecord end def build_joins(manager, joins) - joins = joins.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq - - association_joins = joins.find_all do |join| - [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join) + buckets = joins.group_by do |join| + case join + when String + 'string_join' + when Hash, Symbol, Array + 'association_join' + when ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation + 'stashed_join' + else + raise 'unknown class: %s' % join.class.name + end end - stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) - - non_association_joins = (joins - association_joins - stashed_association_joins) - join_list = custom_join_ast(manager, non_association_joins) + association_joins = buckets['association_join'] || [] + stashed_association_joins = buckets['stashed_join'] || [] + string_joins = (buckets['string_join'] || []).map { |x| + x.strip + }.uniq + join_list = custom_join_ast(manager, string_joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_list) -- cgit v1.2.3 From 80ff64a947b4acd3835faab36e3b7f44ade10170 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Tue, 14 Dec 2010 14:49:33 -0800 Subject: just copy the joins to the list --- .../lib/active_record/associations/has_many_through_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index d0c8af1801..6ad51e2fb4 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -54,7 +54,7 @@ module ActiveRecord end def construct_find_options!(options) - options[:joins] = construct_joins(options[:joins]) + options[:joins] = [construct_joins] + Array.wrap(options[:joins]) options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? && @reflection.source_reflection.options[:include] end -- cgit v1.2.3 From 18402b5d649658a4046769a6d74b76a6731f7c94 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Tue, 14 Dec 2010 14:57:03 -0800 Subject: supporting arel AST nodes when building join statements --- .../associations/through_association_scope.rb | 32 ++++++++++++---------- .../lib/active_record/relation/query_methods.rb | 18 ++++++++++-- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index acddfda924..48386ee124 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -55,33 +55,35 @@ module ActiveRecord end def construct_joins(custom_joins = nil) - polymorphic_join = nil + right = @reflection.through_reflection.klass.arel_table + left = @reflection.klass.arel_table + + conditions = [] + if @reflection.source_reflection.macro == :belongs_to reflection_primary_key = @reflection.klass.primary_key source_primary_key = @reflection.source_reflection.primary_key_name if @reflection.options[:source_type] - polymorphic_join = "AND %s.%s = %s" % [ - @reflection.through_reflection.quoted_table_name, "#{@reflection.source_reflection.options[:foreign_type]}", - @owner.class.quote_value(@reflection.options[:source_type]) - ] + column = @reflection.source_reflection.options[:foreign_type] + conditions << + right[column].eq(@reflection.options[:source_type]) end else reflection_primary_key = @reflection.source_reflection.primary_key_name source_primary_key = @reflection.through_reflection.klass.primary_key if @reflection.source_reflection.options[:as] - polymorphic_join = "AND %s.%s = %s" % [ - @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type", - @owner.class.quote_value(@reflection.through_reflection.klass.name) - ] + column = "#{@reflection.source_reflection.options[:as]}_type" + conditions << + left[column].eq(@reflection.through_reflection.klass.name) end end - "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [ - @reflection.through_reflection.quoted_table_name, - @reflection.quoted_table_name, reflection_primary_key, - @reflection.through_reflection.quoted_table_name, source_primary_key, - polymorphic_join - ] + conditions << + left[reflection_primary_key].eq(right[source_primary_key]) + + right.create_join( + right, + right.create_on(right.create_and(conditions))) end # Construct attributes for associate pointing to owner. diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index ef14f48a8f..8e50f4a9bc 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -247,6 +247,8 @@ module ActiveRecord 'association_join' when ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation 'stashed_join' + when Arel::Nodes::Join + 'join_node' else raise 'unknown class: %s' % join.class.name end @@ -254,12 +256,22 @@ module ActiveRecord association_joins = buckets['association_join'] || [] stashed_association_joins = buckets['stashed_join'] || [] + join_nodes = buckets['join_node'] || [] string_joins = (buckets['string_join'] || []).map { |x| x.strip }.uniq + join_list = custom_join_ast(manager, string_joins) - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_list) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new( + @klass, + association_joins, + join_list + ) + + join_nodes.each do |join| + join_dependency.table_aliases[join.left.name.downcase] = 1 + end join_dependency.graft(*stashed_association_joins) @@ -270,9 +282,9 @@ module ActiveRecord association.join_to(manager) end - return manager unless join_list + manager.join_sources.concat join_nodes + manager.join_sources.concat join_list - join_list.each { |j| manager.from j } manager end -- cgit v1.2.3 From aa4d03fcfc42453b5553c001d24933365effe5f0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Tue, 14 Dec 2010 15:03:06 -0800 Subject: construct_joins no longer needs an argument --- .../lib/active_record/associations/through_association_scope.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 48386ee124..f6b242168c 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -54,7 +54,7 @@ module ActiveRecord custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*" end - def construct_joins(custom_joins = nil) + def construct_joins right = @reflection.through_reflection.klass.arel_table left = @reflection.klass.arel_table -- cgit v1.2.3 From 2f0708e97405fa55e641dd2eac8805f12f878f5a Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Tue, 14 Dec 2010 15:24:25 -0800 Subject: build SQL AST nodes rather than generate strings --- .../associations/through_association_scope.rb | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index f6b242168c..8d701248af 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -23,25 +23,23 @@ module ActiveRecord # Build SQL conditions from attributes, qualified by table name. def construct_conditions - table_name = @reflection.through_reflection.quoted_table_name + table = @reflection.through_reflection.klass.arel_table conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| - "#{table_name}.#{attr} = #{value}" + table[attr].eq(value) end - conditions << sql_conditions if sql_conditions - "(" + conditions.join(') AND (') + ")" + conditions << Arel.sql(sql_conditions) if sql_conditions + table.create_and(conditions) end # Associate attributes pointing to owner, quoted. def construct_quoted_owner_attributes(reflection) if as = reflection.options[:as] - { "#{as}_id" => owner_quoted_id, - "#{as}_type" => reflection.klass.quote_value( - @owner.class.base_class.name.to_s, - reflection.klass.columns_hash["#{as}_type"]) } + { "#{as}_id" => @owner.id, + "#{as}_type" => @owner.class.base_class.name } elsif reflection.macro == :belongs_to - { reflection.klass.primary_key => @owner.class.quote_value(@owner[reflection.primary_key_name]) } + { reflection.klass.primary_key => @owner[reflection.primary_key_name] } else - { reflection.primary_key_name => owner_quoted_id } + { reflection.primary_key_name => @owner.id } end end -- cgit v1.2.3 From a64bf22a718bcc8d1a33234ab8a7cdd91c6d537b Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Tue, 14 Dec 2010 15:27:06 -0800 Subject: class names are already strings, so we do not need to call to_s on the strings that are already strings --- .../lib/active_record/associations/through_association_scope.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 8d701248af..6b7faa9fc3 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -88,7 +88,7 @@ module ActiveRecord def construct_owner_attributes(reflection) if as = reflection.options[:as] { "#{as}_id" => @owner.id, - "#{as}_type" => @owner.class.base_class.name.to_s } + "#{as}_type" => @owner.class.base_class.name } else { reflection.primary_key_name => @owner.id } end @@ -102,7 +102,7 @@ module ActiveRecord join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) if @reflection.options[:source_type] - join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s) + join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name) end if @reflection.through_reflection.options[:conditions].is_a?(Hash) -- cgit v1.2.3 From 7bffa9dd01cdef3088e0d33fabd5fc07eb35c9d9 Mon Sep 17 00:00:00 2001 From: Joe Hannon <ziggurism@gmail.com> Date: Sun, 2 May 2010 16:26:02 -0700 Subject: add test which fails for has_many through self join [#4361 state:open] --- .../test/cases/associations/has_many_through_associations_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 52432b0428..fe60e676d6 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -388,6 +388,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) } end + def test_has_many_association_through_a_has_many_association_to_self + sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1) + john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1) + assert_equal sarah.agents, [john] + assert_equal people(:susan).agents_of_agents, [john] + end + def test_collection_singular_ids_getter_with_string_primary_keys book = books(:awdr) assert_equal 2, book.subscriber_ids.size -- cgit v1.2.3 From 5d37ff6bb2b1f948034bf06e450f2fdb8195c4e5 Mon Sep 17 00:00:00 2001 From: Ernie Miller <ernie@metautonomo.us> Date: Mon, 3 May 2010 20:47:42 -0400 Subject: Fix hm:t to self table aliasing in construct_scope --- .../active_record/associations/through_association_scope.rb | 12 ++++++++++-- .../cases/associations/has_many_through_associations_test.rb | 2 +- activerecord/test/models/person.rb | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 6b7faa9fc3..a55bf6323e 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -21,9 +21,17 @@ module ActiveRecord construct_owner_attributes(@reflection) end + def aliased_through_table + name = @reflection.through_reflection.table_name + + @reflection.table_name == name ? + @reflection.through_reflection.klass.arel_table.alias(name + "_join") : + @reflection.through_reflection.klass.arel_table + end + # Build SQL conditions from attributes, qualified by table name. def construct_conditions - table = @reflection.through_reflection.klass.arel_table + table = aliased_through_table conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| table[attr].eq(value) end @@ -53,7 +61,7 @@ module ActiveRecord end def construct_joins - right = @reflection.through_reflection.klass.arel_table + right = aliased_through_table left = @reflection.klass.arel_table conditions = [] diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index fe60e676d6..34ae297275 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -392,7 +392,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1) john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1) assert_equal sarah.agents, [john] - assert_equal people(:susan).agents_of_agents, [john] + assert_equal people(:susan).agents.map(&:agents).flatten, people(:susan).agents_of_agents end def test_collection_singular_ids_getter_with_string_primary_keys diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 951ec93c53..bee89de042 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -12,6 +12,7 @@ class Person < ActiveRecord::Base belongs_to :primary_contact, :class_name => 'Person' has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id' + has_many :agents_of_agents, :through => :agents, :source => :agents belongs_to :number1_fan, :class_name => 'Person' scope :males, :conditions => { :gender => 'M' } -- cgit v1.2.3 From 8e955c5fe4d14bec9da7978b356d5ce3ae469078 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 09:09:29 +0100 Subject: Should be isolated engine instead of namespaced engine --- railties/lib/rails/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index cda0e0a135..1d81e08cd3 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -207,7 +207,7 @@ module Rails # end # end # - # == Namespaced Engine + # == Isolated Engine # # Normally when you create controllers, helpers and models inside engine, they are treated # as they were created inside the application. This means all applications helpers and named routes -- cgit v1.2.3 From a462a76bd323ef0087ead0bf0d1792a95655975c Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 09:01:18 +0100 Subject: Set proper engine's asset directories when assets are served from engine. When using stylesheet_link_tag(:all) or javascript_include_tag(:all), assets directories are searched for css or js files. When config.serve_static_assets is set to true, those files can be served directly from engine's directories. That's why assets paths should be set individually for controllers inside engine if we want to serve static assets with ActionDispatch::Static --- actionpack/lib/action_controller/railties/paths.rb | 8 +++ railties/test/railties/engine_test.rb | 64 ++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb index 699c44c62c..dce3c2fe88 100644 --- a/actionpack/lib/action_controller/railties/paths.rb +++ b/actionpack/lib/action_controller/railties/paths.rb @@ -16,6 +16,14 @@ module ActionController if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers klass.helper :all end + + if app.config.serve_static_assets && namespace + paths = namespace._railtie.config.paths + + klass.config.assets_dir = paths["public"].first + klass.config.javascripts_dir = paths["public/javascripts"].first + klass.config.stylesheets_dir = paths["public/stylesheets"].first + end end end end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 05bd0c36cd..6b64a19741 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -721,5 +721,69 @@ module RailtiesTest engine_path = File.join(@plugin.path, '..', engine_dir) assert_equal Bukkits::Engine.instance, Rails::Engine.find(engine_path) end + + test "ensure that engine properly sets assets directories" do + add_to_config("config.action_dispatch.show_exceptions = false") + add_to_config("config.serve_static_assets = true") + + @plugin.write "lib/bukkits.rb", <<-RUBY + module Bukkits + class Engine < ::Rails::Engine + isolate_namespace Bukkits + end + end + RUBY + + @plugin.write "public/stylesheets/foo.css", "" + @plugin.write "public/javascripts/foo.js", "" + + @plugin.write "app/views/layouts/bukkits/application.html.erb", <<-RUBY + <%= stylesheet_link_tag :all %> + <%= javascript_include_tag :all %> + <%= yield %> + RUBY + + @plugin.write "app/controllers/bukkits/home_controller.rb", <<-RUBY + module Bukkits + class HomeController < ActionController::Base + def index + render :text => "Good morning!", :layout => "bukkits/application" + end + end + end + RUBY + + @plugin.write "config/routes.rb", <<-RUBY + Bukkits::Engine.routes.draw do + match "/home" => "home#index" + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + mount Bukkits::Engine => "/bukkits" + end + RUBY + + require 'rack/test' + extend Rack::Test::Methods + + boot_rails + + require "#{rails_root}/config/environment" + + assert_equal File.join(@plugin.path, "public"), Bukkits::HomeController.assets_dir + assert_equal File.join(@plugin.path, "public/stylesheets"), Bukkits::HomeController.stylesheets_dir + assert_equal File.join(@plugin.path, "public/javascripts"), Bukkits::HomeController.javascripts_dir + + assert_equal File.join(app_path, "public"), ActionController::Base.assets_dir + assert_equal File.join(app_path, "public/stylesheets"), ActionController::Base.stylesheets_dir + assert_equal File.join(app_path, "public/javascripts"), ActionController::Base.javascripts_dir + + get "/bukkits/home" + + assert_match %r{bukkits/stylesheets/foo.css}, last_response.body + assert_match %r{bukkits/javascripts/foo.js}, last_response.body + end end end -- cgit v1.2.3 From 978314c4e1fc6460da887bd850f0df946fe680b7 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 12:20:20 +0100 Subject: Generate javascripts in engine with --mountable option --- railties/lib/rails/generators/app_base.rb | 6 ++++ .../rails/generators/rails/app/app_generator.rb | 5 ---- .../rails/plugin_new/plugin_new_generator.rb | 35 ++++++++++++++++++++++ .../test/generators/plugin_new_generator_test.rb | 29 ++++++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 2d0c10efca..7766050632 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -171,6 +171,12 @@ gem 'rails', '#{Rails::VERSION::STRING}' def dev_or_edge? options.dev? || options.edge? end + + def empty_directory_with_gitkeep(destination, config = {}) + empty_directory(destination, config) + create_file("#{destination}/.gitkeep") unless options[:skip_git] + end + end end end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index ef1eb8d237..f833b5d041 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -317,11 +317,6 @@ module Rails ].find { |f| File.exist?(f) } unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end - def empty_directory_with_gitkeep(destination, config = {}) - empty_directory(destination, config) - create_file("#{destination}/.gitkeep") unless options[:skip_git] - end - def get_builder_class defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder end diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 9c54b98238..3cd4fa4ba1 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -88,6 +88,29 @@ task :default => :test end end + def stylesheets + empty_directory_with_gitkeep "public/stylesheets" if options[:mountable] + end + + def javascripts + return unless options[:mountable] + + empty_directory "#{app_templates_dir}/public/javascripts" + + unless options[:skip_javascript] + copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}.js", "public/javascripts/#{options[:javascript]}.js" + copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}_ujs.js", "public/javascripts/rails.js" + + if options[:prototype] + copy_file "#{app_templates_dir}/public/javascripts/controls.js", "public/javascripts/controls.js" + copy_file "#{app_templates_dir}/public/javascripts/dragdrop.js", "public/javascripts/dragdrop.js" + copy_file "#{app_templates_dir}/public/javascripts/effects.js", "public/javascripts/effects.js" + end + end + + copy_file "#{app_templates_dir}/public/javascripts/application.js", "public/javascripts/application.js" + end + def script(force = false) directory "script", :force => force do |content| "#{shebang}\n" + content @@ -143,6 +166,14 @@ task :default => :test build(:lib) end + def create_public_stylesheets_files + build(:stylesheets) + end + + def create_javascript_files + build(:javascripts) + end + def create_script_files build(:script) end @@ -163,6 +194,10 @@ task :default => :test public_task :apply_rails_template, :bundle_if_dev_or_edge protected + def app_templates_dir + "../../app/templates" + end + def create_dummy_app(path = nil) dummy_path(path) if path diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index 0d24821ff6..e146cb2bb3 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -91,6 +91,35 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase assert_file "test/dummy/config/database.yml", /postgres/ end + def test_skipping_javascripts_without_mountable_option + run_generator + assert_no_file "public/javascripts/prototype.js" + assert_no_file "public/javascripts/rails.js" + assert_no_file "public/javascripts/controls.js" + assert_no_file "public/javascripts/dragdrop.js" + assert_no_file "public/javascripts/dragdrop.js" + assert_no_file "public/javascripts/application.js" + end + + def test_javascripts_generation + run_generator [destination_root, "--mountable"] + assert_file "public/javascripts/rails.js" + assert_file "public/javascripts/prototype.js" + assert_file "public/javascripts/controls.js" + assert_file "public/javascripts/dragdrop.js" + assert_file "public/javascripts/dragdrop.js" + assert_file "public/javascripts/application.js" + end + + def test_skip_javascripts + run_generator [destination_root, "--skip-javascript", "--mountable"] + assert_no_file "public/javascripts/prototype.js" + assert_no_file "public/javascripts/rails.js" + assert_no_file "public/javascripts/controls.js" + assert_no_file "public/javascripts/dragdrop.js" + assert_no_file "public/javascripts/dragdrop.js" + end + def test_ensure_that_javascript_option_is_passed_to_app_generator run_generator [destination_root, "--javascript", "jquery"] assert_file "test/dummy/public/javascripts/jquery.js" -- cgit v1.2.3 From 7f77d6691f567b33e23b515ad495071bef8f775e Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 12:56:29 +0100 Subject: Fix generation of prototype files, it should work with --javascript prototype, not --prototype --- railties/lib/rails/generators/rails/app/app_generator.rb | 2 +- railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb | 2 +- railties/test/generators/app_generator_test.rb | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index f833b5d041..d25372ea8e 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -109,7 +109,7 @@ module Rails copy_file "public/javascripts/#{@options[:javascript]}.js" copy_file "public/javascripts/#{@options[:javascript]}_ujs.js", "public/javascripts/rails.js" - if options[:prototype] + if options[:javascript] == "prototype" copy_file "public/javascripts/controls.js" copy_file "public/javascripts/dragdrop.js" copy_file "public/javascripts/effects.js" diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 3cd4fa4ba1..47a84e228c 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -101,7 +101,7 @@ task :default => :test copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}.js", "public/javascripts/#{options[:javascript]}.js" copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}_ujs.js", "public/javascripts/rails.js" - if options[:prototype] + if options[:javascript] == "prototype" copy_file "#{app_templates_dir}/public/javascripts/controls.js", "public/javascripts/controls.js" copy_file "#{app_templates_dir}/public/javascripts/dragdrop.js", "public/javascripts/dragdrop.js" copy_file "#{app_templates_dir}/public/javascripts/effects.js", "public/javascripts/effects.js" diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 7faa674a81..02c49ab241 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -135,6 +135,9 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "public/javascripts/application.js" assert_file "public/javascripts/prototype.js" assert_file "public/javascripts/rails.js" + assert_file "public/javascripts/controls.js" + assert_file "public/javascripts/dragdrop.js" + assert_file "public/javascripts/dragdrop.js" assert_file "test" end -- cgit v1.2.3 From ed32c8fe51e347bbccd6cce64b4b8d538c0a61aa Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 12:20:48 +0100 Subject: Generate default layout in engine with --mountable option --- railties/lib/rails/generators/rails/app/app_generator.rb | 1 + .../rails/app/templates/app/views/layouts/application.html.erb.tt | 2 +- .../lib/rails/generators/rails/plugin_new/plugin_new_generator.rb | 6 +++++- railties/test/generators/plugin_new_generator_test.rb | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index d25372ea8e..d842797883 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -284,6 +284,7 @@ module Rails def app_const_base @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, '_').squeeze('_').camelize end + alias :camelized :app_const_base def app_const @app_const ||= "#{app_const_base}::Application" diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt index 1de78eecae..6d56c331c1 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <head> - <title><%= app_const_base %> + <%= camelized %> <%%= stylesheet_link_tag :all %> <%%= javascript_include_tag :defaults %> <%%= csrf_meta_tags %> diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 47a84e228c..9461589ff5 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -8,7 +8,11 @@ module Rails end def app - directory "app" if options[:mountable] + if options[:mountable] + directory "app" + template "#{app_templates_dir}/app/views/layouts/application.html.erb.tt", + "app/views/layouts/#{name}/application.html.erb" + end end def readme diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index e146cb2bb3..2a9e8046b8 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -166,6 +166,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase assert_file "test/dummy/config/routes.rb", /mount Bukkits::Engine => "\/bukkits"/ assert_file "app/controllers/bukkits/application_controller.rb", /module Bukkits\n class ApplicationController < ActionController::Base/ assert_file "app/helpers/bukkits/application_helper.rb", /module Bukkits\n module ApplicationHelper/ + assert_file "app/views/layouts/bukkits/application.html.erb", /Bukkits<\/title>/ end def test_passing_dummy_path_as_a_parameter -- cgit v1.2.3 From bb89f8613abdfa562445519c0dca901e2231a6bf Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 12:21:46 +0100 Subject: Kill whitespace! --- railties/lib/rails/generators/rails/app/app_generator.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index d842797883..94af9fe1e9 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -104,18 +104,18 @@ module Rails def javascripts empty_directory "public/javascripts" - + unless options[:skip_javascript] copy_file "public/javascripts/#{@options[:javascript]}.js" copy_file "public/javascripts/#{@options[:javascript]}_ujs.js", "public/javascripts/rails.js" - + if options[:javascript] == "prototype" copy_file "public/javascripts/controls.js" copy_file "public/javascripts/dragdrop.js" copy_file "public/javascripts/effects.js" end end - + copy_file "public/javascripts/application.js" end -- cgit v1.2.3 From 875bbd58b4175a1219c5f7615946d0b89e51182d Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 15 Dec 2010 13:08:49 +0100 Subject: AppGenerator: use options instead of @options --- railties/lib/rails/generators/rails/app/app_generator.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 94af9fe1e9..3f6ff35a86 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -63,7 +63,7 @@ module Rails end def database_yml - template "config/databases/#{@options[:database]}.yml", "config/database.yml" + template "config/databases/#{options[:database]}.yml", "config/database.yml" end def db @@ -106,8 +106,8 @@ module Rails empty_directory "public/javascripts" unless options[:skip_javascript] - copy_file "public/javascripts/#{@options[:javascript]}.js" - copy_file "public/javascripts/#{@options[:javascript]}_ujs.js", "public/javascripts/rails.js" + copy_file "public/javascripts/#{options[:javascript]}.js" + copy_file "public/javascripts/#{options[:javascript]}_ujs.js", "public/javascripts/rails.js" if options[:javascript] == "prototype" copy_file "public/javascripts/controls.js" -- cgit v1.2.3 From ebc47465a5865ab91dc7d058d2d8a0cc961510d7 Mon Sep 17 00:00:00 2001 From: brainopia <ravwar@gmail.com> Date: Thu, 9 Dec 2010 18:38:52 +0300 Subject: Fix edge cases for domain :all option on cookie store Dont set explicit domain for cookies if host is not a domain name [#6002 state:committed] Signed-off-by: Santiago Pastorino <santiago@wyeworks.com> --- .../lib/action_dispatch/middleware/cookies.rb | 7 +++++-- actionpack/test/dispatch/cookies_test.rb | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index b0a4e3d949..f369d2d3c2 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -131,8 +131,11 @@ module ActionDispatch options[:path] ||= "/" if options[:domain] == :all - @host =~ DOMAIN_REGEXP - options[:domain] = ".#{$1}.#{$2}" + # if host is not ip and matches domain regexp + # (ip confirms to domain regexp so we explicitly check for ip) + options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ DOMAIN_REGEXP) + ".#{$1}.#{$2}" + end end end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 5ec7f12cc1..e2040401c7 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -295,6 +295,27 @@ class CookiesTest < ActionController::TestCase assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/" end + def test_cookie_with_all_domain_option_using_localhost + @request.host = "localhost" + get :set_cookie_with_domain + assert_response :success + assert_cookie_header "user_name=rizwanreza; path=/" + end + + def test_cookie_with_all_domain_option_using_ipv4_address + @request.host = "192.168.1.1" + get :set_cookie_with_domain + assert_response :success + assert_cookie_header "user_name=rizwanreza; path=/" + end + + def test_cookie_with_all_domain_option_using_ipv6_address + @request.host = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + get :set_cookie_with_domain + assert_response :success + assert_cookie_header "user_name=rizwanreza; path=/" + end + def test_deleting_cookie_with_all_domain_option get :delete_cookie_with_domain assert_response :success -- cgit v1.2.3 From eba76640862d071d89c846f8624d1e651d872794 Mon Sep 17 00:00:00 2001 From: Jon Leighton <j@jonathanleighton.com> Date: Sun, 12 Dec 2010 16:35:27 +0000 Subject: Respect the default_scope on a join model when reading a through association --- .../active_record/associations/has_many_through_association.rb | 2 +- .../active_record/associations/has_one_through_association.rb | 2 +- .../active_record/associations/through_association_scope.rb | 7 +++++++ .../cases/associations/has_many_through_associations_test.rb | 4 ++++ .../cases/associations/has_one_through_associations_test.rb | 10 +++++++++- activerecord/test/models/author.rb | 4 ++++ activerecord/test/models/contract.rb | 2 +- activerecord/test/models/post.rb | 7 +++++++ 8 files changed, 34 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 6ad51e2fb4..781aa7ef62 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -81,7 +81,7 @@ module ActiveRecord def find_target return [] unless target_reflection_has_associated_record? - with_scope(@scope) { @reflection.klass.find(:all) } + scoped.all end def has_cached_counter? diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb index 7f28abf464..e8cf73976b 100644 --- a/activerecord/lib/active_record/associations/has_one_through_association.rb +++ b/activerecord/lib/active_record/associations/has_one_through_association.rb @@ -33,7 +33,7 @@ module ActiveRecord private def find_target - with_scope(@scope) { @reflection.klass.find(:first) } + scoped.first end end end diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index a55bf6323e..2ecd0f054a 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -3,6 +3,13 @@ module ActiveRecord module Associations module ThroughAssociationScope + def scoped + with_scope(@scope) do + @reflection.klass.scoped & + @reflection.through_reflection.klass.scoped + end + end + protected def construct_find_scope diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 34ae297275..113d0f6d73 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -462,4 +462,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase post.people << people(:michael) assert_equal readers + 1, post.readers.size end + + def test_has_many_through_with_default_scope_on_join_model + assert_equal posts(:welcome).comments, authors(:david).comments_on_first_posts + end end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index ac43e571cb..93a4f498d0 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -9,9 +9,13 @@ require 'models/member_detail' require 'models/minivan' require 'models/dashboard' require 'models/speedometer' +require 'models/author' +require 'models/post' +require 'models/comment' class HasOneThroughAssociationsTest < ActiveRecord::TestCase - fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans, :dashboards, :speedometers + fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans, + :dashboards, :speedometers, :authors, :posts, :comments def setup @member = members(:groucho) @@ -229,4 +233,8 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase minivan.dashboard end end + + def test_has_one_through_with_default_scope_on_join_model + assert_equal posts(:welcome).comments.first, authors(:david).comment_on_first_posts + end end diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 34bfd2d881..29ee50e801 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -26,6 +26,10 @@ class Author < ActiveRecord::Base has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'" has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post + has_many :first_posts + has_many :comments_on_first_posts, :through => :first_posts, :source => :comments, :order => 'posts.id desc, comments.id asc' + has_one :comment_on_first_posts, :through => :first_posts, :source => :comments, :order => 'posts.id desc, comments.id asc' + has_many :thinking_posts, :class_name => 'Post', :conditions => { :title => 'So I was thinking' }, :dependent => :delete_all has_many :welcome_posts, :class_name => 'Post', :conditions => { :title => 'Welcome to the weblog' } diff --git a/activerecord/test/models/contract.rb b/activerecord/test/models/contract.rb index 606c99cd4e..94fd48e12a 100644 --- a/activerecord/test/models/contract.rb +++ b/activerecord/test/models/contract.rb @@ -1,4 +1,4 @@ class Contract < ActiveRecord::Base belongs_to :company belongs_to :developer -end \ No newline at end of file +end diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 61e782ff14..164b499bf0 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -119,3 +119,10 @@ class PostForAuthor < ActiveRecord::Base cattr_accessor :selected_author default_scope lambda { where(:author_id => PostForAuthor.selected_author) } end + +class FirstPost < ActiveRecord::Base + self.table_name = 'posts' + default_scope where(:id => 1) + has_many :comments, :foreign_key => :post_id + has_one :comment, :foreign_key => :post_id +end -- cgit v1.2.3 From 491ce5b6ce1a3af42d02efff9a2e42b1a8980553 Mon Sep 17 00:00:00 2001 From: Jon Leighton <j@jonathanleighton.com> Date: Sun, 12 Dec 2010 17:03:41 +0000 Subject: Verify that creating a has_many through record where there is a default_scope on the join model works correctly (creates the join record with the default scope applied) --- .../cases/associations/has_many_through_associations_test.rb | 7 +++++++ activerecord/test/models/author.rb | 4 ++++ activerecord/test/models/categorization.rb | 11 ++++++++++- activerecord/test/schema/schema.rb | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 113d0f6d73..44ff01ddc0 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -17,6 +17,8 @@ require 'models/developer' require 'models/subscriber' require 'models/book' require 'models/subscription' +require 'models/categorization' +require 'models/category' class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, @@ -466,4 +468,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_with_default_scope_on_join_model assert_equal posts(:welcome).comments, authors(:david).comments_on_first_posts end + + def test_create_has_many_through_with_default_scope_on_join_model + category = authors(:david).special_categories.create(:name => "Foo") + assert_equal 1, category.categorizations.where(:special => true).count + end end diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 29ee50e801..fd6d2b384a 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -77,6 +77,10 @@ class Author < ActiveRecord::Base has_many :categorizations has_many :categories, :through => :categorizations + has_many :special_categorizations + has_many :special_categories, :through => :special_categorizations, :source => :category + has_one :special_category, :through => :special_categorizations, :source => :category + has_many :categories_like_general, :through => :categorizations, :source => :category, :class_name => 'Category', :conditions => { :name => 'General' } has_many :categorized_posts, :through => :categorizations, :source => :post diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb index 10594323ff..b3fc29fa15 100644 --- a/activerecord/test/models/categorization.rb +++ b/activerecord/test/models/categorization.rb @@ -2,4 +2,13 @@ class Categorization < ActiveRecord::Base belongs_to :post belongs_to :category belongs_to :author -end \ No newline at end of file +end + +class SpecialCategorization < ActiveRecord::Base + self.table_name = 'categorizations' + + default_scope where(:special => true) + + belongs_to :author + belongs_to :category +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index d4eb56c4d7..177045ac51 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -112,6 +112,7 @@ ActiveRecord::Schema.define do t.column :category_id, :integer t.column :post_id, :integer t.column :author_id, :integer + t.column :special, :boolean end create_table :citations, :force => true do |t| -- cgit v1.2.3 From 4cd3302279f4ab5af642a1cd4e601b676ba6278b Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Wed, 15 Dec 2010 11:12:33 -0800 Subject: make sure that join nodes are uniq --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 8e50f4a9bc..6660df302b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -282,7 +282,7 @@ module ActiveRecord association.join_to(manager) end - manager.join_sources.concat join_nodes + manager.join_sources.concat join_nodes.uniq manager.join_sources.concat join_list manager -- cgit v1.2.3 From 15984dbe35651e64dcff69f8e48fca156c380df2 Mon Sep 17 00:00:00 2001 From: Pivotal Labs <pivotal@lapidge.flood.pivotallabs.com> Date: Fri, 9 Jan 2009 12:13:17 -0800 Subject: test for eager load of has_one association with condition on the through table --- activerecord/test/cases/associations/eager_test.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index c96ca90750..34a1cdeebe 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -17,18 +17,26 @@ require 'models/subscription' require 'models/book' require 'models/developer' require 'models/project' +require 'models/member' +require 'models/membership' +require 'models/club' class EagerAssociationTest < ActiveRecord::TestCase fixtures :posts, :comments, :authors, :author_addresses, :categories, :categories_posts, :companies, :accounts, :tags, :taggings, :people, :readers, :owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books, - :developers, :projects, :developers_projects + :developers, :projects, :developers_projects, :members, :memberships, :clubs def setup # preheat table existence caches Comment.find_by_id(1) end + def test_eager_with_has_one_through_join_model_with_conditions_on_the_through + member = Member.find(members(:some_other_guy).id, :include => :favourite_club) + assert_nil member.favourite_club + end + def test_loading_with_one_association posts = Post.find(:all, :include => :comments) post = posts.find { |p| p.id == 1 } -- cgit v1.2.3 From f96fb78e57a9eda13edcba8716b4c7d7d5cbb375 Mon Sep 17 00:00:00 2001 From: Vijay Dev <vijaydev.cse@gmail.com> Date: Thu, 16 Dec 2010 01:48:11 +0530 Subject: removed info about deprecated rake tasks, documented the sandbox option for the rails console and minor corrections --- railties/guides/source/command_line.textile | 42 +++++++++++++---------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile index 11ce3a5003..1e570c9992 100644 --- a/railties/guides/source/command_line.textile +++ b/railties/guides/source/command_line.textile @@ -31,7 +31,7 @@ h4. +rails new+ The first thing we'll want to do is create a new Rails application by running the +rails new+ command after installing Rails. -WARNING: You know you need the rails gem installed by typing +gem install rails+ first, if you don't have this installed, follow the instructions in the "Rails 3 Release Notes":/3_0_release_notes.html +WARNING: You can install the rails gem by typing +gem install rails+, if you don't have it already. Follow the instructions in the "Rails 3 Release Notes":/3_0_release_notes.html <shell> $ rails new commandsapp @@ -73,7 +73,7 @@ $ rails server [2010-04-18 03:20:33] INFO WEBrick::HTTPServer#start: pid=26086 port=3000 </shell> -With just three commands we whipped up a Rails server listening on port 3000. Go to your browser and open "http://localhost:3000":http://localhost:3000, you will see a basic rails app running. +With just three commands we whipped up a Rails server listening on port 3000. Go to your browser and open "http://localhost:3000":http://localhost:3000, you will see a basic Rails app running. h4. +rails generate+ @@ -101,7 +101,7 @@ Using generators will save you a large amount of time by writing *boilerplate co Let's make our own controller with the controller generator. But what command should we use? Let's ask the generator: -INFO: All Rails console utilities have help text. As with most *NIX utilities, you can try adding +--help+ or +-h+ to the end, for example +rails server --help+. +INFO: All Rails console utilities have help text. As with most *nix utilities, you can try adding +--help+ or +-h+ to the end, for example +rails server --help+. <shell> $ rails generate controller @@ -134,6 +134,7 @@ The controller generator is expecting parameters in the form of +generate contro <shell> $ rails generate controller Greetings hello create app/controllers/greetings_controller.rb + route get "greetings/hello" invoke erb create app/views/greetings create app/views/greetings/hello.html.erb @@ -261,6 +262,15 @@ h4. +rails console+ The +console+ command lets you interact with your Rails application from the command line. On the underside, +rails console+ uses IRB, so if you've ever used it, you'll be right at home. This is useful for testing out quick ideas with code and changing data server-side without touching the website. +If you wish to test out some code without changing any data, you can do that by invoking +rails console --sandbox+. + +<shell> +$ rails console --sandbox +Loading development environment in sandbox (Rails 3.0.0) +Any modifications you make will be rolled back on exit +irb(main):001:0> +</shell> + h4. +rails dbconsole+ +rails dbconsole+ figures out which database you're using and drops you into whichever command line interface you would use with it (and figures out the command line parameters to give to it, too!). It supports MySQL, PostgreSQL, SQLite and SQLite3. @@ -281,7 +291,7 @@ $ rails plugin install http://svn.techno-weenie.net/projects/plugins/acts_as_par ... </shell> -h4. +runner+ +h4. +rails runner+ <tt>runner</tt> runs Ruby code in the context of Rails non-interactively. For instance: @@ -289,7 +299,7 @@ h4. +runner+ $ rails runner "Model.long_running_method" </shell> -h4. +destroy+ +h4. +rails destroy+ Think of +destroy+ as the opposite of +generate+. It'll figure out what generate did, and undo it. Believe you-me, the creation of this tutorial used this command many times! @@ -318,7 +328,7 @@ $ rails destroy model Oops notempty app </shell> -h4. +about+ +h4. +rake about+ Check it: Version numbers for Ruby, RubyGems, Rails, the Rails subcomponents, your application's folder, the current Rails environment name, your app's database adapter, and schema version! +about+ is useful when you need to ask for help, check if a security patch might affect you, or when you need some stats for an existing Rails installation. @@ -341,7 +351,7 @@ Environment development h3. The Rails Advanced Command Line -The more advanced uses of the command line are focused around finding useful (even surprising at times) options in the utilities, and fitting utilities to your needs and specific work flow. Listed here are some tricks up Rails' sleeve. +More advanced use of the command line is focused around finding useful (even surprising at times) options in the utilities, and fitting those to your needs and specific work flow. Listed here are some tricks up Rails' sleeve. h4. Rails with Databases and SCM @@ -474,7 +484,7 @@ We take whatever args are supplied, save them to an instance variable, and liter * Check there's a *public* directory. You bet there is. * Run the ERb template called "tutorial.erb". * Save it into "Rails.root/public/tutorial.txt". -* Pass in the arguments we saved through the +:assign+ parameter. +* Pass in the arguments we saved through the +:assigns+ parameter. Next we'll build the template: @@ -540,8 +550,6 @@ rake tmp:sessions:clear # Clears all files in tmp/sessions rake tmp:sockets:clear # Clears all files in tmp/sockets </shell> -Let's take a look at some of these 80 or so rake tasks. - h5. +db:+ Database The most common tasks of the +db:+ Rake namespace are +migrate+ and +create+, and it will pay off to try out all of the migration rake tasks (+up+, +down+, +redo+, +reset+). +rake db:version+ is useful when troubleshooting, telling you the current version of the database. @@ -550,24 +558,10 @@ h5. +doc:+ Documentation If you want to strip out or rebuild any of the Rails documentation (including this guide!), the +doc:+ namespace has the tools. Stripping documentation is mainly useful for slimming your codebase, like if you're writing a Rails application for an embedded platform. -h5. +gems:+ Ruby gems - -You can specify which gems your application uses, and +rake gems:install+ will install them for you. Look at your environment.rb to learn how with the *config.gem* directive. - -NOTE: +gems:unpack+ will unpack, that is internalize your application's Gem dependencies by copying the Gem code into your vendor/gems directory. By doing this you increase your codebase size, but simplify installation on new hosts by eliminating the need to run +rake gems:install+, or finding and installing the gems your application uses. - h5. +notes:+ Code note enumeration These tasks will search through your code for commented lines beginning with "FIXME", "OPTIMIZE", "TODO", or any custom annotation (like XXX) and show you them. -h5. +rails:+ Rails-specific tasks - -In addition to the +gems:unpack+ task above, you can also unpack the Rails backend specific gems into vendor/rails by calling +rake rails:freeze:gems+, to unpack the version of Rails you are currently using, or +rake rails:freeze:edge+ to unpack the most recent (cutting, bleeding edge) version. - -When you have frozen the Rails gems, Rails will prefer to use the code in vendor/rails instead of the system Rails gems. You can "thaw" by running +rake rails:unfreeze+. - -After upgrading Rails, it is useful to run +rails:update+, which will update your config and scripts directories, and upgrade your Rails-specific javascript (like Scriptaculous). - h5. +test:+ Rails tests INFO: A good description of unit testing in Rails is given in "A Guide to Testing Rails Applications":testing.html -- cgit v1.2.3 From fe0db2b80588446b34f5f89bbe73f04b88fb06c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81ctor=20Marti=CC=81nez?= <knoopx@gmail.com> Date: Thu, 30 Sep 2010 19:14:47 +0200 Subject: to_xml doesn't work in such case: Event.select('title as t').to_xml [#4840 state:resolved] NilClass.type is no longer defined in Ruby 1.9 and causes ActiveRecord::Base.to_xml to fail with message: undefined method `type' for nil:NilClass --- activerecord/lib/active_record/serializers/xml_serializer.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index 15abf8bac7..0746908131 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -226,8 +226,14 @@ module ActiveRecord #:nodoc: class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc: def compute_type - type = @serializable.class.serialized_attributes.has_key?(name) ? - super : @serializable.class.columns_hash[name].type + case + when @serializable.class.serialized_attributes.has_key?(name) + type = super + when @serializable.class.columns_hash.has_key?(name) + type = @serializable.class.columns_hash[name].type + else + type = NilClass + end case type when :text -- cgit v1.2.3 From aa40543022303d8a1b695e73ed71406c0da15bde Mon Sep 17 00:00:00 2001 From: Franck Verrot <franck@verrot.fr> Date: Thu, 18 Nov 2010 09:01:45 +0100 Subject: Provide test for #4840: to_xml doesn't work in such case: Event.select('title as t').to_xml --- activerecord/test/cases/xml_serialization_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb index 2003e25e35..a6074b23e7 100644 --- a/activerecord/test/cases/xml_serialization_test.rb +++ b/activerecord/test/cases/xml_serialization_test.rb @@ -262,4 +262,10 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase assert array.include? 'github' end + def test_should_support_aliased_attributes + xml = Author.select("name as firstname").to_xml + array = Hash.from_xml(xml)['authors'] + assert_equal array.size, array.select { |author| author.has_key? 'firstname' }.size + end + end -- cgit v1.2.3 From 96bae30538951367a82235785e7dba3577b50a5a Mon Sep 17 00:00:00 2001 From: Pavel Gorbokon <pahanix@gmail.com> Date: Wed, 24 Nov 2010 10:17:49 +0200 Subject: Replace rudimentary named_scope with scope. [#6052 state:resolved] * rename method names (actually in tests) * rename instance variable @_named_scopes_cache to @_scopes_cache * rename references in doc comments * don't touch CHANGELOG :) --- ...nder_partial_with_record_identification_test.rb | 6 ++-- .../associations/association_collection.rb | 16 ++++----- activerecord/lib/active_record/base.rb | 4 +-- activerecord/test/cases/named_scope_test.rb | 42 +++++++++++----------- activerecord/test/cases/relation_scoping_test.rb | 2 +- activerecord/test/cases/relations_test.rb | 6 ++-- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb index 43c534c111..99f09286ff 100644 --- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb @@ -11,7 +11,7 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base render :partial => @topic.replies end - def render_with_named_scope + def render_with_scope render :partial => Reply.base end @@ -62,8 +62,8 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase assert_equal 'Birdman is better!', @response.body end - def test_rendering_partial_with_named_scope - get :render_with_named_scope + def test_rendering_partial_with_scope + get :render_with_scope assert_template 'replies/_reply' assert_equal 'Birdman is better!Nuh uh!', @response.body end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index abb17a11c6..11a7a725e5 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -102,7 +102,7 @@ module ActiveRecord def reset reset_target! - reset_named_scopes_cache! + reset_scopes_cache! @loaded = false end @@ -160,7 +160,7 @@ module ActiveRecord load_target delete(@target) reset_target! - reset_named_scopes_cache! + reset_scopes_cache! end # Calculate sum using SQL, not Enumerable @@ -253,7 +253,7 @@ module ActiveRecord load_target destroy(@target).tap do reset_target! - reset_named_scopes_cache! + reset_scopes_cache! end end @@ -409,9 +409,9 @@ module ActiveRecord if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) super elsif @reflection.klass.scopes[method] - @_named_scopes_cache ||= {} - @_named_scopes_cache[method] ||= {} - @_named_scopes_cache[method][args] ||= with_scope(@scope) { @reflection.klass.send(method, *args) } + @_scopes_cache ||= {} + @_scopes_cache[method] ||= {} + @_scopes_cache[method][args] ||= with_scope(@scope) { @reflection.klass.send(method, *args) } else with_scope(@scope) do if block_given? @@ -442,8 +442,8 @@ module ActiveRecord @target = Array.new end - def reset_named_scopes_cache! - @_named_scopes_cache = {} + def reset_scopes_cache! + @_scopes_cache = {} end def find_target diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7b9ce21ceb..d0f33c1d18 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -852,8 +852,8 @@ module ActiveRecord #:nodoc: # limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } # - # It is recommended to use block form of unscoped because chaining unscoped with <tt>named_scope</tt> - # does not work. Assuming that <tt>published</tt> is a <tt>named_scope</tt> following two statements are same. + # It is recommended to use block form of unscoped because chaining unscoped with <tt>scope</tt> + # does not work. Assuming that <tt>published</tt> is a <tt>scope</tt> following two statements are same. # # Post.unscoped.published # Post.published diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 6ac3e3fc56..ed5e1e0cba 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -141,26 +141,26 @@ class NamedScopeTest < ActiveRecord::TestCase assert_equal 1, Topic.multiple_extensions.extension_one end - def test_has_many_associations_have_access_to_named_scopes + def test_has_many_associations_have_access_to_scopes assert_not_equal Post.containing_the_letter_a, authors(:david).posts assert !Post.containing_the_letter_a.empty? assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a end - def test_named_scope_with_STI + def test_scope_with_STI assert_equal 3,Post.containing_the_letter_a.count assert_equal 1,SpecialPost.containing_the_letter_a.count end - def test_has_many_through_associations_have_access_to_named_scopes + def test_has_many_through_associations_have_access_to_scopes assert_not_equal Comment.containing_the_letter_e, authors(:david).comments assert !Comment.containing_the_letter_e.empty? assert_equal authors(:david).comments & Comment.containing_the_letter_e, authors(:david).comments.containing_the_letter_e end - def test_named_scopes_honor_current_scopes_from_when_defined + def test_scopes_honor_current_scopes_from_when_defined assert !Post.ranked_by_comments.limit_by(5).empty? assert !authors(:david).posts.ranked_by_comments.limit_by(5).empty? assert_not_equal Post.ranked_by_comments.limit_by(5), authors(:david).posts.ranked_by_comments.limit_by(5) @@ -236,7 +236,7 @@ class NamedScopeTest < ActiveRecord::TestCase end end - def test_any_should_not_fire_query_if_named_scope_loaded + def test_any_should_not_fire_query_if_scope_loaded topics = Topic.base topics.collect # force load assert_no_queries { assert topics.any? } @@ -259,7 +259,7 @@ class NamedScopeTest < ActiveRecord::TestCase end end - def test_many_should_not_fire_query_if_named_scope_loaded + def test_many_should_not_fire_query_if_scope_loaded topics = Topic.base topics.collect # force load assert_no_queries { assert topics.many? } @@ -276,27 +276,27 @@ class NamedScopeTest < ActiveRecord::TestCase assert Topic.base.many? end - def test_should_build_on_top_of_named_scope + def test_should_build_on_top_of_scope topic = Topic.approved.build({}) assert topic.approved end - def test_should_build_new_on_top_of_named_scope + def test_should_build_new_on_top_of_scope topic = Topic.approved.new assert topic.approved end - def test_should_create_on_top_of_named_scope + def test_should_create_on_top_of_scope topic = Topic.approved.create({}) assert topic.approved end - def test_should_create_with_bang_on_top_of_named_scope + def test_should_create_with_bang_on_top_of_scope topic = Topic.approved.create!({}) assert topic.approved end - def test_should_build_on_top_of_chained_named_scopes + def test_should_build_on_top_of_chained_scopes topic = Topic.approved.by_lifo.build({}) assert topic.approved assert_equal 'lifo', topic.author_name @@ -310,7 +310,7 @@ class NamedScopeTest < ActiveRecord::TestCase assert_kind_of Topic, Topic.approved.sample end - def test_should_use_where_in_query_for_named_scope + def test_should_use_where_in_query_for_scope assert_equal Developer.find_all_by_name('Jamis').to_set, Developer.find_all_by_id(Developer.jamises).to_set end @@ -361,7 +361,7 @@ class NamedScopeTest < ActiveRecord::TestCase assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.uniq end - def test_named_scopes_batch_finders + def test_scopes_batch_finders assert_equal 3, Topic.approved.count assert_queries(4) do @@ -381,7 +381,7 @@ class NamedScopeTest < ActiveRecord::TestCase end end - def test_named_scopes_with_reserved_names + def test_scopes_with_reserved_names class << Topic def public_method; end public :public_method @@ -400,7 +400,7 @@ class NamedScopeTest < ActiveRecord::TestCase end end - def test_named_scopes_on_relations + def test_scopes_on_relations # Topic.replied approved_topics = Topic.scoped.approved.order('id DESC') assert_equal topics(:fourth), approved_topics.first @@ -409,19 +409,19 @@ class NamedScopeTest < ActiveRecord::TestCase assert_equal topics(:third), replied_approved_topics.first end - def test_index_on_named_scope + def test_index_on_scope approved = Topic.approved.order('id ASC') assert_equal topics(:second), approved[0] assert approved.loaded? end - def test_nested_named_scopes_queries_size + def test_nested_scopes_queries_size assert_queries(1) do Topic.approved.by_lifo.replied.written_before(Time.now).all end end - def test_named_scopes_are_cached_on_associations + def test_scopes_are_cached_on_associations post = posts(:welcome) assert_equal post.comments.containing_the_letter_e.object_id, post.comments.containing_the_letter_e.object_id @@ -430,7 +430,7 @@ class NamedScopeTest < ActiveRecord::TestCase assert_no_queries { post.comments.containing_the_letter_e.all } end - def test_named_scopes_with_arguments_are_cached_on_associations + def test_scopes_with_arguments_are_cached_on_associations post = posts(:welcome) one = post.comments.limit_by(1).all @@ -443,7 +443,7 @@ class NamedScopeTest < ActiveRecord::TestCase assert_no_queries { post.comments.limit_by(2).all } end - def test_named_scopes_are_reset_on_association_reload + def test_scopes_are_reset_on_association_reload post = posts(:welcome) [:destroy_all, :reset, :delete_all].each do |method| @@ -453,7 +453,7 @@ class NamedScopeTest < ActiveRecord::TestCase end end - def test_named_scoped_are_lazy_loaded_if_table_still_does_not_exist + def test_scoped_are_lazy_loaded_if_table_still_does_not_exist assert_nothing_raised do require "models/without_table" end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index 1678e631e5..f113a9c516 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -422,7 +422,7 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end - def test_named_scope_overwrites_default + def test_scope_overwrites_default expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.name } received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name } assert_equal expected, received diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 1682f34a1d..0b106e3bcd 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -36,7 +36,7 @@ class RelationTest < ActiveRecord::TestCase assert_equal [], relation.bind_values end - def test_two_named_scopes_with_includes_should_not_drop_any_include + def test_two_scopes_with_includes_should_not_drop_any_include car = Car.incl_engines.incl_tyres.first assert_no_queries { car.tyres.length } assert_no_queries { car.engines.length } @@ -248,7 +248,7 @@ class RelationTest < ActiveRecord::TestCase end end - def test_respond_to_class_methods_and_named_scopes + def test_respond_to_class_methods_and_scopes assert DeveloperOrderedBySalary.scoped.respond_to?(:all_ordered_by_name) assert Topic.scoped.respond_to?(:by_lifo) end @@ -754,7 +754,7 @@ class RelationTest < ActiveRecord::TestCase assert_equal 'zyke', FastCar.order('name desc').find(:first, :order => 'id').name end - def test_default_scope_order_with_named_scope_order + def test_default_scope_order_with_scope_order assert_equal 'zyke', CoolCar.order_using_new_style.limit(1).first.name assert_equal 'zyke', CoolCar.order_using_old_style.limit(1).first.name assert_equal 'zyke', FastCar.order_using_new_style.limit(1).first.name -- cgit v1.2.3 From 6bfa846a2cc950bbb3d8b30f076589a60f38b821 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Wed, 15 Dec 2010 14:01:33 -0800 Subject: dry up compute type in attribute --- .../active_record/serializers/xml_serializer.rb | 26 +++++++++------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index 0746908131..8c4adf7116 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -226,23 +226,17 @@ module ActiveRecord #:nodoc: class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc: def compute_type - case - when @serializable.class.serialized_attributes.has_key?(name) - type = super - when @serializable.class.columns_hash.has_key?(name) - type = @serializable.class.columns_hash[name].type - else - type = NilClass - end + klass = @serializable.class + type = if klass.serialized_attributes.key?(name) + super + elsif klass.columns_hash.key?(name) + klass.columns_hash[name].type + else + NilClass + end - case type - when :text - :string - when :time - :datetime - else - type - end + { :text => :string, + :time => :datetime }[type] || type end protected :compute_type end -- cgit v1.2.3 From 09ddca67acbb88e2fdd7300670839cbf647b2694 Mon Sep 17 00:00:00 2001 From: Jon Leighton <j@jonathanleighton.com> Date: Thu, 16 Dec 2010 00:40:03 +0000 Subject: Fix problem with duplicated records when a :uniq :through association is preloaded [#2447 state:resolved] --- activerecord/lib/active_record/association_preload.rb | 1 + activerecord/test/cases/associations/eager_test.rb | 9 ++++++++- .../cases/associations/has_many_through_associations_test.rb | 8 +++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 5eb1071ba2..f2094283a2 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -251,6 +251,7 @@ module ActiveRecord through_record_id = through_record[reflection.through_reflection_primary_key].to_s add_preloaded_records_to_collection(id_to_record_map[through_record_id], reflection.name, through_record.send(source)) end + records.each { |record| record.send(reflection.name).target.uniq! } if options[:uniq] end else diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 34a1cdeebe..d5262b1ee4 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -20,10 +20,11 @@ require 'models/project' require 'models/member' require 'models/membership' require 'models/club' +require 'models/categorization' class EagerAssociationTest < ActiveRecord::TestCase fixtures :posts, :comments, :authors, :author_addresses, :categories, :categories_posts, - :companies, :accounts, :tags, :taggings, :people, :readers, + :companies, :accounts, :tags, :taggings, :people, :readers, :categorizations, :owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books, :developers, :projects, :developers_projects, :members, :memberships, :clubs @@ -910,4 +911,10 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_queries(2) { @tagging = Tagging.preload(:taggable).find(t.id) } assert_no_queries { assert ! @tagging.taggable } end + + def test_preloading_has_many_through_with_uniq + mary = Author.includes(:unique_categorized_posts).where(:id => authors(:mary).id).first + assert_equal 1, mary.unique_categorized_posts.length + assert_equal 1, mary.unique_categorized_post_ids.length + end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 44ff01ddc0..77bc369ecc 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -23,7 +23,7 @@ require 'models/category' class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references, :companies, - :subscribers, :books, :subscriptions, :developers + :subscribers, :books, :subscriptions, :developers, :categorizations # Dummies to force column loads so query counts are clean. def setup @@ -473,4 +473,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase category = authors(:david).special_categories.create(:name => "Foo") assert_equal 1, category.categorizations.where(:special => true).count end + + def test_joining_has_many_through_with_uniq + mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first + assert_equal 1, mary.unique_categorized_posts.length + assert_equal 1, mary.unique_categorized_post_ids.length + end end -- cgit v1.2.3 From 14b880fd035fcdf807051398674c9aa89bd3b4d3 Mon Sep 17 00:00:00 2001 From: Jon Leighton <j@jonathanleighton.com> Date: Wed, 15 Dec 2010 23:27:15 +0000 Subject: Fix various issues with the :primary_key option in :through associations [#2421 state:resolved] --- .../associations/through_association_scope.rb | 26 ++++++++-------------- activerecord/lib/active_record/reflection.rb | 6 ++++- .../test/cases/associations/join_model_test.rb | 16 +++++++++++++ activerecord/test/cases/reflection_test.rb | 5 +++++ activerecord/test/models/categorization.rb | 3 +++ activerecord/test/models/post.rb | 10 +++++++++ 6 files changed, 48 insertions(+), 18 deletions(-) diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 2ecd0f054a..5dc5b0c048 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -39,22 +39,22 @@ module ActiveRecord # Build SQL conditions from attributes, qualified by table name. def construct_conditions table = aliased_through_table - conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| + conditions = construct_owner_attributes(@reflection.through_reflection).map do |attr, value| table[attr].eq(value) end conditions << Arel.sql(sql_conditions) if sql_conditions table.create_and(conditions) end - # Associate attributes pointing to owner, quoted. - def construct_quoted_owner_attributes(reflection) + # Associate attributes pointing to owner + def construct_owner_attributes(reflection) if as = reflection.options[:as] - { "#{as}_id" => @owner.id, + { "#{as}_id" => @owner[reflection.active_record_primary_key], "#{as}_type" => @owner.class.base_class.name } elsif reflection.macro == :belongs_to { reflection.klass.primary_key => @owner[reflection.primary_key_name] } else - { reflection.primary_key_name => @owner.id } + { reflection.primary_key_name => @owner[reflection.active_record_primary_key] } end end @@ -74,7 +74,8 @@ module ActiveRecord conditions = [] if @reflection.source_reflection.macro == :belongs_to - reflection_primary_key = @reflection.klass.primary_key + reflection_primary_key = @reflection.source_reflection.options[:primary_key] || + @reflection.klass.primary_key source_primary_key = @reflection.source_reflection.primary_key_name if @reflection.options[:source_type] column = @reflection.source_reflection.options[:foreign_type] @@ -83,7 +84,8 @@ module ActiveRecord end else reflection_primary_key = @reflection.source_reflection.primary_key_name - source_primary_key = @reflection.through_reflection.klass.primary_key + source_primary_key = @reflection.source_reflection.options[:primary_key] || + @reflection.through_reflection.klass.primary_key if @reflection.source_reflection.options[:as] column = "#{@reflection.source_reflection.options[:as]}_type" conditions << @@ -99,16 +101,6 @@ module ActiveRecord right.create_on(right.create_and(conditions))) end - # Construct attributes for associate pointing to owner. - def construct_owner_attributes(reflection) - if as = reflection.options[:as] - { "#{as}_id" => @owner.id, - "#{as}_type" => @owner.class.base_class.name } - else - { reflection.primary_key_name => @owner.id } - end - end - # Construct attributes for :through pointing to owner and associate. def construct_join_attributes(associate) # TODO: revisit this to allow it for deletion, supposing dependent option is supported diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index fe4b518826..b9caa64a0e 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -205,7 +205,11 @@ module ActiveRecord end def association_foreign_key - @association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key + @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key + end + + def active_record_primary_key + @active_record_primary_key ||= options[:primary_key] || active_record.primary_key end def counter_cache_column diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 4581cb1acd..0a57c7883f 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -298,6 +298,22 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert_equal [authors(:mary)], posts(:authorless).authors end + def test_has_many_going_through_join_model_with_custom_primary_key + assert_equal [authors(:david)], posts(:thinking).authors_using_author_id + end + + def test_has_many_going_through_polymorphic_join_model_with_custom_primary_key + assert_equal [tags(:general)], posts(:eager_other).tags_using_author_id + end + + def test_has_many_through_with_custom_primary_key_on_belongs_to_source + assert_equal [authors(:david), authors(:david)], posts(:thinking).author_using_custom_pk + end + + def test_has_many_through_with_custom_primary_key_on_has_many_source + assert_equal [authors(:david)], posts(:thinking).authors_using_custom_pk + end + def test_both_scoped_and_explicit_joins_should_be_respected assert_nothing_raised do Post.send(:with_scope, :find => {:joins => "left outer join comments on comments.id = posts.id"}) do diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 389ca9eae6..912e3c47bb 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -191,6 +191,11 @@ class ReflectionTest < ActiveRecord::TestCase assert_kind_of ThroughReflection, Subscriber.reflect_on_association(:books) end + def test_active_record_primary_key + assert_equal "nick", Subscriber.reflect_on_association(:subscriptions).active_record_primary_key.to_s + assert_equal "name", Author.reflect_on_association(:essay).active_record_primary_key.to_s + end + def test_collection_association assert Pirate.reflect_on_association(:birds).collection? assert Pirate.reflect_on_association(:parrots).collection? diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb index b3fc29fa15..fdb0a11540 100644 --- a/activerecord/test/models/categorization.rb +++ b/activerecord/test/models/categorization.rb @@ -2,6 +2,9 @@ class Categorization < ActiveRecord::Base belongs_to :post belongs_to :category belongs_to :author + + belongs_to :author_using_custom_pk, :class_name => 'Author', :foreign_key => :author_id, :primary_key => :author_address_extra_id + has_many :authors_using_custom_pk, :class_name => 'Author', :foreign_key => :id, :primary_key => :category_id end class SpecialCategorization < ActiveRecord::Base diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 164b499bf0..974e87d2bf 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -69,6 +69,16 @@ class Post < ActiveRecord::Base has_many :categorizations, :foreign_key => :category_id has_many :authors, :through => :categorizations + has_many :categorizations_using_author_id, :primary_key => :author_id, :foreign_key => :post_id, :class_name => 'Categorization' + has_many :authors_using_author_id, :through => :categorizations_using_author_id, :source => :author + + has_many :taggings_using_author_id, :primary_key => :author_id, :as => :taggable, :class_name => 'Tagging' + has_many :tags_using_author_id, :through => :taggings_using_author_id, :source => :tag + + has_many :standard_categorizations, :class_name => 'Categorization', :foreign_key => :post_id + has_many :author_using_custom_pk, :through => :standard_categorizations + has_many :authors_using_custom_pk, :through => :standard_categorizations + has_many :readers has_many :readers_with_person, :include => :person, :class_name => "Reader" has_many :people, :through => :readers -- cgit v1.2.3 From 81cf98d34268546532aa763b2837b3a4c87e60e1 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Thu, 16 Dec 2010 11:37:11 -0200 Subject: dragdrop was tested twice and effects.js was missing, added checks for controls.js, dragdrop.js and effects.js to another test --- railties/test/generators/app_generator_test.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 02c49ab241..0fe5cdc4a8 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -137,7 +137,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "public/javascripts/rails.js" assert_file "public/javascripts/controls.js" assert_file "public/javascripts/dragdrop.js" - assert_file "public/javascripts/dragdrop.js" + assert_file "public/javascripts/effects.js" assert_file "test" end @@ -154,6 +154,9 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "config/application.rb", /#\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(jquery rails\)/ assert_file "public/javascripts/application.js" assert_file "public/javascripts/prototype.js" + assert_file "public/javascripts/controls.js" + assert_file "public/javascripts/dragdrop.js" + assert_file "public/javascripts/effects.js" assert_file "public/javascripts/rails.js", /prototype/ end -- cgit v1.2.3 From e89fff2f6b815529cf729fc8e900a8275c9514e8 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Thu, 16 Dec 2010 21:37:48 +0100 Subject: Bring back config.action_view.cache_template_loading [#5847 state:resolved] --- actionpack/lib/action_view/base.rb | 8 ++++++ actionpack/lib/action_view/railtie.rb | 10 ++++++- actionpack/lib/action_view/template/resolver.rb | 11 +++++-- railties/test/application/configuration_test.rb | 38 +++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 15944138f7..92ff3380b0 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -172,6 +172,14 @@ module ActionView #:nodoc: class << self delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB' delegate :logger, :to => 'ActionController::Base', :allow_nil => true + + def cache_template_loading + ActionView::Resolver.caching? + end + + def cache_template_loading=(value) + ActionView::Resolver.caching = value + end end attr_accessor :_template diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb index 71cd1a788a..501ec07b09 100644 --- a/actionpack/lib/action_view/railtie.rb +++ b/actionpack/lib/action_view/railtie.rb @@ -35,5 +35,13 @@ module ActionView end end end + + initializer "action_view.caching" do |app| + ActiveSupport.on_load(:action_view) do + if app.config.action_view.cache_template_loading.nil? + ActionView::Resolver.caching = app.config.cache_classes + end + end + end end -end \ No newline at end of file +end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index a17454da28..0dccc99d14 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -5,6 +5,13 @@ require "action_view/template" module ActionView # = Action View Resolver class Resolver + cattr_accessor :caching + self.caching = true + + class << self + alias :caching? :caching + end + def initialize @cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2| h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } } @@ -23,9 +30,7 @@ module ActionView private - def caching? - @caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes - end + delegate :caching?, :to => "self.class" # This is what child classes implement. No defaults are needed # because Resolver guarantees that the arguments are present and diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index c12c4a4660..4f4b7beec4 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -280,5 +280,43 @@ module ApplicationTests get "/" assert_equal "/omg/images/foo.jpg", last_response.body end + + test "config.action_view.cache_template_loading with cache_classes default" do + add_to_config "config.cache_classes = true" + require "#{app_path}/config/environment" + require 'action_view/base' + + assert ActionView::Resolver.caching? + end + + test "config.action_view.cache_template_loading without cache_classes default" do + add_to_config "config.cache_classes = false" + require "#{app_path}/config/environment" + require 'action_view/base' + + assert !ActionView::Resolver.caching? + end + + test "config.action_view.cache_template_loading = false" do + add_to_config <<-RUBY + config.cache_classes = true + config.action_view.cache_template_loading = false + RUBY + require "#{app_path}/config/environment" + require 'action_view/base' + + assert !ActionView::Resolver.caching? + end + + test "config.action_view.cache_template_loading = true" do + add_to_config <<-RUBY + config.cache_classes = false + config.action_view.cache_template_loading = true + RUBY + require "#{app_path}/config/environment" + require 'action_view/base' + + assert ActionView::Resolver.caching? + end end end -- cgit v1.2.3 From 40b15f9f389b9394b22cf36567269e54c66c9fc8 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Thu, 16 Dec 2010 22:20:42 +0100 Subject: ActiveRecord::Base.joins should allow single nil argument [#6181 state:resolved] --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- activerecord/test/cases/relations_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 6660df302b..0ab55ae864 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -63,7 +63,7 @@ module ActiveRecord end def joins(*args) - return self if args.blank? + return self if args.compact.blank? relation = clone diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 0b106e3bcd..20bfafbc5e 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -184,6 +184,10 @@ class RelationTest < ActiveRecord::TestCase assert_equal [2, 4, 6, 8, 10], even_ids.sort end + def test_joins_with_nil_argument + assert_nothing_raised { DependentFirm.joins(nil).first } + end + def test_finding_with_hash_conditions_on_joined_table firms = DependentFirm.joins(:account).where({:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}).to_a assert_equal 1, firms.size -- cgit v1.2.3 From f176b2552ecc06634dae53b82a4562d8e80aeed6 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Thu, 16 Dec 2010 22:53:19 +0100 Subject: Changelog and guide entries for config.action_view.cache_template_loading --- actionpack/CHANGELOG | 2 ++ railties/guides/source/configuring.textile | 2 ++ 2 files changed, 4 insertions(+) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index acd9bd5b63..93b29bcc3a 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.1.0 (unreleased)* +* brought back config.action_view.cache_template_loading, which allows to decide whether templates should be cached or not [Piotr Sarnacki] + * url_for and named url helpers now accept :subdomain and :domain as options [Josh Kalderimis] * The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused (check the documentation for examples). [Josh Kalderimis] diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 33319fa40a..d5faf3da2c 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -63,6 +63,8 @@ end * +config.cache_classes+ controls whether or not application classes should be reloaded on each request. Defaults to _true_ in development, _false_ in test and production. Can also be enabled with +threadsafe!+. +* +config.action_view.cache_template_loading+ controls whether or not templates should be reloaded on each request. Defaults to whatever is set for config.cache_classes. + * +config.cache_store+ configures which cache store to use for Rails caching. Options include +:memory_store+, +:file_store+, +:mem_cache_store+ or the name of your own custom class. Defaults to +:file_store+. * +config.colorize_logging+ specifies whether or not to use ANSI color codes when logging information. Defaults to _true_. -- cgit v1.2.3 From 3b9120fa52b76fb8591fe1d0db85d1a940e867d0 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Thu, 16 Dec 2010 20:10:30 -0200 Subject: Generate add_index by default when giving type belongs_to or references --- activerecord/CHANGELOG | 22 ++++++++++++++++++++++ .../active_record/model/templates/migration.rb | 4 ++++ railties/guides/source/getting_started.textile | 4 +++- .../test/generators/scaffold_generator_test.rb | 7 +++++-- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 9c2e311c75..056de18cde 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,27 @@ *Rails 3.1.0 (unreleased)* +* When a model is generated add_index is added by default for belongs_to or references columns + + rails g model post user:belongs_to will generate the following: + + class CreatePosts < ActiveRecord::Migration + def up + create_table :posts do |t| + t.belongs_to :user + + t.timestamps + end + + add_index :posts, :user_id + end + + def down + drop_table :posts + end + end + + [Santiago Pastorino] + * Setting the id of a belongs_to object will update the reference to the object. [#2989 state:resolved] diff --git a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb b/activerecord/lib/rails/generators/active_record/model/templates/migration.rb index 70e064be21..7d4e1a7404 100644 --- a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb +++ b/activerecord/lib/rails/generators/active_record/model/templates/migration.rb @@ -8,6 +8,10 @@ class <%= migration_class_name %> < ActiveRecord::Migration t.timestamps <% end -%> end + +<% attributes.select {|attr| attr.reference? }.each do |attribute| -%> + add_index :<%= table_name %>, :<%= attribute.name %>_id +<% end -%> end def down diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 902b7353c0..e371632d87 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -811,6 +811,8 @@ class CreateComments < ActiveRecord::Migration t.timestamps end + + add_index :comments, :post_id end def self.down @@ -819,7 +821,7 @@ class CreateComments < ActiveRecord::Migration end </ruby> -The +t.references+ line sets up a foreign key column for the association between the two models. Go ahead and run the migration: +The +t.references+ line sets up a foreign key column for the association between the two models. And the +add_index+ line sets up an index for this association column. Go ahead and run the migration: <shell> $ rake db:migrate diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index f366600b16..df787f61ba 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -3,7 +3,7 @@ require 'rails/generators/rails/scaffold/scaffold_generator' class ScaffoldGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper - arguments %w(product_line title:string price:integer) + arguments %w(product_line title:string product:belongs_to user:references) setup :copy_routes @@ -14,7 +14,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/ assert_file "test/unit/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/ assert_file "test/fixtures/product_lines.yml" - assert_migration "db/migrate/create_product_lines.rb" + assert_migration "db/migrate/create_product_lines.rb", /belongs_to :product/ + assert_migration "db/migrate/create_product_lines.rb", /add_index :product_lines, :product_id/ + assert_migration "db/migrate/create_product_lines.rb", /references :user/ + assert_migration "db/migrate/create_product_lines.rb", /add_index :product_lines, :user_id/ # Route assert_file "config/routes.rb" do |route| -- cgit v1.2.3 From 006ec77e60163d57d655479602230672d98e0f10 Mon Sep 17 00:00:00 2001 From: Don Wilson <robert.don.wilson@gmail.com> Date: Thu, 16 Dec 2010 11:37:11 -0200 Subject: Fixed number_with_precision rounding error [#6182 state:resolved] --- actionpack/lib/action_view/helpers/number_helper.rb | 5 +++-- actionpack/test/template/number_helper_test.rb | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index a9400c347f..650828052f 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -270,12 +270,13 @@ module ActionView digits, rounded_number = 1, 0 else digits = (Math.log10(number.abs) + 1).floor - rounded_number = BigDecimal.new((number / 10 ** (digits - precision)).to_s).round.to_f * 10 ** (digits - precision) + rounded_number = (BigDecimal.new(number.to_s) / 10 ** (digits - precision)).round.to_f * 10 ** (digits - precision) + digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed end precision -= digits precision = precision > 0 ? precision : 0 #don't let it be negative else - rounded_number = BigDecimal.new((number * (10 ** precision)).to_s).round.to_f / 10 ** precision + rounded_number = BigDecimal.new(number.to_s).round(precision).to_f end formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options) if strip_insignificant_zeros diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index ab127521ad..6b234cfbb9 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -100,6 +100,11 @@ class NumberHelperTest < ActionView::TestCase assert_equal("0", number_with_precision(0, :precision => 0)) assert_equal("0.00100", number_with_precision(0.001, :precision => 5)) assert_equal("0.001", number_with_precision(0.00111, :precision => 3)) + assert_equal("10.00", number_with_precision(9.995, :precision => 2)) + assert_equal("11.00", number_with_precision(10.995, :precision => 2)) + assert_equal("10.0", number_with_precision(9.995, :precision => 3, :significant => true)) + assert_equal("9.99", number_with_precision(9.994, :precision => 3, :significant => true)) + assert_equal("11.0", number_with_precision(10.995, :precision => 3, :significant => true)) end def test_number_with_precision_with_custom_delimiter_and_separator -- cgit v1.2.3 From f7e33620caffecb7f065c5e4f5a19597f8c35cdd Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Fri, 17 Dec 2010 16:06:38 +0100 Subject: Allow to use BUNDLE_GEMFILE env variable in boot.rb [#6184 state:resolved] --- railties/lib/rails/generators/rails/app/templates/config/boot.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb index ab6cb374de..f7b36a13da 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/boot.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb @@ -3,7 +3,7 @@ require 'rubygems' # Set up gems listed in the Gemfile. gemfile = File.expand_path('../../Gemfile', __FILE__) begin - ENV['BUNDLE_GEMFILE'] = gemfile + ENV['BUNDLE_GEMFILE'] ||= gemfile require 'bundler' Bundler.setup rescue Bundler::GemNotFound => e -- cgit v1.2.3 From 07567aea5f0c6f14a53f511ab4cc46201b669795 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Fri, 17 Dec 2010 16:11:47 +0100 Subject: Revert "Fixed number_with_precision rounding error [#6182 state:resolved]" This reverts commit 006ec77e60163d57d655479602230672d98e0f10. --- actionpack/lib/action_view/helpers/number_helper.rb | 5 ++--- actionpack/test/template/number_helper_test.rb | 5 ----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 650828052f..a9400c347f 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -270,13 +270,12 @@ module ActionView digits, rounded_number = 1, 0 else digits = (Math.log10(number.abs) + 1).floor - rounded_number = (BigDecimal.new(number.to_s) / 10 ** (digits - precision)).round.to_f * 10 ** (digits - precision) - digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed + rounded_number = BigDecimal.new((number / 10 ** (digits - precision)).to_s).round.to_f * 10 ** (digits - precision) end precision -= digits precision = precision > 0 ? precision : 0 #don't let it be negative else - rounded_number = BigDecimal.new(number.to_s).round(precision).to_f + rounded_number = BigDecimal.new((number * (10 ** precision)).to_s).round.to_f / 10 ** precision end formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options) if strip_insignificant_zeros diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index 6b234cfbb9..ab127521ad 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -100,11 +100,6 @@ class NumberHelperTest < ActionView::TestCase assert_equal("0", number_with_precision(0, :precision => 0)) assert_equal("0.00100", number_with_precision(0.001, :precision => 5)) assert_equal("0.001", number_with_precision(0.00111, :precision => 3)) - assert_equal("10.00", number_with_precision(9.995, :precision => 2)) - assert_equal("11.00", number_with_precision(10.995, :precision => 2)) - assert_equal("10.0", number_with_precision(9.995, :precision => 3, :significant => true)) - assert_equal("9.99", number_with_precision(9.994, :precision => 3, :significant => true)) - assert_equal("11.0", number_with_precision(10.995, :precision => 3, :significant => true)) end def test_number_with_precision_with_custom_delimiter_and_separator -- cgit v1.2.3 From e22885f3a9476990c07235512f5579740eec059b Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Fri, 17 Dec 2010 19:29:33 +0100 Subject: Use require 'bundler/setup' instead of directly calling Bundler.setup in config/boot.rb --- .../lib/rails/generators/rails/app/templates/config/boot.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb index f7b36a13da..4489e58688 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/boot.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb @@ -1,13 +1,6 @@ require 'rubygems' # Set up gems listed in the Gemfile. -gemfile = File.expand_path('../../Gemfile', __FILE__) -begin - ENV['BUNDLE_GEMFILE'] ||= gemfile - require 'bundler' - Bundler.setup -rescue Bundler::GemNotFound => e - STDERR.puts e.message - STDERR.puts "Try running `bundle install`." - exit! -end if File.exist?(gemfile) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) + +require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) -- cgit v1.2.3 From 5008d33eee4767cd84b1087ceec827f1eaec90d0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <aaron.patterson@gmail.com> Date: Fri, 17 Dec 2010 11:31:08 -0800 Subject: dry up case / when statement --- actionpack/lib/action_dispatch/routing/polymorphic_routes.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 49e237f8db..82c4fadb50 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -99,11 +99,9 @@ module ActionDispatch record = extract_record(record_or_hash_or_array) record = record.to_model if record.respond_to?(:to_model) - args = case record_or_hash_or_array - when Hash; [ record_or_hash_or_array ] - when Array; record_or_hash_or_array.dup - else [ record_or_hash_or_array ] - end + args = Array === record_or_hash_or_array ? + record_or_hash_or_array.dup : + [ record_or_hash_or_array ] inflection = if options[:action] && options[:action].to_s == "new" args.pop -- cgit v1.2.3 From f71809f25761440cbec9463bbc082987c90a471b Mon Sep 17 00:00:00 2001 From: Don Wilson <robert.don.wilson@gmail.com> Date: Fri, 17 Dec 2010 09:56:03 -0700 Subject: Fixed number_with_precision rounding error [#6182 state:resolved] --- actionpack/lib/action_view/helpers/number_helper.rb | 5 +++-- actionpack/test/template/number_helper_test.rb | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index a9400c347f..26f8dce3c3 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -270,12 +270,13 @@ module ActionView digits, rounded_number = 1, 0 else digits = (Math.log10(number.abs) + 1).floor - rounded_number = BigDecimal.new((number / 10 ** (digits - precision)).to_s).round.to_f * 10 ** (digits - precision) + rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision) + digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed end precision -= digits precision = precision > 0 ? precision : 0 #don't let it be negative else - rounded_number = BigDecimal.new((number * (10 ** precision)).to_s).round.to_f / 10 ** precision + rounded_number = BigDecimal.new(number.to_s).round(precision).to_f end formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options) if strip_insignificant_zeros diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index ab127521ad..156b7cb5ff 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -100,6 +100,8 @@ class NumberHelperTest < ActionView::TestCase assert_equal("0", number_with_precision(0, :precision => 0)) assert_equal("0.00100", number_with_precision(0.001, :precision => 5)) assert_equal("0.001", number_with_precision(0.00111, :precision => 3)) + assert_equal("10.00", number_with_precision(9.995, :precision => 2)) + assert_equal("11.00", number_with_precision(10.995, :precision => 2)) end def test_number_with_precision_with_custom_delimiter_and_separator @@ -125,6 +127,9 @@ class NumberHelperTest < ActionView::TestCase assert_equal "0.0001", number_with_precision(0.0001, :precision => 1, :significant => true ) assert_equal "0.000100", number_with_precision(0.0001, :precision => 3, :significant => true ) assert_equal "0.0001", number_with_precision(0.0001111, :precision => 1, :significant => true ) + assert_equal "10.0", number_with_precision(9.995, :precision => 3, :significant => true) + assert_equal "9.99", number_with_precision(9.994, :precision => 3, :significant => true) + assert_equal "11.0", number_with_precision(10.995, :precision => 3, :significant => true) end def test_number_with_precision_with_strip_insignificant_zeros -- cgit v1.2.3 From 4eb0d580a2b0d11f7285bca1665e0d42b3266f15 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Sat, 18 Dec 2010 19:42:03 +0100 Subject: Use gemspec instead of gem rails in Gemfile --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 4e13331626..adfaf7c0e4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,7 @@ source 'http://rubygems.org' +gemspec + if ENV['AREL'] gem "arel", :path => ENV['AREL'] else @@ -7,7 +9,6 @@ else end gem "rack", :git => "git://github.com/rack/rack.git" -gem "rails", :path => File.dirname(__FILE__) gem "rake", ">= 0.8.7" gem "mocha", ">= 0.9.8" -- cgit v1.2.3 From d9b732fcadc001b6757e648761acf97833827b58 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Sat, 18 Dec 2010 21:47:27 +0100 Subject: Fix tests in railties --- railties/test/railties/engine_test.rb | 20 ++++++++++---------- railties/test/railties/railtie_test.rb | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 6b64a19741..388f940f81 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -90,7 +90,7 @@ module RailtiesTest env = Rack::MockRequest.env_for("/bukkits") response = Rails.application.call(env) - assert_equal ["HELLO WORLD"], response[2] + assert_equal rack_body(["HELLO WORLD"]), rack_body(response[2]) end test "it provides routes as default endpoint" do @@ -117,7 +117,7 @@ module RailtiesTest env = Rack::MockRequest.env_for("/bukkits/foo") response = Rails.application.call(env) - assert_equal ["foo"], response[2] + assert_equal rack_body(["foo"]), rack_body(response[2]) end test "engine can load its own plugins" do @@ -357,15 +357,15 @@ module RailtiesTest env = Rack::MockRequest.env_for("/foo") response = Rails.application.call(env) - assert_equal ["Something... Something... Something..."], response[2] + assert_equal rack_body(["Something... Something... Something..."]), rack_body(response[2]) env = Rack::MockRequest.env_for("/foo/show") response = Rails.application.call(env) - assert_equal ["/foo"], response[2] + assert_equal rack_body(["/foo"]), rack_body(response[2]) env = Rack::MockRequest.env_for("/foo/bar") response = Rails.application.call(env) - assert_equal ["It's a bar."], response[2] + assert_equal rack_body(["It's a bar."]), rack_body(response[2]) end test "isolated engine should include only its own routes and helpers" do @@ -466,23 +466,23 @@ module RailtiesTest env = Rack::MockRequest.env_for("/bukkits/from_app") response = AppTemplate::Application.call(env) - assert_equal ["false"], response[2] + assert_equal rack_body(["false"]), rack_body(response[2]) env = Rack::MockRequest.env_for("/bukkits/foo/show") response = AppTemplate::Application.call(env) - assert_equal ["/bukkits/foo"], response[2] + assert_equal rack_body(["/bukkits/foo"]), rack_body(response[2]) env = Rack::MockRequest.env_for("/bukkits/foo") response = AppTemplate::Application.call(env) - assert_equal ["Helped."], response[2] + assert_equal rack_body(["Helped."]), rack_body(response[2]) env = Rack::MockRequest.env_for("/bukkits/routes_helpers_in_view") response = AppTemplate::Application.call(env) - assert_equal ["/bukkits/foo, /bar"], response[2] + assert_equal rack_body(["/bukkits/foo, /bar"]), rack_body(response[2]) env = Rack::MockRequest.env_for("/bukkits/polymorphic_path_without_namespace") response = AppTemplate::Application.call(env) - assert_equal ["/bukkits/posts/1"], response[2] + assert_equal rack_body(["/bukkits/posts/1"]), rack_body(response[2]) end test "isolated engine should avoid namespace in names if that's possible" do diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb index 6d194eecba..7ea8364ae9 100644 --- a/railties/test/railties/railtie_test.rb +++ b/railties/test/railties/railtie_test.rb @@ -21,10 +21,10 @@ module RailtiesTest test "Railtie provides railtie_name" do begin - class ::Foo < Rails::Railtie ; end - assert_equal "foo", ::Foo.railtie_name + class ::FooBarBaz < Rails::Railtie ; end + assert_equal "foo_bar_baz", ::FooBarBaz.railtie_name ensure - Object.send(:remove_const, :"Foo") + Object.send(:remove_const, :"FooBarBaz") end end -- cgit v1.2.3 From bcf4e4f2b02157cecc1f1727a95cdf5bfa471771 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson <david@loudthinking.com> Date: Sat, 18 Dec 2010 13:38:05 -0800 Subject: Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with SHA2 encryption and salting --- activemodel/CHANGELOG | 5 +- activemodel/lib/active_model.rb | 1 + activemodel/lib/active_model/secure_password.rb | 73 +++++++++++++++++++++++++ activemodel/test/cases/secure_password_test.rb | 42 ++++++++++++++ activemodel/test/models/user.rb | 8 +++ activerecord/CHANGELOG | 19 +++++++ activerecord/lib/active_record/base.rb | 1 + 7 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 activemodel/lib/active_model/secure_password.rb create mode 100644 activemodel/test/cases/secure_password_test.rb create mode 100644 activemodel/test/models/user.rb diff --git a/activemodel/CHANGELOG b/activemodel/CHANGELOG index 4e963c77b0..a19d029217 100644 --- a/activemodel/CHANGELOG +++ b/activemodel/CHANGELOG @@ -1,15 +1,18 @@ *Rails 3.1.0 (unreleased)* -* No changes +* Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with SHA2 encryption and salting [DHH] + *Rails 3.0.2 (unreleased)* * No changes + *Rails 3.0.1 (October 15, 2010)* * No Changes, just a version bump. + *Rails 3.0.0 (August 29, 2010)* * Added ActiveModel::MassAssignmentSecurity [Eric Chapweske, Josh Kalderimis] diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index be0f24ff92..dd6ee058cc 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -42,6 +42,7 @@ module ActiveModel autoload :Naming autoload :Observer, 'active_model/observing' autoload :Observing + autoload :SecurePassword autoload :Serialization autoload :TestCase autoload :Translation diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb new file mode 100644 index 0000000000..0599ce6865 --- /dev/null +++ b/activemodel/lib/active_model/secure_password.rb @@ -0,0 +1,73 @@ +require 'digest/sha2' + +module ActiveModel + module SecurePassword + extend ActiveSupport::Concern + + module ClassMethods + # Adds methods to set and authenticate against a SHA2-encrypted and salted password. + # This mechanism requires you to have password_digest and password_salt attributes. + # + # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), + # and strength of password (at least 6 chars, not "password") are automatically added. + # You can add more validations by hand if need be. + # + # Example using Active Record (which automatically includes ActiveModel::SecurePassword): + # + # # Schema: User(name:string, password_digest:string, password_salt:string) + # class User < ActiveRecord::Base + # has_secure_password + # end + # + # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") + # user.save # => false, password not long enough + # user.password = "mUc3m00RsqyRe" + # user.save # => false, confirmation doesn't match + # user.password_confirmation = "mUc3m00RsqyRe" + # user.save # => true + # user.authenticate("notright") # => false + # user.authenticate("mUc3m00RsqyRe") # => user + # User.find_by_name("david").try(:authenticate, "notright") # => nil + # User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user + def has_secure_password + attr_reader :password + attr_accessor :password_confirmation + + attr_protected(:password_digest, :password_salt) if respond_to?(:attr_protected) + + validates_confirmation_of :password + validates_presence_of :password_digest + validate :password_must_be_strong + end + end + + module InstanceMethods + # Returns self if the password is correct, otherwise false. + def authenticate(unencrypted_password) + password_digest == encrypt_password(unencrypted_password) ? self : false + end + + # Encrypts the password into the password_digest attribute. + def password=(unencrypted_password) + @password = unencrypted_password + self.password_digest = encrypt_password(unencrypted_password) + end + + private + def salt_for_password + self.password_salt ||= self.object_id.to_s + rand.to_s + end + + def encrypt_password(unencrypted_password) + Digest::SHA2.hexdigest(unencrypted_password + salt_for_password) + end + + def password_must_be_strong + if @password.present? + errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 + errors.add(:password, "can't be 'password'") if @password == "password" + end + end + end + end +end \ No newline at end of file diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb new file mode 100644 index 0000000000..7d7c51e6cb --- /dev/null +++ b/activemodel/test/cases/secure_password_test.rb @@ -0,0 +1,42 @@ +require 'cases/helper' +require 'models/user' + +class SecurePasswordTest < ActiveModel::TestCase + setup do + @user = User.new + end + + test "password must be present" do + assert !@user.valid? + assert_equal 1, @user.errors.size + end + + test "password must match confirmation" do + @user.password = "thiswillberight" + @user.password_confirmation = "wrong" + + assert !@user.valid? + + @user.password_confirmation = "thiswillberight" + + assert @user.valid? + end + + test "password must pass validation rules" do + @user.password = "password" + assert !@user.valid? + + @user.password = "short" + assert !@user.valid? + + @user.password = "plentylongenough" + assert @user.valid? + end + + test "authenticate" do + @user.password = "secret" + + assert !@user.authenticate("wrong") + assert @user.authenticate("secret") + end +end \ No newline at end of file diff --git a/activemodel/test/models/user.rb b/activemodel/test/models/user.rb new file mode 100644 index 0000000000..e221bb8091 --- /dev/null +++ b/activemodel/test/models/user.rb @@ -0,0 +1,8 @@ +class User + include ActiveModel::Validations + include ActiveModel::SecurePassword + + has_secure_password + + attr_accessor :password_digest, :password_salt +end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 056de18cde..0f7e65e8cd 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,24 @@ *Rails 3.1.0 (unreleased)* +* Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with SHA2 encryption and salting [DHH]. Example: + + # Schema: User(name:string, password_digest:string, password_salt:string) + class User < ActiveRecord::Base + has_secure_password + end + + user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") + user.save # => false, password not long enough + user.password = "mUc3m00RsqyRe" + user.save # => false, confirmation doesn't match + user.password_confirmation = "mUc3m00RsqyRe" + user.save # => true + user.authenticate("notright") # => false + user.authenticate("mUc3m00RsqyRe") # => user + User.find_by_name("david").try(:authenticate, "notright") # => nil + User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user + + * When a model is generated add_index is added by default for belongs_to or references columns rails g model post user:belongs_to will generate the following: diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d0f33c1d18..858ccebbfa 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1851,6 +1851,7 @@ MSG include ActiveModel::MassAssignmentSecurity include Callbacks, ActiveModel::Observing, Timestamp include Associations, AssociationPreload, NamedScope + include ActiveModel::SecurePassword # AutosaveAssociation needs to be included before Transactions, because we want # #save_with_autosave_associations to be wrapped inside a transaction. -- cgit v1.2.3 From 39b5ea6e01f6fc652cc63ab4e7e701cfaa9f9405 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson <david@loudthinking.com> Date: Sat, 18 Dec 2010 15:39:32 -0800 Subject: Switch from SHA2 to BCrypt (easy Windows compatibility is coming shortly with new compiled gem) --- activemodel/CHANGELOG | 2 +- activemodel/activemodel.gemspec | 2 ++ activemodel/lib/active_model/secure_password.rb | 14 +++++++------- activerecord/CHANGELOG | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/activemodel/CHANGELOG b/activemodel/CHANGELOG index a19d029217..9dd5e03685 100644 --- a/activemodel/CHANGELOG +++ b/activemodel/CHANGELOG @@ -1,6 +1,6 @@ *Rails 3.1.0 (unreleased)* -* Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with SHA2 encryption and salting [DHH] +* Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with BCrypt encryption and salting [DHH] *Rails 3.0.2 (unreleased)* diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index 1f38e70c36..64aa7ad922 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -22,4 +22,6 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('builder', '~> 3.0.0') s.add_dependency('i18n', '~> 0.5.0') + s.add_dependency('bcrypt-ruby', '~> 2.1.2') + end diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 0599ce6865..900205cf3f 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,4 +1,4 @@ -require 'digest/sha2' +require 'bcrypt' module ActiveModel module SecurePassword @@ -44,13 +44,17 @@ module ActiveModel module InstanceMethods # Returns self if the password is correct, otherwise false. def authenticate(unencrypted_password) - password_digest == encrypt_password(unencrypted_password) ? self : false + if BCrypt::Password.new(password_digest) == (unencrypted_password + salt_for_password) + self + else + false + end end # Encrypts the password into the password_digest attribute. def password=(unencrypted_password) @password = unencrypted_password - self.password_digest = encrypt_password(unencrypted_password) + self.password_digest = BCrypt::Password.create(unencrypted_password + salt_for_password) end private @@ -58,10 +62,6 @@ module ActiveModel self.password_salt ||= self.object_id.to_s + rand.to_s end - def encrypt_password(unencrypted_password) - Digest::SHA2.hexdigest(unencrypted_password + salt_for_password) - end - def password_must_be_strong if @password.present? errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 0f7e65e8cd..9d22842cb3 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,6 +1,6 @@ *Rails 3.1.0 (unreleased)* -* Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with SHA2 encryption and salting [DHH]. Example: +* Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with BCrypt encryption and salting [DHH]. Example: # Schema: User(name:string, password_digest:string, password_salt:string) class User < ActiveRecord::Base -- cgit v1.2.3 From bd9dc4ff23ab1e185df6ccf35d6058c0a3d234ce Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson <david@loudthinking.com> Date: Sat, 18 Dec 2010 19:09:07 -0800 Subject: BCrypt does its own salting, lovely! --- activemodel/lib/active_model/secure_password.rb | 23 +++++++++++------------ activemodel/test/cases/secure_password_test.rb | 11 +++++++++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 900205cf3f..54191f41df 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -4,17 +4,19 @@ module ActiveModel module SecurePassword extend ActiveSupport::Concern + WEAK_PASSWORDS = %w( password qwerty 123456 ) + module ClassMethods - # Adds methods to set and authenticate against a SHA2-encrypted and salted password. - # This mechanism requires you to have password_digest and password_salt attributes. + # Adds methods to set and authenticate against a BCrypt password. + # This mechanism requires you to have a password_digest attribute. # # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), - # and strength of password (at least 6 chars, not "password") are automatically added. + # and strength of password (at least 6 chars, not "password", etc) are automatically added. # You can add more validations by hand if need be. # # Example using Active Record (which automatically includes ActiveModel::SecurePassword): # - # # Schema: User(name:string, password_digest:string, password_salt:string) + # # Schema: User(name:string, password_digest:string) # class User < ActiveRecord::Base # has_secure_password # end @@ -33,7 +35,7 @@ module ActiveModel attr_reader :password attr_accessor :password_confirmation - attr_protected(:password_digest, :password_salt) if respond_to?(:attr_protected) + attr_protected(:password_digest) if respond_to?(:attr_protected) validates_confirmation_of :password validates_presence_of :password_digest @@ -44,7 +46,7 @@ module ActiveModel module InstanceMethods # Returns self if the password is correct, otherwise false. def authenticate(unencrypted_password) - if BCrypt::Password.new(password_digest) == (unencrypted_password + salt_for_password) + if BCrypt::Password.new(password_digest) == unencrypted_password self else false @@ -54,18 +56,15 @@ module ActiveModel # Encrypts the password into the password_digest attribute. def password=(unencrypted_password) @password = unencrypted_password - self.password_digest = BCrypt::Password.create(unencrypted_password + salt_for_password) + self.password_digest = BCrypt::Password.create(unencrypted_password) end - private - def salt_for_password - self.password_salt ||= self.object_id.to_s + rand.to_s - end + private def password_must_be_strong if @password.present? errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 - errors.add(:password, "can't be 'password'") if @password == "password" + errors.add(:password, "is a too weak and common") if WEAK_PASSWORDS.include?(@password) end end end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 7d7c51e6cb..62fffce233 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -33,6 +33,17 @@ class SecurePasswordTest < ActiveModel::TestCase assert @user.valid? end + test "too weak passwords" do + @user.password = "123456" + assert !@user.valid? + + @user.password = "password" + assert !@user.valid? + + @user.password = "d9034rfjlakj34RR$!!" + assert @user.valid? + end + test "authenticate" do @user.password = "secret" -- cgit v1.2.3 From d592fa946d43fdadf23f872a5c3334fb4f108f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com> Date: Sun, 19 Dec 2010 09:28:15 +0100 Subject: Avoid warnings and fix small typo on SecurePassword. --- activemodel/lib/active_model/secure_password.rb | 6 +++--- activemodel/test/cases/secure_password_test.rb | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 54191f41df..3e7d3174ac 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -62,9 +62,9 @@ module ActiveModel private def password_must_be_strong - if @password.present? - errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 - errors.add(:password, "is a too weak and common") if WEAK_PASSWORDS.include?(@password) + if password.present? + errors.add(:password, "must be longer than 6 characters") unless password.size > 6 + errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) end end end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 62fffce233..c46a092d2d 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -34,11 +34,13 @@ class SecurePasswordTest < ActiveModel::TestCase end test "too weak passwords" do - @user.password = "123456" + @user.password = "012345" assert !@user.valid? + assert_equal ["must be longer than 6 characters"], @user.errors[:password] @user.password = "password" assert !@user.valid? + assert_equal ["is too weak and common"], @user.errors[:password] @user.password = "d9034rfjlakj34RR$!!" assert @user.valid? -- cgit v1.2.3 From b8f6dd8cbb2de870a4805800fd89148a417bc612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com> Date: Sun, 19 Dec 2010 09:30:46 +0100 Subject: Add missing require and remove extra module. --- activemodel/lib/active_model/secure_password.rb | 39 ++++++++++++------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 3e7d3174ac..cd6256e3d6 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/object/blank' require 'bcrypt' module ActiveModel @@ -43,30 +44,28 @@ module ActiveModel end end - module InstanceMethods - # Returns self if the password is correct, otherwise false. - def authenticate(unencrypted_password) - if BCrypt::Password.new(password_digest) == unencrypted_password - self - else - false - end + # Returns self if the password is correct, otherwise false. + def authenticate(unencrypted_password) + if BCrypt::Password.new(password_digest) == unencrypted_password + self + else + false end + end - # Encrypts the password into the password_digest attribute. - def password=(unencrypted_password) - @password = unencrypted_password - self.password_digest = BCrypt::Password.create(unencrypted_password) - end + # Encrypts the password into the password_digest attribute. + def password=(unencrypted_password) + @password = unencrypted_password + self.password_digest = BCrypt::Password.create(unencrypted_password) + end + private - private - def password_must_be_strong - if password.present? - errors.add(:password, "must be longer than 6 characters") unless password.size > 6 - errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) - end - end + def password_must_be_strong + if password.present? + errors.add(:password, "must be longer than 6 characters") unless password.size > 6 + errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) + end end end end \ No newline at end of file -- cgit v1.2.3 From 432556b9238b182dbd380a8f9936e3ca5dc6fa57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com> Date: Sun, 19 Dec 2010 09:34:31 +0100 Subject: Make password messages translatable. --- activemodel/lib/active_model/locale/en.yml | 4 ++++ activemodel/lib/active_model/secure_password.rb | 4 ++-- activemodel/test/cases/secure_password_test.rb | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index 44425b4a28..c370a1f33e 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -25,3 +25,7 @@ en: less_than_or_equal_to: "must be less than or equal to %{count}" odd: "must be odd" even: "must be even" + + attributes: + password: + unsecure: "is too weak and common" diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index cd6256e3d6..cd997a61db 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -63,8 +63,8 @@ module ActiveModel def password_must_be_strong if password.present? - errors.add(:password, "must be longer than 6 characters") unless password.size > 6 - errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) + errors.add(:password, :too_short, :count => 7) unless password.size > 6 + errors.add(:password, :unsecure) if WEAK_PASSWORDS.include?(password) end end end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index c46a092d2d..e75bfca02a 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -36,7 +36,7 @@ class SecurePasswordTest < ActiveModel::TestCase test "too weak passwords" do @user.password = "012345" assert !@user.valid? - assert_equal ["must be longer than 6 characters"], @user.errors[:password] + assert_equal ["is too short (minimum is 7 characters)"], @user.errors[:password] @user.password = "password" assert !@user.valid? -- cgit v1.2.3 From 863de37b05900f037132656812b7ef550d096ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com> Date: Sun, 19 Dec 2010 09:37:08 +0100 Subject: 'unsecure' => 'insecure' --- activemodel/lib/active_model/locale/en.yml | 2 +- activemodel/lib/active_model/secure_password.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index c370a1f33e..4a27355c6c 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -28,4 +28,4 @@ en: attributes: password: - unsecure: "is too weak and common" + insecure: "is too weak and common" diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index cd997a61db..1dcd389f8a 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -64,7 +64,7 @@ module ActiveModel def password_must_be_strong if password.present? errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :unsecure) if WEAK_PASSWORDS.include?(password) + errors.add(:password, :insecure) if WEAK_PASSWORDS.include?(password) end end end -- cgit v1.2.3 From a39a3337698ca42ab158dc3b4b08ea75039b8a89 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar <mikel@rubyx.com> Date: Sun, 19 Dec 2010 20:39:54 +1100 Subject: Added ability to specify which passwords you want as weak passwords --- activemodel/lib/active_model/secure_password.rb | 31 ++++++++++++++---- activemodel/test/cases/secure_password_test.rb | 42 ++++++++++++++++++------- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 1dcd389f8a..06af18dfd1 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -5,12 +5,10 @@ module ActiveModel module SecurePassword extend ActiveSupport::Concern - WEAK_PASSWORDS = %w( password qwerty 123456 ) - module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. - # + # # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), # and strength of password (at least 6 chars, not "password", etc) are automatically added. # You can add more validations by hand if need be. @@ -24,9 +22,9 @@ module ActiveModel # # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") # user.save # => false, password not long enough - # user.password = "mUc3m00RsqyRe" + # user.password = "mUc3m00RsqyRe" # user.save # => false, confirmation doesn't match - # user.password_confirmation = "mUc3m00RsqyRe" + # user.password_confirmation = "mUc3m00RsqyRe" # user.save # => true # user.authenticate("notright") # => false # user.authenticate("mUc3m00RsqyRe") # => user @@ -42,6 +40,27 @@ module ActiveModel validates_presence_of :password_digest validate :password_must_be_strong end + + # Allows you to specify the set of weak passwords that will be validated against + # if you specify has_secure_password in your model. + # + # The default set of weak passwords are: + # + # class User < ActiveRecord::Base + # weak_passwords = %w( password qwerty 123456 mypass ) + # end + def weak_passwords=(*values) + @weak_passwords = values.flatten + end + + # Returns the list of current weak passwords defined. Defaults to the standard + # list of 'password', 'qwerty' and '123456' + # + # User.weak_passwords #=> ['password', 'qwerty', '123456'] + def weak_passwords + @weak_passwords ||= %w( password qwerty 123456 ) + end + end # Returns self if the password is correct, otherwise false. @@ -64,7 +83,7 @@ module ActiveModel def password_must_be_strong if password.present? errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :insecure) if WEAK_PASSWORDS.include?(password) + errors.add(:password, :insecure) if self.class.weak_passwords.include?(password) end end end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index e75bfca02a..5d788d7a1e 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -2,37 +2,57 @@ require 'cases/helper' require 'models/user' class SecurePasswordTest < ActiveModel::TestCase + setup do + User.weak_passwords = %w( password qwerty 123456 ) @user = User.new end + test "there should be a list of default weak passwords" do + assert_equal %w( password qwerty 123456 ), User.weak_passwords + end + + test "specifying the list of passwords" do + User.weak_passwords = %w( pass ) + assert_equal %w( pass ), User.weak_passwords + end + + test "adding to the list of passwords" do + User.weak_passwords << 'pass' + @user.password = "password" + assert !@user.valid? + + @user.password = "pass" + assert !@user.valid? + end + test "password must be present" do assert !@user.valid? assert_equal 1, @user.errors.size end - + test "password must match confirmation" do @user.password = "thiswillberight" @user.password_confirmation = "wrong" - + assert !@user.valid? - + @user.password_confirmation = "thiswillberight" - + assert @user.valid? end - + test "password must pass validation rules" do @user.password = "password" assert !@user.valid? - + @user.password = "short" assert !@user.valid? - + @user.password = "plentylongenough" assert @user.valid? end - + test "too weak passwords" do @user.password = "012345" assert !@user.valid? @@ -41,14 +61,14 @@ class SecurePasswordTest < ActiveModel::TestCase @user.password = "password" assert !@user.valid? assert_equal ["is too weak and common"], @user.errors[:password] - + @user.password = "d9034rfjlakj34RR$!!" assert @user.valid? end - + test "authenticate" do @user.password = "secret" - + assert !@user.authenticate("wrong") assert @user.authenticate("secret") end -- cgit v1.2.3 From fa14df08a845abc61a6ed5bed1742ba59a4c2b8d Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar <mikel@rubyx.com> Date: Sun, 19 Dec 2010 20:54:15 +1100 Subject: Fix incorrect docs --- activemodel/lib/active_model/secure_password.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 06af18dfd1..c5f32fa3fa 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -42,9 +42,7 @@ module ActiveModel end # Allows you to specify the set of weak passwords that will be validated against - # if you specify has_secure_password in your model. - # - # The default set of weak passwords are: + # if you specify has_secure_password in your model: # # class User < ActiveRecord::Base # weak_passwords = %w( password qwerty 123456 mypass ) -- cgit v1.2.3 From 6c217f98db6984c8d000103ac3cf66970eeaeb3f Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar <mikel@rubyx.com> Date: Sun, 19 Dec 2010 21:28:37 +1100 Subject: Add set_weak_passwords call in alignment with set_table_name. --- activemodel/lib/active_model/secure_password.rb | 16 +++++++++++----- activemodel/test/cases/secure_password_test.rb | 5 +++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index c5f32fa3fa..6703d5daac 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -41,12 +41,18 @@ module ActiveModel validate :password_must_be_strong end - # Allows you to specify the set of weak passwords that will be validated against - # if you specify has_secure_password in your model: + # Specify the weak passwords to be used in the model: # - # class User < ActiveRecord::Base - # weak_passwords = %w( password qwerty 123456 mypass ) + # class User + # weak_passwords %w( password qwerty 123456 mypass ) # end + def set_weak_passwords(values) + @weak_passwords = values + end + + # Change the list of weak passwords that will be validated against: + # + # User.weak_passwords = %w( password qwerty 123456 mypass ) def weak_passwords=(*values) @weak_passwords = values.flatten end @@ -55,7 +61,7 @@ module ActiveModel # list of 'password', 'qwerty' and '123456' # # User.weak_passwords #=> ['password', 'qwerty', '123456'] - def weak_passwords + def weak_passwords(values = nil) @weak_passwords ||= %w( password qwerty 123456 ) end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 5d788d7a1e..2c3da2c93e 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -17,6 +17,11 @@ class SecurePasswordTest < ActiveModel::TestCase assert_equal %w( pass ), User.weak_passwords end + test "specifying the list of passwords in the class" do + User.send(:set_weak_passwords, ['pass']) + assert_equal %w( pass ), User.weak_passwords + end + test "adding to the list of passwords" do User.weak_passwords << 'pass' @user.password = "password" -- cgit v1.2.3 From 6d80f3a1ba52248cb8837af5ad40a23dc4fea213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com> Date: Sun, 19 Dec 2010 13:30:19 +0100 Subject: Use class_attribute to ensure weak_passwords are inheritable. --- activemodel/lib/active_model/secure_password.rb | 26 ++++++++----------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 6703d5daac..8da08f34ec 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,10 +1,16 @@ require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/class/attribute' require 'bcrypt' module ActiveModel module SecurePassword extend ActiveSupport::Concern + included do + class_attribute :weak_passwords + self.weak_passwords = %w( password qwerty 123456 ) + end + module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. @@ -44,27 +50,11 @@ module ActiveModel # Specify the weak passwords to be used in the model: # # class User - # weak_passwords %w( password qwerty 123456 mypass ) + # set_weak_passwords %w( password qwerty 123456 mypass ) # end def set_weak_passwords(values) - @weak_passwords = values - end - - # Change the list of weak passwords that will be validated against: - # - # User.weak_passwords = %w( password qwerty 123456 mypass ) - def weak_passwords=(*values) - @weak_passwords = values.flatten + self.weak_passwords = values end - - # Returns the list of current weak passwords defined. Defaults to the standard - # list of 'password', 'qwerty' and '123456' - # - # User.weak_passwords #=> ['password', 'qwerty', '123456'] - def weak_passwords(values = nil) - @weak_passwords ||= %w( password qwerty 123456 ) - end - end # Returns self if the password is correct, otherwise false. -- cgit v1.2.3 From b50024f2fff8a3da46135d4838fe1cda8cb95ceb Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Sun, 19 Dec 2010 15:11:51 +0100 Subject: Do not load all application's task in generated engine --- .../generators/rails/plugin_new/templates/Rakefile | 9 +-- railties/lib/rails/tasks/engine.rake | 66 ++++++++++++++++++++++ 2 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 railties/lib/rails/tasks/engine.rake diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile index 25292f59ad..5704e75a29 100755 --- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile @@ -16,9 +16,6 @@ Rake::RDocTask.new(:rdoc) do |rdoc| end <% if full? && !options[:skip_active_record] -%> -namespace :app do - ENGINE_PATH = File.expand_path("..", __FILE__) - load File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__) -end -<% end -%> - +APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__) +load 'rails/tasks/engine.rake' +<% end %> diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake new file mode 100644 index 0000000000..593be93797 --- /dev/null +++ b/railties/lib/rails/tasks/engine.rake @@ -0,0 +1,66 @@ +task "load_app" do + namespace :app do + load APP_RAKEFILE + end + + if !defined?(ENGINE_PATH) || !ENGINE_PATH + ENGINE_PATH = find_engine_path(APP_RAKEFILE) + end +end + +namespace :db do + task :reset => [:load_app, :"app:db:reset"] + + desc "Migrate the database (options: VERSION=x, VERBOSE=false)." + task :migrate => [:load_app, :"app:db:migrate"] + task :"migrate:up" => [:load_app, :"app:db:migrate:up"] + task :"migrate:down" => [:load_app, :"app:db:migrate:down"] + task :"migrate:redo" => [:load_app, :"app:db:migrate:redo"] + task :"migrate:reset" => [:load_app, :"app:db:migrate:reset"] + + desc "Display status of migrations" + task :"migrate:status" => [:load_app, :"app:db:migrate:status"] + + desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)' + task :create => [:load_app, :"app:db:create"] + task :"create:all" => [:load_app, :"app:db:create:all"] + + desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)' + task :drop => [:load_app, :"app:db:drop"] + task :"drop:all" => [:load_app, :"app:db:drop:all"] + + desc "Load fixtures into the current environment's database." + task :"fixtures:load" => [:load_app, :"app:db:fixtures:load"] + + desc "Rolls the schema back to the previous version (specify steps w/ STEP=n)." + task :rollback => [:load_app, :"app:db:rollback"] + + desc "Create a db/schema.rb file that can be portably used against any DB supported by AR" + task :"schema:dump" => [:load_app, :"app:db:schema:dump"] + + desc "Load a schema.rb file into the database" + task :"schema:load" => [:load_app, :"app:db:schema:load"] + + desc "Load the seed data from db/seeds.rb" + task :seed => [:load_app, :"app:db:seed"] + + desc "Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)" + task :setup => [:load_app, :"app:db:setup"] + + desc "Dump the database structure to an SQL file" + task :"structure:dump" => [:load_app, :"app:db:structure:dump"] + + desc "Retrieves the current schema version number" + task :version => [:load_app, :"app:db:version"] +end + + +def find_engine_path(path) + return if path == "/" + + if Rails::Engine.find(path) + path + else + find_engine_path(File.expand_path('..', path)) + end +end -- cgit v1.2.3 From c29a4485a2bd13fdf8a062b5078e9e305d3601f5 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Sun, 19 Dec 2010 15:59:55 +0100 Subject: Refactoring of application's task in engine.rake --- railties/lib/rails/tasks/engine.rake | 43 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake index 593be93797..2f0e7be896 100644 --- a/railties/lib/rails/tasks/engine.rake +++ b/railties/lib/rails/tasks/engine.rake @@ -8,53 +8,56 @@ task "load_app" do end end +def app_task(name) + task name => [:load_app, "app:db:#{name}"] +end + namespace :db do - task :reset => [:load_app, :"app:db:reset"] + app_task "reset" desc "Migrate the database (options: VERSION=x, VERBOSE=false)." - task :migrate => [:load_app, :"app:db:migrate"] - task :"migrate:up" => [:load_app, :"app:db:migrate:up"] - task :"migrate:down" => [:load_app, :"app:db:migrate:down"] - task :"migrate:redo" => [:load_app, :"app:db:migrate:redo"] - task :"migrate:reset" => [:load_app, :"app:db:migrate:reset"] + app_task "migrate" + app_task "migrate:up" + app_task "migrate:down" + app_task "migrate:redo" + app_task "migrate:reset" desc "Display status of migrations" - task :"migrate:status" => [:load_app, :"app:db:migrate:status"] + app_task "migrate:status" desc 'Create the database from config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)' - task :create => [:load_app, :"app:db:create"] - task :"create:all" => [:load_app, :"app:db:create:all"] + app_task "create" + app_task "create:all" desc 'Drops the database for the current Rails.env (use db:drop:all to drop all databases)' - task :drop => [:load_app, :"app:db:drop"] - task :"drop:all" => [:load_app, :"app:db:drop:all"] + app_task "drop" + app_task "drop:all" desc "Load fixtures into the current environment's database." - task :"fixtures:load" => [:load_app, :"app:db:fixtures:load"] + app_task "fixtures:load" desc "Rolls the schema back to the previous version (specify steps w/ STEP=n)." - task :rollback => [:load_app, :"app:db:rollback"] + app_task "rollback" desc "Create a db/schema.rb file that can be portably used against any DB supported by AR" - task :"schema:dump" => [:load_app, :"app:db:schema:dump"] + app_task "schema:dump" desc "Load a schema.rb file into the database" - task :"schema:load" => [:load_app, :"app:db:schema:load"] + app_task "schema:load" desc "Load the seed data from db/seeds.rb" - task :seed => [:load_app, :"app:db:seed"] + app_task "seed" desc "Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)" - task :setup => [:load_app, :"app:db:setup"] + app_task "setup" desc "Dump the database structure to an SQL file" - task :"structure:dump" => [:load_app, :"app:db:structure:dump"] + app_task "structure:dump" desc "Retrieves the current schema version number" - task :version => [:load_app, :"app:db:version"] + app_task "version" end - def find_engine_path(path) return if path == "/" -- cgit v1.2.3 From f249d07c44d2f18e508ac83153c9f9cb8bce340b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sun, 19 Dec 2010 13:53:08 -0200 Subject: Add missing require --- actionpack/lib/action_controller/caching/pages.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index 3e57d2c236..f305ed30fa 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -1,4 +1,5 @@ require 'fileutils' +require 'active_support/concern' require 'active_support/core_ext/class/attribute_accessors' module ActionController #:nodoc: -- cgit v1.2.3 From 7b875953b83e6c53ecd5f818e6b044047656f51b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sun, 19 Dec 2010 13:54:30 -0200 Subject: Use Concern in Rails::Railtie::Configurable --- railties/lib/rails/railtie/configurable.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb index b6d4ed2312..532ebf65f5 100644 --- a/railties/lib/rails/railtie/configurable.rb +++ b/railties/lib/rails/railtie/configurable.rb @@ -1,9 +1,9 @@ +require 'active_support/concern' + module Rails class Railtie module Configurable - def self.included(base) - base.extend ClassMethods - end + extend ActiveSupport::Concern module ClassMethods delegate :config, :to => :instance -- cgit v1.2.3 From 13e5a26dbb3c11b18438d10b752e87fdf4727172 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sun, 19 Dec 2010 13:58:59 -0200 Subject: Fix indentation --- railties/lib/rails/railtie/configurable.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb index 532ebf65f5..53796c74cf 100644 --- a/railties/lib/rails/railtie/configurable.rb +++ b/railties/lib/rails/railtie/configurable.rb @@ -26,9 +26,9 @@ module Rails protected - def method_missing(*args, &block) - instance.send(*args, &block) - end + def method_missing(*args, &block) + instance.send(*args, &block) + end end end end -- cgit v1.2.3 From 37e643120a5f4ba9fa15b74d6f489f7a0bb66c0d Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Sun, 19 Dec 2010 17:56:54 +0100 Subject: Temporary fix for railties test for newest rack changes --- railties/test/railties/engine_test.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 388f940f81..552a846330 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -358,14 +358,17 @@ module RailtiesTest env = Rack::MockRequest.env_for("/foo") response = Rails.application.call(env) assert_equal rack_body(["Something... Something... Something..."]), rack_body(response[2]) + response[2].close env = Rack::MockRequest.env_for("/foo/show") response = Rails.application.call(env) assert_equal rack_body(["/foo"]), rack_body(response[2]) + response[2].close env = Rack::MockRequest.env_for("/foo/bar") response = Rails.application.call(env) assert_equal rack_body(["It's a bar."]), rack_body(response[2]) + response[2].close end test "isolated engine should include only its own routes and helpers" do @@ -467,22 +470,27 @@ module RailtiesTest env = Rack::MockRequest.env_for("/bukkits/from_app") response = AppTemplate::Application.call(env) assert_equal rack_body(["false"]), rack_body(response[2]) + response[2].close env = Rack::MockRequest.env_for("/bukkits/foo/show") response = AppTemplate::Application.call(env) assert_equal rack_body(["/bukkits/foo"]), rack_body(response[2]) + response[2].close env = Rack::MockRequest.env_for("/bukkits/foo") response = AppTemplate::Application.call(env) assert_equal rack_body(["Helped."]), rack_body(response[2]) + response[2].close env = Rack::MockRequest.env_for("/bukkits/routes_helpers_in_view") response = AppTemplate::Application.call(env) assert_equal rack_body(["/bukkits/foo, /bar"]), rack_body(response[2]) + response[2].close env = Rack::MockRequest.env_for("/bukkits/polymorphic_path_without_namespace") response = AppTemplate::Application.call(env) assert_equal rack_body(["/bukkits/posts/1"]), rack_body(response[2]) + response[2].close end test "isolated engine should avoid namespace in names if that's possible" do -- cgit v1.2.3 From 08ccd29b5b1e3badc2176a8036fea138b774c38f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sun, 19 Dec 2010 14:58:14 -0200 Subject: Remove weak_passwords list and the length/strong password validator, leave that up to the programmer --- activemodel/lib/active_model/locale/en.yml | 4 -- activemodel/lib/active_model/secure_password.rb | 37 +++--------------- activemodel/test/cases/secure_password_test.rb | 50 +------------------------ activerecord/CHANGELOG | 10 ++--- 4 files changed, 12 insertions(+), 89 deletions(-) diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index 4a27355c6c..44425b4a28 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -25,7 +25,3 @@ en: less_than_or_equal_to: "must be less than or equal to %{count}" odd: "must be odd" even: "must be even" - - attributes: - password: - insecure: "is too weak and common" diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 8da08f34ec..f4411cde80 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,22 +1,16 @@ -require 'active_support/core_ext/object/blank' -require 'active_support/core_ext/class/attribute' +require 'active_support/concern' require 'bcrypt' module ActiveModel module SecurePassword extend ActiveSupport::Concern - included do - class_attribute :weak_passwords - self.weak_passwords = %w( password qwerty 123456 ) - end - module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. # - # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), - # and strength of password (at least 6 chars, not "password", etc) are automatically added. + # Validations for presence of password, confirmation of password (using + # a "password_confirmation" attribute) are automatically added. # You can add more validations by hand if need be. # # Example using Active Record (which automatically includes ActiveModel::SecurePassword): @@ -26,8 +20,8 @@ module ActiveModel # has_secure_password # end # - # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") - # user.save # => false, password not long enough + # user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch") + # user.save # => false, password required # user.password = "mUc3m00RsqyRe" # user.save # => false, confirmation doesn't match # user.password_confirmation = "mUc3m00RsqyRe" @@ -44,16 +38,6 @@ module ActiveModel validates_confirmation_of :password validates_presence_of :password_digest - validate :password_must_be_strong - end - - # Specify the weak passwords to be used in the model: - # - # class User - # set_weak_passwords %w( password qwerty 123456 mypass ) - # end - def set_weak_passwords(values) - self.weak_passwords = values end end @@ -71,14 +55,5 @@ module ActiveModel @password = unencrypted_password self.password_digest = BCrypt::Password.create(unencrypted_password) end - - private - - def password_must_be_strong - if password.present? - errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :insecure) if self.class.weak_passwords.include?(password) - end - end end -end \ No newline at end of file +end diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 2c3da2c93e..79be715730 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -4,33 +4,9 @@ require 'models/user' class SecurePasswordTest < ActiveModel::TestCase setup do - User.weak_passwords = %w( password qwerty 123456 ) @user = User.new end - test "there should be a list of default weak passwords" do - assert_equal %w( password qwerty 123456 ), User.weak_passwords - end - - test "specifying the list of passwords" do - User.weak_passwords = %w( pass ) - assert_equal %w( pass ), User.weak_passwords - end - - test "specifying the list of passwords in the class" do - User.send(:set_weak_passwords, ['pass']) - assert_equal %w( pass ), User.weak_passwords - end - - test "adding to the list of passwords" do - User.weak_passwords << 'pass' - @user.password = "password" - assert !@user.valid? - - @user.password = "pass" - assert !@user.valid? - end - test "password must be present" do assert !@user.valid? assert_equal 1, @user.errors.size @@ -47,34 +23,10 @@ class SecurePasswordTest < ActiveModel::TestCase assert @user.valid? end - test "password must pass validation rules" do - @user.password = "password" - assert !@user.valid? - - @user.password = "short" - assert !@user.valid? - - @user.password = "plentylongenough" - assert @user.valid? - end - - test "too weak passwords" do - @user.password = "012345" - assert !@user.valid? - assert_equal ["is too short (minimum is 7 characters)"], @user.errors[:password] - - @user.password = "password" - assert !@user.valid? - assert_equal ["is too weak and common"], @user.errors[:password] - - @user.password = "d9034rfjlakj34RR$!!" - assert @user.valid? - end - test "authenticate" do @user.password = "secret" assert !@user.authenticate("wrong") assert @user.authenticate("secret") end -end \ No newline at end of file +end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 9d22842cb3..fd571c4ca4 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -6,12 +6,12 @@ class User < ActiveRecord::Base has_secure_password end - - user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") - user.save # => false, password not long enough - user.password = "mUc3m00RsqyRe" + + user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch") + user.save # => false, password required + user.password = "mUc3m00RsqyRe" user.save # => false, confirmation doesn't match - user.password_confirmation = "mUc3m00RsqyRe" + user.password_confirmation = "mUc3m00RsqyRe" user.save # => true user.authenticate("notright") # => false user.authenticate("mUc3m00RsqyRe") # => user -- cgit v1.2.3 From 5fb42ac478923c78ebc3e457acea4dc6726e3796 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sun, 19 Dec 2010 18:50:18 -0200 Subject: Tests and docs which explain the use of validate with a block and without arguments --- activemodel/lib/active_model/validations.rb | 12 +++++++++++- activemodel/test/cases/validations_test.rb | 10 +++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index b044caa8d3..6cb015a144 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -104,7 +104,7 @@ module ActiveModel # end # end # - # Or with a block which is passed with the current record to be validated: + # With a block which is passed with the current record to be validated: # # class Comment # include ActiveModel::Validations @@ -118,6 +118,16 @@ module ActiveModel # end # end # + # Or with a block where self points to the current record to be validated: + # + # class Comment + # include ActiveModel::Validations + # + # validate do + # errors.add(:base, "Must be friends to leave a comment") unless commenter.friend_of?(commentee) + # end + # end + # def validate(*args, &block) options = args.extract_options! if options.key?(:on) diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 55b477dd10..e90dc7d4e3 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -148,6 +148,14 @@ class ValidationsTest < ActiveModel::TestCase end def test_validate_block + Topic.validate { errors.add("title", "will never be valid") } + t = Topic.new("title" => "Title", "content" => "whatever") + assert t.invalid? + assert t.errors[:title].any? + assert_equal ["will never be valid"], t.errors["title"] + end + + def test_validate_block_with_params Topic.validate { |topic| topic.errors.add("title", "will never be valid") } t = Topic.new("title" => "Title", "content" => "whatever") assert t.invalid? @@ -187,7 +195,7 @@ class ValidationsTest < ActiveModel::TestCase assert t.invalid? assert_equal "can't be blank", t.errors["title"].first Topic.validates_presence_of :title, :author_name - Topic.validate {|topic| topic.errors.add('author_email_address', 'will never be valid')} + Topic.validate {errors.add('author_email_address', 'will never be valid')} Topic.validates_length_of :title, :content, :minimum => 2 t = Topic.new :title => '' -- cgit v1.2.3 From ab2bde45f8140a3ebf8b478e688ef612fc4181fc Mon Sep 17 00:00:00 2001 From: Santiago Pastorino <santiago@wyeworks.com> Date: Sun, 19 Dec 2010 19:15:26 -0200 Subject: Remove require AS::Concern from places where is already in --- actionpack/lib/action_controller/caching/pages.rb | 1 - activemodel/lib/active_model/dirty.rb | 1 - activemodel/lib/active_model/secure_password.rb | 1 - railties/lib/rails/railtie/configurable.rb | 2 -- 4 files changed, 5 deletions(-) diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index f305ed30fa..3e57d2c236 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -1,5 +1,4 @@ require 'fileutils' -require 'active_support/concern' require 'active_support/core_ext/class/attribute_accessors' module ActionController #:nodoc: diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index 1dfd0b6132..a479795d51 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -1,5 +1,4 @@ require 'active_model/attribute_methods' -require 'active_support/concern' require 'active_support/hash_with_indifferent_access' require 'active_support/core_ext/object/duplicable' diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index f4411cde80..52941942b8 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,4 +1,3 @@ -require 'active_support/concern' require 'bcrypt' module ActiveModel diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb index 53796c74cf..920ab67ff1 100644 --- a/railties/lib/rails/railtie/configurable.rb +++ b/railties/lib/rails/railtie/configurable.rb @@ -1,5 +1,3 @@ -require 'active_support/concern' - module Rails class Railtie module Configurable -- cgit v1.2.3 From 916c25aeeeab6ef0f2375a6063fb36a43ed55346 Mon Sep 17 00:00:00 2001 From: John Firebaugh <john_firebaugh@us.ibm.com> Date: Tue, 23 Nov 2010 19:21:48 -0800 Subject: Use assert_body --- railties/test/railties/engine_test.rb | 36 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 552a846330..8a003228b7 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -90,7 +90,7 @@ module RailtiesTest env = Rack::MockRequest.env_for("/bukkits") response = Rails.application.call(env) - assert_equal rack_body(["HELLO WORLD"]), rack_body(response[2]) + assert_body "HELLO WORLD", response end test "it provides routes as default endpoint" do @@ -117,7 +117,7 @@ module RailtiesTest env = Rack::MockRequest.env_for("/bukkits/foo") response = Rails.application.call(env) - assert_equal rack_body(["foo"]), rack_body(response[2]) + assert_body "foo", response end test "engine can load its own plugins" do @@ -293,23 +293,15 @@ module RailtiesTest env = Rack::MockRequest.env_for("/app.html") response = Rails.application.call(env) - assert_equal rack_body(response[2]), rack_body(File.open(File.join(app_path, "public/app.html"))) + assert_body File.read(File.join(app_path, "public/app.html")), response env = Rack::MockRequest.env_for("/bukkits/bukkits.html") response = Rails.application.call(env) - assert_equal rack_body(response[2]), rack_body(File.open(File.join(@plugin.path, "public/bukkits.html"))) + assert_body File.read(File.join(@plugin.path, "public/bukkits.html")), response env = Rack::MockRequest.env_for("/bukkits/file_from_app.html") response = Rails.application.call(env) - assert_equal rack_body(response[2]), rack_body(File.open(File.join(app_path, "public/bukkits/file_from_app.html"))) - end - - def rack_body(obj) - buffer = "" - obj.each do |part| - buffer << part - end - buffer + assert_body File.read(File.join(app_path, "public/bukkits/file_from_app.html")), response end test "shared engine should include application's helpers and own helpers" do @@ -357,17 +349,17 @@ module RailtiesTest env = Rack::MockRequest.env_for("/foo") response = Rails.application.call(env) - assert_equal rack_body(["Something... Something... Something..."]), rack_body(response[2]) + assert_body "Something... Something... Something...", response response[2].close env = Rack::MockRequest.env_for("/foo/show") response = Rails.application.call(env) - assert_equal rack_body(["/foo"]), rack_body(response[2]) + assert_body "/foo", response response[2].close env = Rack::MockRequest.env_for("/foo/bar") response = Rails.application.call(env) - assert_equal rack_body(["It's a bar."]), rack_body(response[2]) + assert_body "It's a bar.", response response[2].close end @@ -469,27 +461,27 @@ module RailtiesTest env = Rack::MockRequest.env_for("/bukkits/from_app") response = AppTemplate::Application.call(env) - assert_equal rack_body(["false"]), rack_body(response[2]) + assert_body "false", response response[2].close env = Rack::MockRequest.env_for("/bukkits/foo/show") response = AppTemplate::Application.call(env) - assert_equal rack_body(["/bukkits/foo"]), rack_body(response[2]) + assert_body "/bukkits/foo", response response[2].close env = Rack::MockRequest.env_for("/bukkits/foo") response = AppTemplate::Application.call(env) - assert_equal rack_body(["Helped."]), rack_body(response[2]) + assert_body "Helped.", response response[2].close env = Rack::MockRequest.env_for("/bukkits/routes_helpers_in_view") response = AppTemplate::Application.call(env) - assert_equal rack_body(["/bukkits/foo, /bar"]), rack_body(response[2]) + assert_body "/bukkits/foo, /bar", response response[2].close env = Rack::MockRequest.env_for("/bukkits/polymorphic_path_without_namespace") response = AppTemplate::Application.call(env) - assert_equal rack_body(["/bukkits/posts/1"]), rack_body(response[2]) + assert_body "/bukkits/posts/1", response response[2].close end @@ -551,7 +543,7 @@ module RailtiesTest env = Rack::MockRequest.env_for("/bukkits/posts/new") response = AppTemplate::Application.call(env) - assert rack_body(response[2]) =~ /name="post\[title\]"/ + assert extract_body(response) =~ /name="post\[title\]"/ end test "loading seed data" do -- cgit v1.2.3 From d4f995301bace57070e37ac97b9b18a70aed2230 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Sun, 19 Dec 2010 23:36:29 +0100 Subject: More love for engines tests --- railties/test/railties/engine_test.rb | 134 ++++++++++++++++------------------ 1 file changed, 61 insertions(+), 73 deletions(-) diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 8a003228b7..92aa025238 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -1,12 +1,15 @@ require "isolation/abstract_unit" require "railties/shared_tests" require 'stringio' +require 'rack/test' +require 'rack/file' module RailtiesTest class EngineTest < Test::Unit::TestCase include ActiveSupport::Testing::Isolation include SharedTests + include Rack::Test::Methods def setup build_app @@ -87,10 +90,8 @@ module RailtiesTest boot_rails - env = Rack::MockRequest.env_for("/bukkits") - response = Rails.application.call(env) - - assert_body "HELLO WORLD", response + get("/bukkits") + assert_equal "HELLO WORLD", last_response.body end test "it provides routes as default endpoint" do @@ -115,9 +116,8 @@ module RailtiesTest boot_rails - env = Rack::MockRequest.env_for("/bukkits/foo") - response = Rails.application.call(env) - assert_body "foo", response + get("/bukkits/foo") + assert_equal "foo", last_response.body end test "engine can load its own plugins" do @@ -191,13 +191,11 @@ module RailtiesTest boot_rails env = Rack::MockRequest.env_for("/") - response = Bukkits::Engine.call(env) - + Bukkits::Engine.call(env) assert_equal Bukkits::Engine.routes, env['action_dispatch.routes'] env = Rack::MockRequest.env_for("/") - response = Rails.application.call(env) - + Rails.application.call(env) assert_equal Rails.application.routes, env['action_dispatch.routes'] end @@ -230,6 +228,12 @@ module RailtiesTest <%= stylesheet_link_tag("foo") %> ERB + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + mount Bukkits::Engine => "/bukkits" + end + RUBY + add_to_config 'config.asset_path = "/omg%s"' boot_rails @@ -239,9 +243,8 @@ module RailtiesTest ::Bukkits::Engine.config.asset_path = "/bukkits%s" - env = Rack::MockRequest.env_for("/foo") - response = Bukkits::Engine.call(env) - stripped_body = response[2].body.split("\n").map(&:strip).join + get("/bukkits/foo") + stripped_body = last_response.body.split("\n").map(&:strip).join expected = "/omg/bukkits/images/foo.png" + "<script src=\"/omg/bukkits/javascripts/foo.js\" type=\"text/javascript\"></script>" + @@ -264,14 +267,16 @@ module RailtiesTest end RUBY - boot_rails + app_file "config/routes.rb", <<-RUBY + AppTemplate::Application.routes.draw do + mount Bukkits::Engine => "/bukkits" + end + RUBY - env = Rack::MockRequest.env_for("/foo") - response = Bukkits::Engine.call(env) - stripped_body = response[2].body.strip + boot_rails - expected = "/bukkits/images/foo.png" - assert_equal expected, stripped_body + get("/bukkits/foo") + assert_equal "/bukkits/images/foo.png", last_response.body.strip end test "engine's files are served via ActionDispatch::Static" do @@ -291,17 +296,14 @@ module RailtiesTest boot_rails - env = Rack::MockRequest.env_for("/app.html") - response = Rails.application.call(env) - assert_body File.read(File.join(app_path, "public/app.html")), response + get("/app.html") + assert_equal File.read(File.join(app_path, "public/app.html")), last_response.body - env = Rack::MockRequest.env_for("/bukkits/bukkits.html") - response = Rails.application.call(env) - assert_body File.read(File.join(@plugin.path, "public/bukkits.html")), response + get("/bukkits/bukkits.html") + assert_equal File.read(File.join(@plugin.path, "public/bukkits.html")), last_response.body - env = Rack::MockRequest.env_for("/bukkits/file_from_app.html") - response = Rails.application.call(env) - assert_body File.read(File.join(app_path, "public/bukkits/file_from_app.html")), response + get("/bukkits/file_from_app.html") + assert_equal File.read(File.join(app_path, "public/bukkits/file_from_app.html")), last_response.body end test "shared engine should include application's helpers and own helpers" do @@ -347,20 +349,14 @@ module RailtiesTest boot_rails - env = Rack::MockRequest.env_for("/foo") - response = Rails.application.call(env) - assert_body "Something... Something... Something...", response - response[2].close + get("/foo") + assert_equal "Something... Something... Something...", last_response.body - env = Rack::MockRequest.env_for("/foo/show") - response = Rails.application.call(env) - assert_body "/foo", response - response[2].close + get("/foo/show") + assert_equal "/foo", last_response.body - env = Rack::MockRequest.env_for("/foo/bar") - response = Rails.application.call(env) - assert_body "It's a bar.", response - response[2].close + get("/foo/bar") + assert_equal "It's a bar.", last_response.body end test "isolated engine should include only its own routes and helpers" do @@ -459,30 +455,20 @@ module RailtiesTest assert ::Bukkits::MyMailer.method_defined?(:foo_path) assert !::Bukkits::MyMailer.method_defined?(:bar_path) - env = Rack::MockRequest.env_for("/bukkits/from_app") - response = AppTemplate::Application.call(env) - assert_body "false", response - response[2].close - - env = Rack::MockRequest.env_for("/bukkits/foo/show") - response = AppTemplate::Application.call(env) - assert_body "/bukkits/foo", response - response[2].close - - env = Rack::MockRequest.env_for("/bukkits/foo") - response = AppTemplate::Application.call(env) - assert_body "Helped.", response - response[2].close - - env = Rack::MockRequest.env_for("/bukkits/routes_helpers_in_view") - response = AppTemplate::Application.call(env) - assert_body "/bukkits/foo, /bar", response - response[2].close - - env = Rack::MockRequest.env_for("/bukkits/polymorphic_path_without_namespace") - response = AppTemplate::Application.call(env) - assert_body "/bukkits/posts/1", response - response[2].close + get("/bukkits/from_app") + assert_equal "false", last_response.body + + get("/bukkits/foo/show") + assert_equal "/bukkits/foo", last_response.body + + get("/bukkits/foo") + assert_equal "Helped.", last_response.body + + get("/bukkits/routes_helpers_in_view") + assert_equal "/bukkits/foo, /bar", last_response.body + + get("/bukkits/polymorphic_path_without_namespace") + assert_equal "/bukkits/posts/1", last_response.body end test "isolated engine should avoid namespace in names if that's possible" do @@ -541,9 +527,8 @@ module RailtiesTest boot_rails - env = Rack::MockRequest.env_for("/bukkits/posts/new") - response = AppTemplate::Application.call(env) - assert extract_body(response) =~ /name="post\[title\]"/ + get("/bukkits/posts/new") + assert_match /name="post\[title\]"/, last_response.body end test "loading seed data" do @@ -612,16 +597,14 @@ module RailtiesTest end RUBY - require 'rack/test' - extend Rack::Test::Methods - boot_rails require "#{rails_root}/config/environment" - get "/foo" + + get("/foo") assert_equal "foo", last_response.body - get "/bukkits/bar" + get("/bukkits/bar") assert_equal "bar", last_response.body end @@ -785,5 +768,10 @@ module RailtiesTest assert_match %r{bukkits/stylesheets/foo.css}, last_response.body assert_match %r{bukkits/javascripts/foo.js}, last_response.body end + + private + def app + Rails.application + end end end -- cgit v1.2.3 From 0f7c970e4f1cf0f3bcc01c22a6a3038cb3e34668 Mon Sep 17 00:00:00 2001 From: John Firebaugh <john_firebaugh@us.ibm.com> Date: Tue, 23 Nov 2010 18:04:05 -0800 Subject: Introduce ActionDispatch::Reloader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the implementation on the 2-3-stable branch, patches by Hongli Lai <hongli@phusion.nl>, and helpful suggestions from José Valim. Hongli Lai's patches included locking around the request cycle; this is now handled by Rack::Lock (https://github.com/rack/rack/issues/issue/87/). [#2873] Signed-off-by: José Valim <jose.valim@gmail.com> --- actionpack/lib/action_dispatch.rb | 1 + .../lib/action_dispatch/middleware/reloader.rb | 82 ++++++++++++ actionpack/test/dispatch/reloader_test.rb | 139 +++++++++++++++++++++ railties/lib/rails/application.rb | 1 + railties/test/application/middleware_test.rb | 7 ++ 5 files changed, 230 insertions(+) create mode 100644 actionpack/lib/action_dispatch/middleware/reloader.rb create mode 100644 actionpack/test/dispatch/reloader_test.rb diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 50faf666e6..50d0d191c1 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -53,6 +53,7 @@ module ActionDispatch autoload :Flash autoload :Head autoload :ParamsParser + autoload :Reloader autoload :RemoteIp autoload :Rescue autoload :ShowExceptions diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb new file mode 100644 index 0000000000..b84410fc62 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/reloader.rb @@ -0,0 +1,82 @@ +module ActionDispatch + # ActionDispatch::Reloader provides to_prepare and to_cleanup callbacks. + # These are analogs of ActionDispatch::Callback's before and after + # callbacks, with the difference that to_cleanup is not called until the + # request is fully complete -- that is, after #close has been called on + # the request body. This is important for streaming responses such as the + # following: + # + # self.response_body = lambda { |response, output| + # # code here which refers to application models + # } + # + # Cleanup callbacks will not be called until after the response_body lambda + # is evaluated, ensuring that it can refer to application models and other + # classes before they are unloaded. + # + # By default, ActionDispatch::Reloader is included in the middleware stack + # only in the development environment. + # + class Reloader + include ActiveSupport::Callbacks + + define_callbacks :prepare, :scope => :name + define_callbacks :cleanup, :scope => :name + + # Add a preparation callback. Preparation callbacks are run before each + # request. + # + # If a symbol with a block is given, the symbol is used as an identifier. + # That allows to_prepare to be called again with the same identifier to + # replace the existing callback. Passing an identifier is a suggested + # practice if the code adding a preparation block may be reloaded. + def self.to_prepare(*args, &block) + first_arg = args.first + if first_arg.is_a?(Symbol) && block_given? + remove_method :"__#{first_arg}" if method_defined?(:"__#{first_arg}") + define_method :"__#{first_arg}", &block + set_callback(:prepare, :"__#{first_arg}") + else + set_callback(:prepare, *args, &block) + end + end + + # Add a cleanup callback. Cleanup callbacks are run after each request is + # complete (after #close is called on the response body). + def self.to_cleanup(&block) + set_callback(:cleanup, &block) + end + + def self.prepare! + new(nil).send(:_run_prepare_callbacks) + end + + def self.cleanup! + new(nil).send(:_run_cleanup_callbacks) + end + + def self.reload! + prepare! + cleanup! + end + + def initialize(app) + @app = app + end + + module CleanupOnClose + def close + super if defined?(super) + ensure + ActionDispatch::Reloader.cleanup! + end + end + + def call(env) + _run_prepare_callbacks + response = @app.call(env) + response[2].extend(CleanupOnClose) + response + end + end +end diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb new file mode 100644 index 0000000000..58ef346b5c --- /dev/null +++ b/actionpack/test/dispatch/reloader_test.rb @@ -0,0 +1,139 @@ +require 'abstract_unit' + +class ReloaderTest < Test::Unit::TestCase + Reloader = ActionDispatch::Reloader + + def test_prepare_callbacks + a = b = c = nil + Reloader.to_prepare { |*args| a = b = c = 1 } + Reloader.to_prepare { |*args| b = c = 2 } + Reloader.to_prepare { |*args| c = 3 } + + # Ensure to_prepare callbacks are not run when defined + assert_nil a || b || c + + # Run callbacks + call_and_return_body + + assert_equal 1, a + assert_equal 2, b + assert_equal 3, c + end + + def test_to_prepare_with_identifier_replaces + a = b = 0 + Reloader.to_prepare(:unique_id) { |*args| a = b = 1 } + Reloader.to_prepare(:unique_id) { |*args| a = 2 } + + call_and_return_body + assert_equal 2, a + assert_equal 0, b + end + + class MyBody < Array + def initialize(&block) + @on_close = block + end + + def foo + "foo" + end + + def bar + "bar" + end + + def close + @on_close.call if @on_close + end + end + + def test_returned_body_object_always_responds_to_close + body = call_and_return_body + assert body.respond_to?(:close) + end + + def test_returned_body_object_behaves_like_underlying_object + body = call_and_return_body do + b = MyBody.new + b << "hello" + b << "world" + [200, { "Content-Type" => "text/html" }, b] + end + assert_equal 2, body.size + assert_equal "hello", body[0] + assert_equal "world", body[1] + assert_equal "foo", body.foo + assert_equal "bar", body.bar + end + + def test_it_calls_close_on_underlying_object_when_close_is_called_on_body + close_called = false + body = call_and_return_body do + b = MyBody.new do + close_called = true + end + [200, { "Content-Type" => "text/html" }, b] + end + body.close + assert close_called + end + + def test_returned_body_object_responds_to_all_methods_supported_by_underlying_object + body = call_and_return_body do + [200, { "Content-Type" => "text/html" }, MyBody.new] + end + assert body.respond_to?(:size) + assert body.respond_to?(:each) + assert body.respond_to?(:foo) + assert body.respond_to?(:bar) + end + + def test_cleanup_callbacks_are_called_when_body_is_closed + cleaned = false + Reloader.to_cleanup { cleaned = true } + + body = call_and_return_body + assert !cleaned + + body.close + assert cleaned + end + + def test_prepare_callbacks_arent_called_when_body_is_closed + prepared = false + Reloader.to_prepare { prepared = true } + + body = call_and_return_body + prepared = false + + body.close + assert !prepared + end + + def test_manual_reloading + prepared = cleaned = false + Reloader.to_prepare { prepared = true } + Reloader.to_cleanup { cleaned = true } + + Reloader.prepare! + assert prepared + assert !cleaned + + prepared = cleaned = false + Reloader.cleanup! + assert !prepared + assert cleaned + + prepared = cleaned = false + Reloader.reload! + assert prepared + assert cleaned + end + + private + def call_and_return_body(&block) + @reloader ||= Reloader.new(block || proc {[200, {}, 'response']}) + @reloader.call({'rack.input' => StringIO.new('')})[2] + end +end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index b248bc737c..3b7505ce37 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -156,6 +156,7 @@ module Rails middleware.use ::ActionDispatch::ShowExceptions, config.consider_all_requests_local if config.action_dispatch.show_exceptions middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header + middleware.use ::ActionDispatch::Reloader unless config.cache_classes middleware.use ::ActionDispatch::Callbacks, !config.cache_classes middleware.use ::ActionDispatch::Cookies diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 173ac40b12..a2217888e4 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -27,6 +27,7 @@ module ApplicationTests "ActionDispatch::ShowExceptions", "ActionDispatch::RemoteIp", "Rack::Sendfile", + "ActionDispatch::Reloader", "ActionDispatch::Callbacks", "ActiveRecord::ConnectionAdapters::ConnectionManagement", "ActiveRecord::QueryCache", @@ -81,6 +82,12 @@ module ApplicationTests assert !middleware.include?("ActionDispatch::ShowExceptions") end + test "removes ActionDispatch::Reloader if cache_classes is true" do + add_to_config "config.cache_classes = true" + boot! + assert !middleware.include?("ActionDispatch::Reloader") + end + test "use middleware" do use_frameworks [] add_to_config "config.middleware.use Rack::Config" -- cgit v1.2.3 From 435bccda930e4dde3d0fafca958e1c8330b4c3ca Mon Sep 17 00:00:00 2001 From: John Firebaugh <john_firebaugh@us.ibm.com> Date: Sun, 19 Dec 2010 15:58:58 -0800 Subject: Replace AD::Callbacks.to_prepare with AD::Reloader.to_prepare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim <jose.valim@gmail.com> --- .../lib/action_dispatch/middleware/callbacks.rb | 31 ++-------- actionpack/test/dispatch/callbacks_test.rb | 72 ++++------------------ activerecord/lib/active_record/railtie.rb | 2 +- .../lib/active_support/file_update_checker.rb | 2 +- activesupport/lib/active_support/i18n_railtie.rb | 2 +- railties/lib/rails/application.rb | 2 +- railties/lib/rails/application/finisher.rb | 8 ++- railties/lib/rails/console/app.rb | 3 +- railties/test/application/console_test.rb | 8 +-- 9 files changed, 34 insertions(+), 96 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb index 0bb950d1cc..5776a7bb27 100644 --- a/actionpack/lib/action_dispatch/middleware/callbacks.rb +++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb @@ -1,32 +1,14 @@ module ActionDispatch # Provide callbacks to be executed before and after the request dispatch. - # - # It also provides a to_prepare callback, which is performed in all requests - # in development by only once in production and notification callback for async - # operations. - # class Callbacks include ActiveSupport::Callbacks define_callbacks :call, :rescuable => true - define_callbacks :prepare, :scope => :name - # Add a preparation callback. Preparation callbacks are run before every - # request in development mode, and before the first request in production mode. - # - # If a symbol with a block is given, the symbol is used as an identifier. - # That allows to_prepare to be called again with the same identifier to - # replace the existing callback. Passing an identifier is a suggested - # practice if the code adding a preparation block may be reloaded. def self.to_prepare(*args, &block) - first_arg = args.first - if first_arg.is_a?(Symbol) && block_given? - remove_method :"__#{first_arg}" if method_defined?(:"__#{first_arg}") - define_method :"__#{first_arg}", &block - set_callback(:prepare, :"__#{first_arg}") - else - set_callback(:prepare, *args, &block) - end + ActiveSupport::Deprecation.warn "ActionDispatch::Callbacks.to_prepare is deprecated. " << + "Please use ActionDispatch::Reloader.to_prepare instead." + ActionDispatch::Reloader.to_prepare(*args, &block) end def self.before(*args, &block) @@ -37,14 +19,13 @@ module ActionDispatch set_callback(:call, :after, *args, &block) end - def initialize(app, prepare_each_request = false) - @app, @prepare_each_request = app, prepare_each_request - _run_prepare_callbacks + def initialize(app, unused = nil) + ActiveSupport::Deprecation.warn "Passing a second argument to ActionDispatch::Callbacks.new is deprecated." unless unused.nil? + @app = app end def call(env) _run_call_callbacks do - _run_prepare_callbacks if @prepare_each_request @app.call(env) end end diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb index d3aa55a1ba..5becb621de 100644 --- a/actionpack/test/dispatch/callbacks_test.rb +++ b/actionpack/test/dispatch/callbacks_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -class DispatcherTest < Test::Unit::TestCase +class DispatcherTest < ActiveSupport::TestCase class Foo cattr_accessor :a, :b end @@ -13,65 +13,9 @@ class DispatcherTest < Test::Unit::TestCase def setup Foo.a, Foo.b = 0, 0 - ActionDispatch::Callbacks.reset_callbacks(:prepare) ActionDispatch::Callbacks.reset_callbacks(:call) end - def test_prepare_callbacks_with_cache_classes - a = b = c = nil - ActionDispatch::Callbacks.to_prepare { |*args| a = b = c = 1 } - ActionDispatch::Callbacks.to_prepare { |*args| b = c = 2 } - ActionDispatch::Callbacks.to_prepare { |*args| c = 3 } - - # Ensure to_prepare callbacks are not run when defined - assert_nil a || b || c - - # Run callbacks - dispatch - - assert_equal 1, a - assert_equal 2, b - assert_equal 3, c - - # Make sure they are only run once - a = b = c = nil - dispatch - assert_nil a || b || c - end - - def test_prepare_callbacks_without_cache_classes - a = b = c = nil - ActionDispatch::Callbacks.to_prepare { |*args| a = b = c = 1 } - ActionDispatch::Callbacks.to_prepare { |*args| b = c = 2 } - ActionDispatch::Callbacks.to_prepare { |*args| c = 3 } - - # Ensure to_prepare callbacks are not run when defined - assert_nil a || b || c - - # Run callbacks - dispatch(false) - - assert_equal 1, a - assert_equal 2, b - assert_equal 3, c - - # Make sure they are run again - a = b = c = nil - dispatch(false) - assert_equal 1, a - assert_equal 2, b - assert_equal 3, c - end - - def test_to_prepare_with_identifier_replaces - ActionDispatch::Callbacks.to_prepare(:unique_id) { |*args| Foo.a, Foo.b = 1, 1 } - ActionDispatch::Callbacks.to_prepare(:unique_id) { |*args| Foo.a = 2 } - - dispatch - assert_equal 2, Foo.a - assert_equal 0, Foo.b - end - def test_before_and_after_callbacks ActionDispatch::Callbacks.before { |*args| Foo.a += 1; Foo.b += 1 } ActionDispatch::Callbacks.after { |*args| Foo.a += 1; Foo.b += 1 } @@ -85,10 +29,20 @@ class DispatcherTest < Test::Unit::TestCase assert_equal 4, Foo.b end + def test_to_prepare_deprecation + prepared = false + assert_deprecated do + ActionDispatch::Callbacks.to_prepare { prepared = true } + end + + ActionDispatch::Reloader.prepare! + assert prepared + end + private - def dispatch(cache_classes = true, &block) - @dispatcher ||= ActionDispatch::Callbacks.new(block || DummyApp.new, !cache_classes) + def dispatch(&block) + @dispatcher ||= ActionDispatch::Callbacks.new(block || DummyApp.new) @dispatcher.call({'rack.input' => StringIO.new('')}) end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index dfe255ad7c..ba455ea79d 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -82,7 +82,7 @@ module ActiveRecord ActiveSupport.on_load(:active_record) do instantiate_observers - ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do + ActionDispatch::Reloader.to_prepare(:activerecord_instantiate_observers) do ActiveRecord::Base.instantiate_observers end end diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index cd658fe173..a97e9d7daf 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -8,7 +8,7 @@ module ActiveSupport # I18n.reload! # end # - # ActionDispatch::Callbacks.to_prepare do + # ActionDispatch::Reloader.to_prepare do # i18n_reloader.execute_if_updated # end # diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index f8a5616a76..282337d373 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -19,7 +19,7 @@ module I18n # on to_prepare callbacks. This will only happen on the config.after_initialize # callback below. initializer "i18n.callbacks" do - ActionDispatch::Callbacks.to_prepare do + ActionDispatch::Reloader.to_prepare do I18n::Railtie.reloader.execute_if_updated end end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 3b7505ce37..149c63cd9e 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -157,7 +157,7 @@ module Rails middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header middleware.use ::ActionDispatch::Reloader unless config.cache_classes - middleware.use ::ActionDispatch::Callbacks, !config.cache_classes + middleware.use ::ActionDispatch::Callbacks middleware.use ::ActionDispatch::Cookies if config.session_store diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index e3342be7ee..a45b61c99c 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -21,7 +21,7 @@ module Rails initializer :add_to_prepare_blocks do config.to_prepare_blocks.each do |block| - ActionDispatch::Callbacks.to_prepare(&block) + ActionDispatch::Reloader.to_prepare(&block) end end @@ -37,6 +37,10 @@ module Rails build_middleware_stack end + initializer :run_prepare_callbacks do + ActionDispatch::Reloader.prepare! + end + initializer :eager_load! do if config.cache_classes && !$rails_rake_task ActiveSupport.run_load_hooks(:before_eager_load, self) @@ -52,7 +56,7 @@ module Rails initializer :set_routes_reloader do |app| reloader = lambda { app.routes_reloader.execute_if_updated } reloader.call - ActionDispatch::Callbacks.to_prepare(&reloader) + ActionDispatch::Reloader.to_prepare(&reloader) end # Disable dependency loading during request cycle diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb index 9d9763699d..4a988efd82 100644 --- a/railties/lib/rails/console/app.rb +++ b/railties/lib/rails/console/app.rb @@ -26,7 +26,6 @@ end # reloads the environment def reload!(print=true) puts "Reloading..." if print - # This triggers the to_prepare callbacks - ActionDispatch::Callbacks.new(Proc.new {}, false).call({}) + ActionDispatch::Reloader.reload! true end diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb index d4159dd0fd..5f84c2b948 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -26,14 +26,14 @@ class ConsoleTest < Test::Unit::TestCase assert_instance_of ActionDispatch::Integration::Session, session end - def test_reload_should_fire_preparation_callbacks + def test_reload_should_fire_preparation_and_cleanup_callbacks load_environment a = b = c = nil # TODO: These should be defined on the initializer - ActionDispatch::Callbacks.to_prepare { a = b = c = 1 } - ActionDispatch::Callbacks.to_prepare { b = c = 2 } - ActionDispatch::Callbacks.to_prepare { c = 3 } + ActionDispatch::Reloader.to_prepare { a = b = c = 1 } + ActionDispatch::Reloader.to_prepare { b = c = 2 } + ActionDispatch::Reloader.to_cleanup { c = 3 } # Hide Reloading... output silence_stream(STDOUT) { reload! } -- cgit v1.2.3 From e683ab7c390f26e110387822c7ffb5ac6aafebca Mon Sep 17 00:00:00 2001 From: John Firebaugh <john_firebaugh@us.ibm.com> Date: Sun, 19 Dec 2010 16:00:01 -0800 Subject: Use AD::Reloader.to_cleanup for reloading [#2873 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim <jose.valim@gmail.com> --- activerecord/lib/active_record/railtie.rb | 8 +++----- railties/lib/rails/application/bootstrap.rb | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index ba455ea79d..61fb0ae468 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -69,11 +69,9 @@ module ActiveRecord end initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app| - unless app.config.cache_classes - ActiveSupport.on_load(:active_record) do - ActionDispatch::Callbacks.after do - ActiveRecord::Base.clear_reloadable_connections! - end + ActiveSupport.on_load(:active_record) do + ActionDispatch::Reloader.to_cleanup do + ActiveRecord::Base.clear_reloadable_connections! end end end diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index 213aa0768a..9c9d85eed6 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -51,11 +51,9 @@ module Rails end initializer :set_clear_dependencies_hook do - unless config.cache_classes - ActionDispatch::Callbacks.after do - ActiveSupport::DescendantsTracker.clear - ActiveSupport::Dependencies.clear - end + ActionDispatch::Reloader.to_cleanup do + ActiveSupport::DescendantsTracker.clear + ActiveSupport::Dependencies.clear end end -- cgit v1.2.3 From 0cbfd6c28d327304432f7d0c067662b5c1e41a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com> Date: Mon, 20 Dec 2010 12:41:49 +0100 Subject: Small changes on AD::Reloader. --- .../lib/action_dispatch/middleware/reloader.rb | 23 +++----------------- actionpack/test/dispatch/reloader_test.rb | 25 +++++----------------- activerecord/lib/active_record/railtie.rb | 2 +- railties/lib/rails/console/app.rb | 3 ++- railties/test/application/console_test.rb | 6 +++--- 5 files changed, 14 insertions(+), 45 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb index b84410fc62..579b5d8a02 100644 --- a/actionpack/lib/action_dispatch/middleware/reloader.rb +++ b/actionpack/lib/action_dispatch/middleware/reloader.rb @@ -25,26 +25,14 @@ module ActionDispatch # Add a preparation callback. Preparation callbacks are run before each # request. - # - # If a symbol with a block is given, the symbol is used as an identifier. - # That allows to_prepare to be called again with the same identifier to - # replace the existing callback. Passing an identifier is a suggested - # practice if the code adding a preparation block may be reloaded. def self.to_prepare(*args, &block) - first_arg = args.first - if first_arg.is_a?(Symbol) && block_given? - remove_method :"__#{first_arg}" if method_defined?(:"__#{first_arg}") - define_method :"__#{first_arg}", &block - set_callback(:prepare, :"__#{first_arg}") - else - set_callback(:prepare, *args, &block) - end + set_callback(:prepare, *args, &block) end # Add a cleanup callback. Cleanup callbacks are run after each request is # complete (after #close is called on the response body). - def self.to_cleanup(&block) - set_callback(:cleanup, &block) + def self.to_cleanup(*args, &block) + set_callback(:cleanup, *args, &block) end def self.prepare! @@ -55,11 +43,6 @@ module ActionDispatch new(nil).send(:_run_cleanup_callbacks) end - def self.reload! - prepare! - cleanup! - end - def initialize(app) @app = app end diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb index 58ef346b5c..995b19030c 100644 --- a/actionpack/test/dispatch/reloader_test.rb +++ b/actionpack/test/dispatch/reloader_test.rb @@ -20,16 +20,6 @@ class ReloaderTest < Test::Unit::TestCase assert_equal 3, c end - def test_to_prepare_with_identifier_replaces - a = b = 0 - Reloader.to_prepare(:unique_id) { |*args| a = b = 1 } - Reloader.to_prepare(:unique_id) { |*args| a = 2 } - - call_and_return_body - assert_equal 2, a - assert_equal 0, b - end - class MyBody < Array def initialize(&block) @on_close = block @@ -50,7 +40,7 @@ class ReloaderTest < Test::Unit::TestCase def test_returned_body_object_always_responds_to_close body = call_and_return_body - assert body.respond_to?(:close) + assert_respond_to body, :close end def test_returned_body_object_behaves_like_underlying_object @@ -83,10 +73,10 @@ class ReloaderTest < Test::Unit::TestCase body = call_and_return_body do [200, { "Content-Type" => "text/html" }, MyBody.new] end - assert body.respond_to?(:size) - assert body.respond_to?(:each) - assert body.respond_to?(:foo) - assert body.respond_to?(:bar) + assert_respond_to body, :size + assert_respond_to body, :each + assert_respond_to body, :foo + assert_respond_to body, :bar end def test_cleanup_callbacks_are_called_when_body_is_closed @@ -124,11 +114,6 @@ class ReloaderTest < Test::Unit::TestCase Reloader.cleanup! assert !prepared assert cleaned - - prepared = cleaned = false - Reloader.reload! - assert prepared - assert cleaned end private diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 61fb0ae468..2accf0a48f 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -80,7 +80,7 @@ module ActiveRecord ActiveSupport.on_load(:active_record) do instantiate_observers - ActionDispatch::Reloader.to_prepare(:activerecord_instantiate_observers) do + ActionDispatch::Reloader.to_prepare do ActiveRecord::Base.instantiate_observers end end diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb index 4a988efd82..95c74baae2 100644 --- a/railties/lib/rails/console/app.rb +++ b/railties/lib/rails/console/app.rb @@ -26,6 +26,7 @@ end # reloads the environment def reload!(print=true) puts "Reloading..." if print - ActionDispatch::Reloader.reload! + ActionDispatch::Reloader.cleanup! + ActionDispatch::Reloader.prepare! true end diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb index 5f84c2b948..793e73556c 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -31,9 +31,9 @@ class ConsoleTest < Test::Unit::TestCase a = b = c = nil # TODO: These should be defined on the initializer - ActionDispatch::Reloader.to_prepare { a = b = c = 1 } - ActionDispatch::Reloader.to_prepare { b = c = 2 } - ActionDispatch::Reloader.to_cleanup { c = 3 } + ActionDispatch::Reloader.to_cleanup { a = b = c = 1 } + ActionDispatch::Reloader.to_cleanup { b = c = 2 } + ActionDispatch::Reloader.to_prepare { c = 3 } # Hide Reloading... output silence_stream(STDOUT) { reload! } -- cgit v1.2.3