From 44b752bea148b9df8f4d806e410e30fff26f680e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 9 Jul 2010 16:39:34 -0400 Subject: expanding on :uniq option in has_many --- railties/guides/source/association_basics.textile | 42 ++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile index c69f2ae8c9..bc08874a30 100644 --- a/railties/guides/source/association_basics.textile +++ b/railties/guides/source/association_basics.textile @@ -1371,7 +1371,47 @@ The +:through+ option specifies a join model through which to perform the query. h6(#has_many-uniq). +:uniq+ -Specify the +:uniq => true+ option to remove duplicates from the collection. This is most useful in conjunction with the +:through+ option. +Specify the +:uniq => true+ option to remove duplicates from the collection. It only works with +:through+ option. + + +class Person < ActiveRecord::Base + has_many :readers + has_many :posts, :through => :readers + + def self.lab + person = Person.create(:name => 'john') + p = Post.create(:name => 'a1') + person.posts << p + person.posts << p + person.reload + puts person.posts.inspect #=> [#, #] + puts Reader.all.inspect #=> [#, #] + end +end + + +In the above case +readers+ table has two records and +person.posts+ brings out both of these records even though these records are basically pointing to the same +post+ record. + +Now let's add +uniq => true+ option. + + +class Person + has_many :readers + has_many :posts, :through => :readers, :uniq => true + + def self.experiment + person = Person.create(:name => 'honda') + p = Post.create(:name => 'a1') + person.posts << p + person.posts << p + person.reload + puts person.posts.inspect #=> [#] + puts Reader.all.inspect #=> [#, #] + end +end + + +In the above case +readers+ table still has two records. However +person.posts+ will show only one +post+ record because collection will load only +unique+ records. h6(#has_many-validate). +:validate+ -- cgit v1.2.3 From 86d5c728fbbce6ba8ae73f8926084f5c0eee9a41 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 10 Jul 2010 00:50:59 +0200 Subject: revises recent commit related to :uniq => true --- railties/guides/source/association_basics.textile | 50 ++++++++++------------- 1 file changed, 22 insertions(+), 28 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile index bc08874a30..aec1b0732d 100644 --- a/railties/guides/source/association_basics.textile +++ b/railties/guides/source/association_basics.textile @@ -1371,47 +1371,41 @@ The +:through+ option specifies a join model through which to perform the query. h6(#has_many-uniq). +:uniq+ -Specify the +:uniq => true+ option to remove duplicates from the collection. It only works with +:through+ option. +Set the +:uniq+ option to true to keep the collection free of duplicates. This is mostly useful together with the +:through+ option. class Person < ActiveRecord::Base - has_many :readers - has_many :posts, :through => :readers - - def self.lab - person = Person.create(:name => 'john') - p = Post.create(:name => 'a1') - person.posts << p - person.posts << p - person.reload - puts person.posts.inspect #=> [#, #] - puts Reader.all.inspect #=> [#, #] - end + has_many :readings + has_many :posts, :through => :readings end + +person = Person.create(:name => 'john') +post = Post.create(:name => 'a1') +person.posts << post +person.posts << post +person.posts.inspect # => [#, #] +Reading.all.inspect # => [#, #] -In the above case +readers+ table has two records and +person.posts+ brings out both of these records even though these records are basically pointing to the same +post+ record. +In the above case there are two readings and +person.posts+ brings out both of them even though these records are pointing to the same post. -Now let's add +uniq => true+ option. +Now let's set +:uniq+ to true: class Person - has_many :readers - has_many :posts, :through => :readers, :uniq => true - - def self.experiment - person = Person.create(:name => 'honda') - p = Post.create(:name => 'a1') - person.posts << p - person.posts << p - person.reload - puts person.posts.inspect #=> [#] - puts Reader.all.inspect #=> [#, #] - end + has_many :readings + has_many :posts, :through => :readings, :uniq => true end + +person = Person.create(:name => 'honda') +post = Post.create(:name => 'a1') +person.posts << post +person.posts << post +person.posts.inspect # => [#] +Reading.all.inspect # => [#, #] -In the above case +readers+ table still has two records. However +person.posts+ will show only one +post+ record because collection will load only +unique+ records. +In the above case there are still two readings. However +person.posts+ shows only one post because collection loads only unique records. h6(#has_many-validate). +:validate+ -- cgit v1.2.3 From c81e476d6d4cb88e7e29d6e18bb3e5acce92fb8f Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 10 Jul 2010 00:56:17 +0200 Subject: missing article, only seen in github's colored diff by the beard of the prophet --- railties/guides/source/association_basics.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile index aec1b0732d..b1ee4b8be4 100644 --- a/railties/guides/source/association_basics.textile +++ b/railties/guides/source/association_basics.textile @@ -1405,7 +1405,7 @@ person.posts.inspect # => [#] Reading.all.inspect # => [#, #] -In the above case there are still two readings. However +person.posts+ shows only one post because collection loads only unique records. +In the above case there are still two readings. However +person.posts+ shows only one post because the collection loads only unique records. h6(#has_many-validate). +:validate+ -- cgit v1.2.3 From b0fab0c5c400bc5ca10908643e8b543767e50a29 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Sun, 11 Jul 2010 19:17:36 +0200 Subject: Getting started guide: rephrase the paragraph about the root route for better understanding --- railties/guides/source/getting_started.textile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index f547f29087..8e018437fd 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -322,16 +322,15 @@ $ rm public/index.html We need to do this as Rails will deliver any static file in the +public+ directory in preference to any dynamic contact we generate from the controllers. -Now, you have to tell Rails where your actual home page is located. Open the file +config/routes.rb+ in your editor. This is your application's _routing file_ which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. There are only comments in this file, so we need to add at the top the following: +Now, you have to tell Rails where your actual home page is located. Open the file +config/routes.rb+ in your editor. This is your application's _routing file_ which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. This file contains many sample routes on commented lines, and one of them actually shows you how to connect the root of your site to a specific controller and action. Find the line beginning with +:root to+, uncomment it and change it like the following: Blog::Application.routes.draw do |map| - root :to => "home#index" - - # The priority is based upon order of creation: - # first created -> highest priority. #... + # You can have the root of your site routed with "root" + # just remember to delete public/index.html. + root :to => "home#index" The +root :to => "home#index"+ tells Rails to map the root action to the home controller's index action. -- cgit v1.2.3 From 1dddc79fee290702ccef75834ab85fddf3a5fd8f Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Sun, 11 Jul 2010 21:47:31 +0200 Subject: Getting started guide: Post validation example is clearer if we do p=Post.new;p.save better than p=Post.create;p.save --- railties/guides/source/getting_started.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 8e018437fd..7a5266ce2c 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -474,7 +474,7 @@ $ rails console After the console loads, you can use it to work with your application's models: ->> p = Post.create(:content => "A new post") +>> p = Post.new(:content => "A new post") => # -- cgit v1.2.3 From 1a35b6215fe4fb1630e2a635789038c5e6e56c63 Mon Sep 17 00:00:00 2001 From: Steven Hancock Date: Sun, 11 Jul 2010 23:57:26 -0700 Subject: Removed deprecated |map| block argument from routing docs since it is no longer generated in edge Rails config/routes.rb Didn't touch plugins guide since I'm not too clear on how routes work in plugins. --- railties/guides/source/getting_started.textile | 2 +- railties/guides/source/i18n.textile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index f547f29087..b4d1ae74c7 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -325,7 +325,7 @@ We need to do this as Rails will deliver any static file in the +public+ directo Now, you have to tell Rails where your actual home page is located. Open the file +config/routes.rb+ in your editor. This is your application's _routing file_ which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. There are only comments in this file, so we need to add at the top the following: -Blog::Application.routes.draw do |map| +Blog::Application.routes.draw do root :to => "home#index" diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index b09bb470ee..63d22db485 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -287,7 +287,7 @@ You most probably have something like this in one of your applications: # config/routes.rb -Yourapp::Application.routes.draw do |map| +Yourapp::Application.routes.draw do root :to => "home#index" end -- cgit v1.2.3 From cf69a010790d22dc2e66f172437c71ccff2e6366 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Mon, 12 Jul 2010 18:23:13 +0200 Subject: Getting started guide: typos and changelog --- railties/guides/source/getting_started.textile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 7a5266ce2c..3a4f16c3cd 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -1193,7 +1193,7 @@ The +destroy+ action will find the post we are looking at, locate the comment wi h4. Deleting Associated Objects -If you delete a post then it's associated comments will also need to be deleted. Otherwise they would simply occupy space in the database. Rails allows you to use the +dependent+ option of an association to achieve this. Modify the Post model, +app/models/post.rb+, as follows: +If you delete a post then its associated comments will also need to be deleted. Otherwise they would simply occupy space in the database. Rails allows you to use the +dependent+ option of an association to achieve this. Modify the Post model, +app/models/post.rb+, as follows: class Post < ActiveRecord::Base @@ -1485,6 +1485,7 @@ h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2 +* July 12, 2010: Fixes, editing and updating of code samples by "Jaime Iniesta":http://jaimeiniesta.com * May 16, 2010: Added a section on configuration gotchas to address common encoding problems that people might have by "Yehuda Katz":http://www.yehudakatz.com * April 30, 2010: Fixes, editing and updating of code samples by "Rohit Arondekar":http://rohitarondekar.com * April 25, 2010: Couple of more minor fixups "Mikel Lindsaar":credits.html#raasdnil -- cgit v1.2.3 From 6b29dc876fe185881d46731c3ae170478a3828fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 12 Jul 2010 20:18:54 +0200 Subject: Just add connection management middleware if running in a concurrent environment. --- railties/test/application/initializers/frameworks_test.rb | 12 +++++++++++- railties/test/application/middleware_test.rb | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'railties') diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 4ff10091b1..d8e916f45f 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -98,7 +98,17 @@ module ApplicationTests require "#{app_path}/config/environment" - expects = [ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActiveRecord::SessionStore] + expects = [ActiveRecord::QueryCache, ActiveRecord::SessionStore] + middleware = Rails.application.config.middleware.map { |m| m.klass } + assert_equal expects, middleware & expects + end + + test "database middleware initializes when allow concurrency is true" do + add_to_config "config.threadsafe!" + + require "#{app_path}/config/environment" + + expects = [ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache] middleware = Rails.application.config.middleware.map { |m| m.klass } assert_equal expects, middleware & expects end diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index e66e81ea2c..ed06b4c767 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -28,7 +28,6 @@ module ApplicationTests "ActionDispatch::RemoteIp", "Rack::Sendfile", "ActionDispatch::Callbacks", - "ActiveRecord::ConnectionAdapters::ConnectionManagement", "ActiveRecord::QueryCache", "ActionDispatch::Cookies", "ActionDispatch::Session::CookieStore", -- cgit v1.2.3 From 7e075e62479a0eccad6eaf7a7c20f45347860c83 Mon Sep 17 00:00:00 2001 From: Benjamin Quorning Date: Fri, 9 Jul 2010 15:58:58 +0200 Subject: Fixed many references to the old config/environment.rb and Rails::Initializer --- railties/lib/rails/generators/actions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index a27d38e23a..2280cc1507 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -40,7 +40,7 @@ module Rails end end - # Adds an entry into config/environment.rb for the supplied gem. If env + # Adds an entry into Gemfile for the supplied gem. If env # is specified, add the gem to the given environment. # # ==== Example @@ -100,7 +100,7 @@ module Rails end end - # Adds a line inside the Initializer block for config/environment.rb. + # Adds a line inside the Application class for config/application.rb. # # If options :env is specified, the line is appended to the corresponding # file in config/environments. -- cgit v1.2.3 From 00f49c74e8354e393ed4ffd6bfa8beb657c8920d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 13 Jul 2010 23:35:58 +0200 Subject: AS guides: reword the docs of Enumerable#group_by --- railties/guides/source/active_support_core_extensions.textile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index 58824d7aeb..097d51e007 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -1840,9 +1840,7 @@ h3. Extensions to +Enumerable+ h4. +group_by+ -Ruby 1.8.7 and up define +group_by+, and Active Support does it for previous versions. - -This iterator takes a block and builds an ordered hash with its return values as keys. Each key is mapped to the array of elements for which the block returned that value: +Active Support redefines +group_by+ in Ruby 1.8.7 so that it returns an ordered hash as in 1.9: entries_by_surname_initial = address_book.group_by do |entry| @@ -1850,7 +1848,7 @@ entries_by_surname_initial = address_book.group_by do |entry| end -WARNING. Active Support redefines +group_by+ in Ruby 1.8.7 so that it still returns an ordered hash. +Distinct block return values are added to the hash as they come, so that's the resulting order. NOTE: Defined in +active_support/core_ext/enumerable.rb+. -- cgit v1.2.3 From 981258cf5e3c4ae8e3e2fa93327e2ac325e9b4b3 Mon Sep 17 00:00:00 2001 From: Michael Hutchinson Date: Wed, 14 Jul 2010 01:39:00 -0700 Subject: Active Record Query Interface Guide: Fixed minor typos. --- railties/guides/source/active_record_querying.textile | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index f5e70aef41..a8d86659d1 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -337,7 +337,7 @@ Just like in Ruby. If you want a shorter syntax be sure to check out the "Hash C h4. Hash Conditions -Active Record also allows you to pass in a 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: +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: NOTE: Only equality, range and subset checking are possible with Hash conditions. @@ -473,7 +473,7 @@ SELECT * FROM clients LIMIT 5, 5 h4. Group -To apply +GROUP BY+ clause to the SQL fired by the finder, you can specify the +group+ method on the find. +To apply a +GROUP BY+ clause to the SQL fired by the finder, you can specify the +group+ method on the find. For example, if you want to find a collection of the dates orders were created on: @@ -527,7 +527,9 @@ client.save h4. Locking Records for Update -Locking is helpful for preventing the race conditions when updating records in the database and ensuring atomic updated. Active Record provides two locking mechanism: +Locking is helpful for preventing race conditions when updating records in the database and ensuring atomic updates. + +Active Record provides two locking mechanisms: * Optimistic Locking * Pessimistic Locking @@ -569,7 +571,7 @@ end h5. Pessimistic Locking -Pessimistic locking uses locking mechanism provided by the underlying database. Passing +:lock => true+ to +Model.find+ obtains an exclusive lock on the selected rows. +Model.find+ using +:lock+ are usually wrapped inside a transaction for preventing deadlock conditions. +Pessimistic locking uses a locking mechanism provided by the underlying database. Passing +:lock => true+ to +Model.find+ obtains an exclusive lock on the selected rows. +Model.find+ using +:lock+ are usually wrapped inside a transaction for preventing deadlock conditions. For example: @@ -601,7 +603,7 @@ end h3. Joining Tables -Model.find provides a +:joins+ option for specifying +JOIN+ clauses on the resulting SQL. There multiple different ways to specify the +:joins+ option: +Model.find provides a +:joins+ option for specifying +JOIN+ clauses on the resulting SQL. There are multiple ways to specify the +:joins+ option: h4. Using a String SQL Fragment @@ -782,7 +784,7 @@ You can specify an exclamation point (!) on the end of the dynamic find If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_first_name_and_locked("Ryan", true)+. -There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_first_name(params[:first_name])+. Using this will firstly perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_first_name("Ryan")+: +There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_first_name(params[:first_name])+. Using this will first perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_first_name("Ryan")+: SELECT * FROM clients WHERE (clients.first_name = 'Ryan') LIMIT 1 @@ -792,7 +794,7 @@ INSERT INTO clients (first_name, updated_at, created_at, orders_count, locked) COMMIT -+find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will act similar to calling +new+ with the arguments you passed in. For example: ++find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will act similarly to calling +new+ with the arguments you passed in. For example: client = Client.find_or_initialize_by_first_name('Ryan') -- cgit v1.2.3 From 6de6fa80106bd1a5b1b2369891536ec36e94037a Mon Sep 17 00:00:00 2001 From: Michael Hutchinson Date: Wed, 14 Jul 2010 02:30:12 -0700 Subject: Active Record Query Interface Guide: Fixed a few typos and made minor changes to improve readability. --- .../guides/source/active_record_querying.textile | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index a8d86659d1..8c43a02c96 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -347,7 +347,7 @@ h5. Equality Conditions Client.where({ :locked => true }) -The field name does not have to be a symbol it can also be a string: +The field name can also be a string: Client.where({ 'locked' => true }) @@ -447,7 +447,7 @@ h4. Limit and Offset To apply +LIMIT+ to the SQL fired by the +Model.find+, you can specify the +LIMIT+ using +limit+ and +offset+ methods on the relation. -If you want to limit the amount of records to a certain subset of all the records retrieved you usually use +limit+ for this, sometimes coupled with +offset+. Limit is the maximum number of records that will be retrieved from a query, and offset is the number of records it will start reading from from the first record of the set. For example: +You can use +limit+ to specify the number of records to be retrieved, and use +offset+ to specify the number of records to skip before starting to return the records. For example: Client.limit(5) @@ -491,7 +491,7 @@ SELECT * FROM orders GROUP BY date(created_at) h4. Having -SQL uses +HAVING+ clause to specify conditions on the +GROUP BY+ fields. You can specify the +HAVING+ clause to the SQL fired by the +Model.find+ using +:having+ option on the find. +SQL uses the +HAVING+ clause to specify conditions on the +GROUP BY+ fields. You can add the +HAVING+ clause to the SQL fired by the +Model.find+ by adding the +:having+ option to the find. For example: @@ -517,7 +517,7 @@ Any attempt to alter or destroy the readonly records will not succeed, raising a Client.first.readonly(true) -If you assign this record to a variable client, calling the following code will raise an +ActiveRecord::ReadOnlyRecord+ exception: +For example, calling the following code will raise an +ActiveRecord::ReadOnlyRecord+ exception: client = Client.first.readonly(true) @@ -540,7 +540,7 @@ Optimistic locking allows multiple users to access the same record for edits, an Optimistic locking column -In order to use optimistic locking, the table needs to have a column called +lock_version+. Each time the record is updated, Active Record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice will let the last one saved raise an +ActiveRecord::StaleObjectError+ exception if the first was also updated. Example: +In order to use optimistic locking, the table needs to have a column called +lock_version+. Each time the record is updated, Active Record increments the +lock_version+ column. If an update request is made with a lower value in the +lock_version+ field than is currently in the +lock_version+ column in the database, the update request will fail with an +ActiveRecord::StaleObjectError+. Example: c1 = Client.find(1) @@ -700,7 +700,7 @@ time_range = (Time.now.midnight - 1.day)..Time.now.midnight Client.joins(:orders).where('orders.created_at' => time_range) -An alternative and cleaner syntax to this is to nest the hash conditions: +An alternative and cleaner syntax is to nest the hash conditions: time_range = (Time.now.midnight - 1.day)..Time.now.midnight @@ -729,7 +729,7 @@ This code looks fine at the first sight. But the problem lies within the total n Solution to N 1 queries problem -Active Record lets you specify all the associations in advanced that are going to be loaded. This is possible by specifying the +includes+ method of the +Model.find+ call. With +includes+, Active Record ensures that all the specified associations are loaded using minimum possible number of queries. +Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the +includes+ method of the +Model.find+ call. With +includes+, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries. Revisiting the above case, we could rewrite +Client.all+ to use eager load addresses: @@ -751,7 +751,7 @@ SELECT addresses.* FROM addresses h4. Eager Loading Multiple Associations -Active Record lets you eager load any possible number of associations with a single +Model.find+ call by using an array, hash, or a nested hash of array/hash with the +includes+ method. +Active Record lets you eager load any number of associations with a single +Model.find+ call by using an array, hash, or a nested hash of array/hash with the +includes+ method. h5. Array of Multiple Associations @@ -767,7 +767,7 @@ h5. Nested Associations Hash Category.find(1).includes(:posts => [{:comments => :guest}, :tags]) -The above code finds the category with id 1 and eager loads all the posts associated with the found category. Additionally, it will also eager load every posts' tags and comments. Every comment's guest association will get eager loaded as well. +This will find the category with id 1 and eager load all of the associated posts, the associated posts' tags and comments, and every comment's guest association. h4. Specifying Conditions on Eager Loaded Associations @@ -838,7 +838,7 @@ Client.exists?(1,2,3) Client.exists?([1,2,3]) -Further more, +exists+ takes a +conditions+ option much like find: +The +exists+ method may also take a +conditions+ option much like find: Client.exists?(:conditions => "first_name = 'Ryan'") -- cgit v1.2.3 From 438bff6ccd0ba495c684a0377fd94cf302e340c5 Mon Sep 17 00:00:00 2001 From: Michael Hutchinson Date: Wed, 14 Jul 2010 03:36:55 -0700 Subject: Active Record Query Interface Guide: Corrected the explanation for the OFFSET example. --- railties/guides/source/active_record_querying.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index 8c43a02c96..2e23604838 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -465,7 +465,7 @@ Or chaining both +limit+ and +offset+: Client.limit(5).offset(5) -This code will return a maximum of 5 clients and because it specifies an offset this time, it will return these records starting from the 5th client in the clients table. The SQL looks like: +This code will return a maximum of 5 clients beginning with the 6th client in the clients table, skipping the first five clients as specified by the offset. The SQL looks like: SELECT * FROM clients LIMIT 5, 5 -- cgit v1.2.3 From 684fb5e7d50801edcf20eceaa465259602871e9c Mon Sep 17 00:00:00 2001 From: Andrew Kaspick Date: Tue, 13 Jul 2010 21:59:28 -0500 Subject: upgrade prototype to be compatible with rails.js [#5109 state:committed] Signed-off-by: Xavier Noria --- .../app/templates/public/javascripts/prototype.js | 2927 ++++++++++++++------ 1 file changed, 2027 insertions(+), 900 deletions(-) (limited to 'railties') 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 9fe6e1243b..06249a6ae3 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,5 +1,5 @@ -/* Prototype JavaScript framework, version 1.6.1 - * (c) 2005-2009 Sam Stephenson +/* Prototype JavaScript framework, version 1.7_rc2 + * (c) 2005-2010 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://www.prototypejs.org/ @@ -7,7 +7,8 @@ *--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.6.1', + + Version: '1.7_rc2', Browser: (function(){ var ua = navigator.userAgent; @@ -17,13 +18,15 @@ var Prototype = { Opera: isOpera, WebKit: ua.indexOf('AppleWebKit/') > -1, Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, - MobileSafari: /Apple.*Mobile.*Safari/.test(ua) + MobileSafari: /Apple.*Mobile/.test(ua) } })(), BrowserFeatures: { XPath: !!document.evaluate, + SelectorsAPI: !!document.querySelector, + ElementExtensions: (function() { var constructor = window.Element || window.HTMLElement; return !!(constructor && constructor.prototype); @@ -32,9 +35,9 @@ var Prototype = { if (typeof window.HTMLDivElement !== 'undefined') return true; - var div = document.createElement('div'); - var form = document.createElement('form'); - var isSupported = false; + var div = document.createElement('div'), + form = document.createElement('form'), + isSupported = false; if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { isSupported = true; @@ -50,6 +53,7 @@ var Prototype = { JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, emptyFunction: function() { }, + K: function(x) { return x } }; @@ -79,6 +83,14 @@ var Try = { /* Based on Alex Arnell's inheritance implementation. */ var Class = (function() { + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + function subclass() {}; function create() { var parent = null, properties = $A(arguments); @@ -99,7 +111,7 @@ var Class = (function() { parent.subclasses.push(klass); } - for (var i = 0; i < properties.length; i++) + for (var i = 0, length = properties.length; i < length; i++) klass.addMethods(properties[i]); if (!klass.prototype.initialize) @@ -110,10 +122,10 @@ var Class = (function() { } function addMethods(source) { - var ancestor = this.superclass && this.superclass.prototype; - var properties = Object.keys(source); + var ancestor = this.superclass && this.superclass.prototype, + properties = Object.keys(source); - if (!Object.keys({ toString: true }).length) { + if (IS_DONTENUM_BUGGY) { if (source.toString != Object.prototype.toString) properties.push("toString"); if (source.valueOf != Object.prototype.valueOf) @@ -123,7 +135,7 @@ var Class = (function() { for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; if (ancestor && Object.isFunction(value) && - value.argumentNames().first() == "$super") { + value.argumentNames()[0] == "$super") { var method = value; value = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; @@ -147,7 +159,35 @@ var Class = (function() { })(); (function() { - var _toString = Object.prototype.toString; + var _toString = Object.prototype.toString, + NULL_TYPE = 'Null', + UNDEFINED_TYPE = 'Undefined', + BOOLEAN_TYPE = 'Boolean', + NUMBER_TYPE = 'Number', + STRING_TYPE = 'String', + OBJECT_TYPE = 'Object', + BOOLEAN_CLASS = '[object Boolean]', + NUMBER_CLASS = '[object Number]', + STRING_CLASS = '[object String]', + ARRAY_CLASS = '[object Array]', + NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && + typeof JSON.stringify === 'function' && + JSON.stringify(0) === '0' && + typeof JSON.stringify(Prototype.K) === 'undefined'; + + function Type(o) { + switch(o) { + case null: return NULL_TYPE; + case (void 0): return UNDEFINED_TYPE; + } + var type = typeof o; + switch(type) { + case 'boolean': return BOOLEAN_TYPE; + case 'number': return NUMBER_TYPE; + case 'string': return STRING_TYPE; + } + return OBJECT_TYPE; + } function extend(destination, source) { for (var property in source) @@ -166,27 +206,70 @@ var Class = (function() { } } - function toJSON(object) { - var type = typeof object; - switch (type) { - case 'undefined': - case 'function': - case 'unknown': return; - case 'boolean': return object.toString(); + function toJSON(value) { + return Str('', { '': value }, []); + } + + function Str(key, holder, stack) { + var value = holder[key], + type = typeof value; + + if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { + value = value.toJSON(key); } - if (object === null) return 'null'; - if (object.toJSON) return object.toJSON(); - if (isElement(object)) return; + var _class = _toString.call(value); - var results = []; - for (var property in object) { - var value = toJSON(object[property]); - if (!isUndefined(value)) - results.push(property.toJSON() + ': ' + value); + switch (_class) { + case NUMBER_CLASS: + case BOOLEAN_CLASS: + case STRING_CLASS: + value = value.valueOf(); + } + + switch (value) { + case null: return 'null'; + case true: return 'true'; + case false: return 'false'; + } + + type = typeof value; + switch (type) { + case 'string': + return value.inspect(true); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'object': + + for (var i = 0, length = stack.length; i < length; i++) { + if (stack[i] === value) { throw new TypeError(); } + } + stack.push(value); + + var partial = []; + if (_class === ARRAY_CLASS) { + for (var i = 0, length = value.length; i < length; i++) { + var str = Str(i, value, stack); + partial.push(typeof str === 'undefined' ? 'null' : str); + } + partial = '[' + partial.join(',') + ']'; + } else { + var keys = Object.keys(value); + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i], str = Str(key, value, stack); + if (typeof str !== "undefined") { + partial.push(key.inspect(true)+ ':' + str); + } + } + partial = '{' + partial.join(',') + '}'; + } + stack.pop(); + return partial; } + } - return '{' + results.join(', ') + '}'; + function stringify(object) { + return JSON.stringify(object); } function toQueryString(object) { @@ -198,9 +281,13 @@ var Class = (function() { } function keys(object) { + if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } var results = []; - for (var property in object) - results.push(property); + for (var property in object) { + if (object.hasOwnProperty(property)) { + results.push(property); + } + } return results; } @@ -220,9 +307,15 @@ var Class = (function() { } function isArray(object) { - return _toString.call(object) == "[object Array]"; + return _toString.call(object) === ARRAY_CLASS; } + var hasNativeIsArray = (typeof Array.isArray == 'function') + && Array.isArray([]) && !Array.isArray({}); + + if (hasNativeIsArray) { + isArray = Array.isArray; + } function isHash(object) { return object instanceof Hash; @@ -233,11 +326,11 @@ var Class = (function() { } function isString(object) { - return _toString.call(object) == "[object String]"; + return _toString.call(object) === STRING_CLASS; } function isNumber(object) { - return _toString.call(object) == "[object Number]"; + return _toString.call(object) === NUMBER_CLASS; } function isUndefined(object) { @@ -247,10 +340,10 @@ var Class = (function() { extend(Object, { extend: extend, inspect: inspect, - toJSON: toJSON, + toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, toQueryString: toQueryString, toHTML: toHTML, - keys: keys, + keys: Object.keys || keys, values: values, clone: clone, isElement: isElement, @@ -311,7 +404,7 @@ Object.extend(Function.prototype, (function() { function delay(timeout) { var __method = this, args = slice.call(arguments, 1); - timeout = timeout * 1000 + timeout = timeout * 1000; return window.setTimeout(function() { return __method.apply(__method, args); }, timeout); @@ -352,14 +445,28 @@ Object.extend(Function.prototype, (function() { })()); -Date.prototype.toJSON = function() { - return '"' + this.getUTCFullYear() + '-' + - (this.getUTCMonth() + 1).toPaddedString(2) + '-' + - this.getUTCDate().toPaddedString(2) + 'T' + - this.getUTCHours().toPaddedString(2) + ':' + - this.getUTCMinutes().toPaddedString(2) + ':' + - this.getUTCSeconds().toPaddedString(2) + 'Z"'; -}; + +(function(proto) { + + + function toISOString() { + return this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z'; + } + + + function toJSON() { + return this.toISOString(); + } + + if (!proto.toISOString) proto.toISOString = toISOString; + if (!proto.toJSON) proto.toJSON = toJSON; + +})(Date.prototype); RegExp.prototype.match = RegExp.prototype.test; @@ -418,6 +525,9 @@ Object.extend(String, { }); Object.extend(String.prototype, (function() { + var NATIVE_JSON_PARSE_SUPPORT = window.JSON && + typeof JSON.parse === 'function' && + JSON.parse('{"test": true}').test; function prepareReplacement(replacement) { if (Object.isFunction(replacement)) return replacement; @@ -484,8 +594,8 @@ Object.extend(String.prototype, (function() { } function extractScripts() { - var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); - var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'), + matchOne = new RegExp(Prototype.ScriptFragment, 'im'); return (this.match(matchAll) || []).map(function(scriptTag) { return (scriptTag.match(matchOne) || ['', ''])[1]; }); @@ -510,8 +620,9 @@ Object.extend(String.prototype, (function() { return match[1].split(separator || '&').inject({ }, function(hash, pair) { if ((pair = pair.split('='))[0]) { - var key = decodeURIComponent(pair.shift()); - var value = pair.length > 1 ? pair.join('=') : pair[0]; + var key = decodeURIComponent(pair.shift()), + value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); if (key in hash) { @@ -538,17 +649,9 @@ Object.extend(String.prototype, (function() { } function camelize() { - var parts = this.split('-'), len = parts.length; - if (len == 1) return parts[0]; - - var camelized = this.charAt(0) == '-' - ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) - : parts[0]; - - for (var i = 1; i < len; i++) - camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); - - return camelized; + return this.replace(/-+(.)?/g, function(match, chr) { + return chr ? chr.toUpperCase() : ''; + }); } function capitalize() { @@ -578,10 +681,6 @@ Object.extend(String.prototype, (function() { return "'" + escapedString.replace(/'/g, '\\\'') + "'"; } - function toJSON() { - return this.inspect(true); - } - function unfilterJSON(filter) { return this.replace(filter || Prototype.JSONFilter, '$1'); } @@ -589,29 +688,42 @@ Object.extend(String.prototype, (function() { function isJSON() { var str = this; if (str.blank()) return false; - str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); - return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'); + str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'); + str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); + return (/^[\],:{}\s]*$/).test(str); } function evalJSON(sanitize) { - var json = this.unfilterJSON(); + var json = this.unfilterJSON(), + cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + if (cx.test(json)) { + json = json.replace(cx, function (a) { + return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } try { if (!sanitize || json.isJSON()) return eval('(' + json + ')'); } catch (e) { } throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); } + function parseJSON() { + var json = this.unfilterJSON(); + return JSON.parse(json); + } + function include(pattern) { return this.indexOf(pattern) > -1; } function startsWith(pattern) { - return this.indexOf(pattern) === 0; + return this.lastIndexOf(pattern, 0) === 0; } function endsWith(pattern) { var d = this.length - pattern.length; - return d >= 0 && this.lastIndexOf(pattern) === d; + return d >= 0 && this.indexOf(pattern, d) === d; } function empty() { @@ -631,7 +743,7 @@ Object.extend(String.prototype, (function() { sub: sub, scan: scan, truncate: truncate, - strip: String.prototype.trim ? String.prototype.trim : strip, + strip: String.prototype.trim || strip, stripTags: stripTags, stripScripts: stripScripts, extractScripts: extractScripts, @@ -648,10 +760,9 @@ Object.extend(String.prototype, (function() { underscore: underscore, dasherize: dasherize, inspect: inspect, - toJSON: toJSON, unfilterJSON: unfilterJSON, isJSON: isJSON, - evalJSON: evalJSON, + evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, include: include, startsWith: startsWith, endsWith: endsWith, @@ -677,8 +788,9 @@ var Template = Class.create({ var before = match[1] || ''; if (before == '\\') return match[2]; - var ctx = object, expr = match[3]; - var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + var ctx = object, expr = match[3], + pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); if (match == null) return before; @@ -943,6 +1055,7 @@ var Enumerable = (function() { find: detect }; })(); + function $A(iterable) { if (!iterable) return []; if ('toArray' in Object(iterable)) return iterable.toArray(); @@ -951,6 +1064,7 @@ function $A(iterable) { return results; } + function $w(string) { if (!Object.isString(string)) return []; string = string.strip(); @@ -1007,7 +1121,7 @@ Array.from = $A; } function reverse(inline) { - return (inline !== false ? this : this.toArray())._reverse(); + return (inline === false ? this.toArray() : this)._reverse(); } function uniq(sorted) { @@ -1037,15 +1151,6 @@ Array.from = $A; return '[' + this.map(Object.inspect).join(', ') + ']'; } - function toJSON() { - var results = []; - this.each(function(object) { - var value = Object.toJSON(object); - if (!Object.isUndefined(value)) results.push(value); - }); - return '[' + results.join(', ') + ']'; - } - function indexOf(item, i) { i || (i = 0); var length = this.length; @@ -1094,8 +1199,7 @@ Array.from = $A; clone: clone, toArray: clone, size: size, - inspect: inspect, - toJSON: toJSON + inspect: inspect }); var CONCAT_ARGUMENTS_BUGGY = (function() { @@ -1116,6 +1220,7 @@ var Hash = Class.create(Enumerable, (function() { this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); } + function _each(iterator) { for (var key in this._object) { var value = this._object[key], pair = [key, value]; @@ -1144,6 +1249,8 @@ var Hash = Class.create(Enumerable, (function() { return Object.clone(this._object); } + + function keys() { return this.pluck('key'); } @@ -1193,10 +1300,6 @@ var Hash = Class.create(Enumerable, (function() { }).join(', ') + '}>'; } - function toJSON() { - return Object.toJSON(this.toObject()); - } - function clone() { return new Hash(this); } @@ -1216,7 +1319,7 @@ var Hash = Class.create(Enumerable, (function() { update: update, toQueryString: toQueryString, inspect: inspect, - toJSON: toJSON, + toJSON: toObject, clone: clone }; })()); @@ -1241,10 +1344,6 @@ Object.extend(Number.prototype, (function() { return '0'.times(length - string.length) + string; } - function toJSON() { - return isFinite(this) ? this.toString() : 'null'; - } - function abs() { return Math.abs(this); } @@ -1266,7 +1365,6 @@ Object.extend(Number.prototype, (function() { succ: succ, times: times, toPaddedString: toPaddedString, - toJSON: toJSON, abs: abs, round: round, ceil: ceil, @@ -1558,14 +1656,14 @@ Ajax.Response = Class.create({ var transport = this.transport = request.transport, readyState = this.readyState = transport.readyState; - if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { this.status = this.getStatus(); this.statusText = this.getStatusText(); this.responseText = String.interpret(transport.responseText); this.headerJSON = this._getHeaderJSON(); } - if(readyState == 4) { + if (readyState == 4) { var xml = transport.responseXML; this.responseXML = Object.isUndefined(xml) ? null : xml; this.responseJSON = this._getResponseJSON(); @@ -1705,7 +1803,6 @@ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { }); - function $(element) { if (arguments.length > 1) { for (var i = 0, elements = [], length = arguments.length; i < length; i++) @@ -1730,7 +1827,7 @@ if (Prototype.BrowserFeatures.XPath) { /*--------------------------------------------------------------------------*/ -if (!window.Node) var Node = { }; +if (!Node) var Node = { }; if (!Node.ELEMENT_NODE) { Object.extend(Node, { @@ -1750,29 +1847,26 @@ if (!Node.ELEMENT_NODE) { } + (function(global) { - var SETATTRIBUTE_IGNORES_NAME = (function(){ - var elForm = document.createElement("form"); - var elInput = document.createElement("input"); - var root = document.documentElement; - elInput.setAttribute("name", "test"); - elForm.appendChild(elInput); - root.appendChild(elForm); - var isBuggy = elForm.elements - ? (typeof elForm.elements.test == "undefined") - : null; - root.removeChild(elForm); - elForm = elInput = null; - return isBuggy; + var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ + try { + var el = document.createElement(''); + return el.tagName.toLowerCase() === 'input' && el.name === 'x'; + } + catch(err) { + return false; + } })(); var element = global.Element; + global.Element = function(tagName, attributes) { attributes = attributes || { }; tagName = tagName.toLowerCase(); var cache = Element.cache; - if (SETATTRIBUTE_IGNORES_NAME && attributes.name) { + if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { tagName = '<' + tagName + ' name="' + attributes.name + '">'; delete attributes.name; return Element.writeAttribute(document.createElement(tagName), attributes); @@ -1780,12 +1874,23 @@ if (!Node.ELEMENT_NODE) { if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); }; + Object.extend(global.Element, element || { }); if (element) global.Element.prototype = element.prototype; + })(this); -Element.cache = { }; Element.idCounter = 1; +Element.cache = { }; + +function purgeElement(element) { + var uid = element._prototypeUID; + if (uid) { + Element.stopObserving(element); + element._prototypeUID = void 0; + delete Element.Storage[uid]; + } +} Element.Methods = { visible: function(element) { @@ -1798,7 +1903,6 @@ Element.Methods = { return element; }, - hide: function(element) { element = $(element); element.style.display = 'none'; @@ -1861,6 +1965,10 @@ Element.Methods = { function update(element, content) { element = $(element); + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + while (i--) purgeElement(descendants[i]); + if (content && content.toElement) content = content.toElement(); @@ -1967,19 +2075,26 @@ Element.Methods = { element = $(element); var result = '<' + element.tagName.toLowerCase(); $H({'id': 'id', 'className': 'class'}).each(function(pair) { - var property = pair.first(), attribute = pair.last(); - var value = (element[property] || '').toString(); + var property = pair.first(), + attribute = pair.last(), + value = (element[property] || '').toString(); if (value) result += ' ' + attribute + '=' + value.inspect(true); }); return result + '>'; }, - recursivelyCollect: function(element, property) { + recursivelyCollect: function(element, property, maximumLength) { element = $(element); + maximumLength = maximumLength || -1; var elements = []; - while (element = element[property]) + + while (element = element[property]) { if (element.nodeType == 1) elements.push(Element.extend(element)); + if (elements.length == maximumLength) + break; + } + return elements; }, @@ -1998,13 +2113,17 @@ Element.Methods = { }, immediateDescendants: function(element) { - if (!(element = $(element).firstChild)) return []; - while (element && element.nodeType != 1) element = element.nextSibling; - if (element) return [element].concat($(element).nextSiblings()); - return []; + var results = [], child = $(element).firstChild; + while (child) { + if (child.nodeType === 1) { + results.push(Element.extend(child)); + } + child = child.nextSibling; + } + return results; }, - previousSiblings: function(element) { + previousSiblings: function(element, maximumLength) { return Element.recursivelyCollect(element, 'previousSibling'); }, @@ -2019,9 +2138,10 @@ Element.Methods = { }, match: function(element, selector) { + element = $(element); if (Object.isString(selector)) - selector = new Selector(selector); - return selector.match($(element)); + return Prototype.Selector.match(element, selector); + return selector.match(element); }, up: function(element, expression, index) { @@ -2029,7 +2149,7 @@ Element.Methods = { if (arguments.length == 1) return $(element.parentNode); var ancestors = Element.ancestors(element); return Object.isNumber(expression) ? ancestors[expression] : - Selector.findElement(ancestors, expression, index); + Prototype.Selector.find(ancestors, expression, index); }, down: function(element, expression, index) { @@ -2041,29 +2161,40 @@ Element.Methods = { previous: function(element, expression, index) { element = $(element); - if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); - var previousSiblings = Element.previousSiblings(element); - return Object.isNumber(expression) ? previousSiblings[expression] : - Selector.findElement(previousSiblings, expression, index); + if (Object.isNumber(expression)) index = expression, expression = false; + if (!Object.isNumber(index)) index = 0; + + if (expression) { + return Prototype.Selector.find(element.previousSiblings(), expression, index); + } else { + return element.recursivelyCollect("previousSibling", index + 1)[index]; + } }, next: function(element, expression, index) { element = $(element); - if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); - var nextSiblings = Element.nextSiblings(element); - return Object.isNumber(expression) ? nextSiblings[expression] : - Selector.findElement(nextSiblings, expression, index); + if (Object.isNumber(expression)) index = expression, expression = false; + if (!Object.isNumber(index)) index = 0; + + if (expression) { + return Prototype.Selector.find(element.nextSiblings(), expression, index); + } else { + var maximumLength = Object.isNumber(index) ? index + 1 : 1; + return element.recursivelyCollect("nextSibling", index + 1)[index]; + } }, select: function(element) { - var args = Array.prototype.slice.call(arguments, 1); - return Selector.findChildElements(element, args); + element = $(element); + var expressions = Array.prototype.slice.call(arguments, 1).join(', '); + return Prototype.Selector.select(expressions, element); }, adjacent: function(element) { - var args = Array.prototype.slice.call(arguments, 1); - return Selector.findChildElements(element.parentNode, args).without(element); + element = $(element); + var expressions = Array.prototype.slice.call(arguments, 1).join(', '); + return Prototype.Selector.select(expressions, element.parentNode).without(element); }, identify: function(element) { @@ -2227,28 +2358,6 @@ Element.Methods = { return element; }, - getDimensions: function(element) { - element = $(element); - var display = Element.getStyle(element, 'display'); - if (display != 'none' && display != null) // Safari bug - return {width: element.offsetWidth, height: element.offsetHeight}; - - var els = element.style; - var originalVisibility = els.visibility; - var originalPosition = els.position; - var originalDisplay = els.display; - els.visibility = 'hidden'; - if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari - els.position = 'absolute'; - els.display = 'block'; - var originalWidth = element.clientWidth; - var originalHeight = element.clientHeight; - els.display = originalDisplay; - els.position = originalPosition; - els.visibility = originalVisibility; - return {width: originalWidth, height: originalHeight}; - }, - makePositioned: function(element) { element = $(element); var pos = Element.getStyle(element, 'position'); @@ -2295,11 +2404,13 @@ Element.Methods = { cumulativeOffset: function(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 Element._returnOffset(valueL, valueT); }, @@ -2322,11 +2433,11 @@ Element.Methods = { element = $(element); if (Element.getStyle(element, 'position') == 'absolute') return element; - var offsets = Element.positionedOffset(element); - var top = offsets[1]; - var left = offsets[0]; - var width = element.clientWidth; - var height = element.clientHeight; + 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); @@ -2346,8 +2457,8 @@ Element.Methods = { if (Element.getStyle(element, 'position') == 'relative') return element; element.style.position = 'relative'; - var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); - var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + 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'; @@ -2378,9 +2489,10 @@ Element.Methods = { }, viewportOffset: function(forElement) { - var valueT = 0, valueL = 0; + var valueT = 0, + valueL = 0, + element = forElement; - var element = forElement; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; @@ -2412,11 +2524,10 @@ Element.Methods = { }, arguments[2] || { }); source = $(source); - var p = Element.viewportOffset(source); + var p = Element.viewportOffset(source), delta = [0, 0], parent = null; element = $(element); - var delta = [0, 0]; - var parent = null; + if (Element.getStyle(element, 'position') == 'absolute') { parent = Element.getOffsetParent(element); delta = Element.viewportOffset(parent); @@ -2495,8 +2606,7 @@ else if (Prototype.Browser.IE) { Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( function(proceed, element) { element = $(element); - try { element.offsetParent } - catch(e) { return $(document.body) } + if (!element.parentNode) return $(document.body); var position = element.getStyle('position'); if (position !== 'static') return proceed(element); element.setStyle({ position: 'relative' }); @@ -2510,8 +2620,7 @@ else if (Prototype.Browser.IE) { Element.Methods[method] = Element.Methods[method].wrap( function(proceed, element) { element = $(element); - try { element.offsetParent } - catch(e) { return Element._returnOffset(0,0) } + if (!element.parentNode) return Element._returnOffset(0, 0); var position = element.getStyle('position'); if (position !== 'static') return proceed(element); var offsetParent = element.getOffsetParent(); @@ -2525,14 +2634,6 @@ else if (Prototype.Browser.IE) { ); }); - Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( - function(proceed, element) { - try { element.offsetParent } - catch(e) { return Element._returnOffset(0,0) } - return proceed(element); - } - ); - Element.Methods.getStyle = function(element, style) { element = $(element); style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); @@ -2576,10 +2677,9 @@ else if (Prototype.Browser.IE) { Element._attributeTranslations = (function(){ - var classProp = 'className'; - var forProp = 'for'; - - var el = document.createElement('div'); + var classProp = 'className', + forProp = 'for', + el = document.createElement('div'); el.setAttribute(classProp, 'x'); @@ -2622,10 +2722,9 @@ else if (Prototype.Browser.IE) { }, _getEv: (function(){ - var el = document.createElement('div'); + var el = document.createElement('div'), f; el.onclick = Prototype.emptyFunction; var value = el.getAttribute('onclick'); - var f; if (String(value).indexOf('{') > -1) { f = function(element, attribute) { @@ -2753,7 +2852,7 @@ else if (Prototype.Browser.WebKit) { (value < 0.00001) ? 0 : value; if (value == 1) - if(element.tagName.toUpperCase() == 'IMG' && element.width) { + if (element.tagName.toUpperCase() == 'IMG' && element.width) { element.width++; element.width--; } else try { var n = document.createTextNode(' '); @@ -2793,8 +2892,8 @@ if ('outerHTML' in document.documentElement) { var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); if (Element._insertionTranslations.tags[tagName]) { - var nextSibling = element.next(); - var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + var nextSibling = element.next(), + fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); parent.removeChild(element); if (nextSibling) fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); @@ -2816,11 +2915,17 @@ Element._returnOffset = function(l, t) { }; Element._getContentFromAnonymousElement = function(tagName, html) { - var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; + var div = new Element('div'), + t = Element._insertionTranslations.tags[tagName]; if (t) { div.innerHTML = t[0] + html + t[1]; - t[2].times(function() { div = div.firstChild }); - } else div.innerHTML = html; + for (var i = t[2]; i--; ) { + div = div.firstChild; + } + } + else { + div.innerHTML = html; + } return $A(div.childNodes); }; @@ -2877,7 +2982,7 @@ Object.extend(Element, Element.Methods); div = null; -})(document.createElement('div')) +})(document.createElement('div')); Element.extend = (function() { @@ -2885,8 +2990,8 @@ Element.extend = (function() { if (typeof window.Element != 'undefined') { var proto = window.Element.prototype; if (proto) { - var id = '_' + (Math.random()+'').slice(2); - var el = document.createElement(tagName); + var id = '_' + (Math.random()+'').slice(2), + el = document.createElement(tagName); proto[id] = 'x'; var isBuggy = (el[id] !== 'x'); delete proto[id]; @@ -2953,10 +3058,14 @@ Element.extend = (function() { return extend; })(); -Element.hasAttribute = function(element, attribute) { - if (element.hasAttribute) return element.hasAttribute(attribute); - return Element.Methods.Simulated.hasAttribute(element, attribute); -}; +if (document.documentElement.hasAttribute) { + Element.hasAttribute = function(element, attribute) { + return element.hasAttribute(attribute); + }; +} +else { + Element.hasAttribute = Element.Methods.Simulated.hasAttribute; +} Element.addMethods = function(methods) { var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; @@ -3020,8 +3129,9 @@ Element.addMethods = function(methods) { klass = 'HTML' + tagName.capitalize() + 'Element'; if (window[klass]) return window[klass]; - var element = document.createElement(tagName); - var proto = element['__proto__'] || element.constructor.prototype; + var element = document.createElement(tagName), + proto = element['__proto__'] || element.constructor.prototype; + element = null; return proto; } @@ -3104,8 +3214,8 @@ Element.addMethods({ uid = 0; } else { if (typeof element._prototypeUID === "undefined") - element._prototypeUID = [Element.Storage.UID++]; - uid = element._prototypeUID[0]; + element._prototypeUID = Element.Storage.UID++; + uid = element._prototypeUID; } if (!Element.Storage[uid]) @@ -3150,770 +3260,1698 @@ Element.addMethods({ } } return Element.extend(clone); - } -}); -/* Portions of the Selector class are derived from Jack Slocum's DomQuery, - * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style - * license. Please see http://www.yui-ext.com/ for more information. */ - -var Selector = Class.create({ - initialize: function(expression) { - this.expression = expression.strip(); - - if (this.shouldUseSelectorsAPI()) { - this.mode = 'selectorsAPI'; - } else if (this.shouldUseXPath()) { - this.mode = 'xpath'; - this.compileXPathMatcher(); - } else { - this.mode = "normal"; - this.compileMatcher(); - } - }, - shouldUseXPath: (function() { - - var IS_DESCENDANT_SELECTOR_BUGGY = (function(){ - var isBuggy = false; - if (document.evaluate && window.XPathResult) { - var el = document.createElement('div'); - el.innerHTML = '
'; - - var xpath = ".//*[local-name()='ul' or local-name()='UL']" + - "//*[local-name()='li' or local-name()='LI']"; - - var result = document.evaluate(xpath, el, null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - - isBuggy = (result.snapshotLength !== 2); - el = null; - } - return isBuggy; - })(); + purge: function(element) { + if (!(element = $(element))) return; + purgeElement(element); - return function() { - if (!Prototype.BrowserFeatures.XPath) return false; + var descendants = element.getElementsByTagName('*'), + i = descendants.length; - var e = this.expression; + while (i--) purgeElement(descendants[i]); - if (Prototype.Browser.WebKit && - (e.include("-of-type") || e.include(":empty"))) - return false; + return null; + } +}); - if ((/(\[[\w-]*?:|:checked)/).test(e)) - return false; +(function() { - if (IS_DESCENDANT_SELECTOR_BUGGY) return false; + function toDecimal(pctString) { + var match = pctString.match(/^(\d+)%?$/i); + if (!match) return null; + return (Number(match[1]) / 100); + } - return true; + function getPixelValue(value, property) { + if (Object.isElement(value)) { + element = value; + value = element.getStyle(property); } - - })(), - - shouldUseSelectorsAPI: function() { - if (!Prototype.BrowserFeatures.SelectorsAPI) return false; - - if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false; - - if (!Selector._div) Selector._div = new Element('div'); - - try { - Selector._div.querySelector(this.expression); - } catch(e) { - return false; + if (value === null) { + return null; } - return true; - }, + if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) { + return window.parseFloat(value); + } - compileMatcher: function() { - var e = this.expression, ps = Selector.patterns, h = Selector.handlers, - c = Selector.criteria, le, p, m, len = ps.length, name; + if (/\d/.test(value) && element.runtimeStyle) { + var style = element.style.left, rStyle = element.runtimeStyle.left; + element.runtimeStyle.left = element.currentStyle.left; + element.style.left = value || 0; + value = element.style.pixelLeft; + element.style.left = style; + element.runtimeStyle.left = rStyle; - if (Selector._cache[e]) { - this.matcher = Selector._cache[e]; - return; + return value; } - this.matcher = ["this.matcher = function(root) {", - "var r = root, h = Selector.handlers, c = false, n;"]; - - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i = 0; i"; - } -}); + var position = element.getStyle('position'), + width = element.getStyle('width'); -if (Prototype.BrowserFeatures.SelectorsAPI && - document.compatMode === 'BackCompat') { - Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){ - var div = document.createElement('div'), - span = document.createElement('span'); - - div.id = "prototype_test_id"; - span.className = 'Test'; - div.appendChild(span); - var isIgnored = (div.querySelector('#prototype_test_id .test') !== null); - div = span = null; - return isIgnored; - })(); -} + element.setStyle({ + position: 'absolute', + visibility: 'hidden', + display: 'block' + }); -Object.extend(Selector, { - _cache: { }, - - xpath: { - descendant: "//*", - child: "/*", - adjacent: "/following-sibling::*[1]", - laterSibling: '/following-sibling::*', - tagName: function(m) { - if (m[1] == '*') return ''; - return "[local-name()='" + m[1].toLowerCase() + - "' or local-name()='" + m[1].toUpperCase() + "']"; - }, - className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", - id: "[@id='#{1}']", - attrPresence: function(m) { - m[1] = m[1].toLowerCase(); - return new Template("[@#{1}]").evaluate(m); - }, - attr: function(m) { - m[1] = m[1].toLowerCase(); - m[3] = m[5] || m[6]; - return new Template(Selector.xpath.operators[m[2]]).evaluate(m); - }, - pseudo: function(m) { - var h = Selector.xpath.pseudos[m[1]]; - if (!h) return ''; - if (Object.isFunction(h)) return h(m); - return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); - }, - operators: { - '=': "[@#{1}='#{3}']", - '!=': "[@#{1}!='#{3}']", - '^=': "[starts-with(@#{1}, '#{3}')]", - '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", - '*=': "[contains(@#{1}, '#{3}')]", - '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", - '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" - }, - pseudos: { - 'first-child': '[not(preceding-sibling::*)]', - 'last-child': '[not(following-sibling::*)]', - 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', - 'empty': "[count(*) = 0 and (count(text()) = 0)]", - 'checked': "[@checked]", - 'disabled': "[(@disabled) and (@type!='hidden')]", - 'enabled': "[not(@disabled) and (@type!='hidden')]", - 'not': function(m) { - var e = m[6], p = Selector.patterns, - x = Selector.xpath, le, v, len = p.length, name; - - var exclusion = []; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i = 0; i= 0)]"; - return new Template(predicate).evaluate({ - fragment: fragment, a: a, b: b }); - } + var positionedWidth = element.getStyle('width'); + + var newWidth; + if (width && (positionedWidth === width)) { + newWidth = getPixelValue(width); + } else if (width && (position === 'absolute' || position === 'fixed')) { + newWidth = getPixelValue(width); + } else { + var parent = element.parentNode, pLayout = $(parent).getLayout(); + + newWidth = pLayout.get('width') - + this.get('margin-left') - + this.get('border-left') - + this.get('padding-left') - + this.get('padding-right') - + this.get('border-right') - + this.get('margin-right'); } - } - }, - criteria: { - tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', - className: 'n = h.className(n, r, "#{1}", c); c = false;', - id: 'n = h.id(n, r, "#{1}", c); c = false;', - attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', - attr: function(m) { - m[3] = (m[5] || m[6]); - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); - }, - pseudo: function(m) { - if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); - return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); + element.setStyle({ width: newWidth + 'px' }); + + this._prepared = true; }, - descendant: 'c = "descendant";', - child: 'c = "child";', - adjacent: 'c = "adjacent";', - laterSibling: 'c = "laterSibling";' - }, - - patterns: [ - { name: 'laterSibling', re: /^\s*~\s*/ }, - { name: 'child', re: /^\s*>\s*/ }, - { name: 'adjacent', re: /^\s*\+\s*/ }, - { name: 'descendant', re: /^\s/ }, - - { name: 'tagName', re: /^\s*(\*|[\w\-]+)(\b|$)?/ }, - { name: 'id', re: /^#([\w\-\*]+)(\b|$)/ }, - { name: 'className', re: /^\.([\w\-\*]+)(\b|$)/ }, - { name: 'pseudo', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ }, - { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ }, - { name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ } - ], - - assertions: { - tagName: function(element, matches) { - return matches[1].toUpperCase() == element.tagName.toUpperCase(); + + _end: function() { + var element = this.element; + var originalStyles = element.retrieve('prototype_original_styles'); + element.store('prototype_original_styles', null); + element.setStyle(originalStyles); + this._prepared = false; }, - className: function(element, matches) { - return Element.hasClassName(element, matches[1]); + _compute: function(property) { + var COMPUTATIONS = Element.Layout.COMPUTATIONS; + if (!(property in COMPUTATIONS)) { + throw "Property not found."; + } + return this._set(property, COMPUTATIONS[property].call(this, this.element)); }, - id: function(element, matches) { - return element.id === matches[1]; + toObject: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var obj = {}; + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + var value = this.get(key); + if (value != null) obj[key] = value; + }, this); + return obj; }, - attrPresence: function(element, matches) { - return Element.hasAttribute(element, matches[1]); + toHash: function() { + var obj = this.toObject.apply(this, arguments); + return new Hash(obj); }, - attr: function(element, matches) { - var nodeValue = Element.readAttribute(element, matches[1]); - return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); - } - }, + toCSS: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var css = {}; - handlers: { - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - a.push(node); - return a; - }, + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return; - mark: function(nodes) { - var _true = Prototype.emptyFunction; - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = _true; - return nodes; + var value = this.get(key); + if (value != null) css[cssNameFor(key)] = value + 'px'; + }, this); + return css; }, - unmark: (function(){ + inspect: function() { + return "#"; + } + }); - var PROPERTIES_ATTRIBUTES_MAP = (function(){ - var el = document.createElement('div'), - isBuggy = false, - propName = '_countedByPrototype', - value = 'x' - el[propName] = value; - isBuggy = (el.getAttribute(propName) === value); - el = null; - return isBuggy; - })(); - - return PROPERTIES_ATTRIBUTES_MAP ? - function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node.removeAttribute('_countedByPrototype'); - return nodes; - } : - function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = void 0; - return nodes; - } - })(), + Object.extend(Element.Layout, { + PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'), - index: function(parentNode, reverse, ofType) { - parentNode._countedByPrototype = Prototype.emptyFunction; - if (reverse) { - for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { - var node = nodes[i]; - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - } else { - for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - }, + COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'), - unique: function(nodes) { - if (nodes.length == 0) return nodes; - var results = [], n; - for (var i = 0, l = nodes.length; i < l; i++) - if (typeof (n = nodes[i])._countedByPrototype == 'undefined') { - n._countedByPrototype = Prototype.emptyFunction; - results.push(Element.extend(n)); - } - return Selector.handlers.unmark(results); - }, + COMPUTATIONS: { + 'height': function(element) { + if (!this._preComputing) this._begin(); - descendant: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName('*')); - return results; - }, + var bHeight = this.get('border-box-height'); + if (bHeight <= 0) return 0; - child: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) { - for (var j = 0, child; child = node.childNodes[j]; j++) - if (child.nodeType == 1 && child.tagName != '!') results.push(child); - } - return results; - }, + var bTop = this.get('border-top'), + bBottom = this.get('border-bottom'); - adjacent: function(nodes) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - var next = this.nextElementSibling(node); - if (next) results.push(next); - } - return results; - }, + var pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); - laterSibling: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, Element.nextSiblings(node)); - return results; - }, + if (!this._preComputing) this._end(); - nextElementSibling: function(node) { - while (node = node.nextSibling) - if (node.nodeType == 1) return node; - return null; - }, + return bHeight - bTop - bBottom - pTop - pBottom; + }, - previousElementSibling: function(node) { - while (node = node.previousSibling) - if (node.nodeType == 1) return node; - return null; - }, + 'width': function(element) { + if (!this._preComputing) this._begin(); - tagName: function(nodes, root, tagName, combinator) { - var uTagName = tagName.toUpperCase(); - var results = [], h = Selector.handlers; - if (nodes) { - if (combinator) { - if (combinator == "descendant") { - for (var i = 0, node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName(tagName)); - return results; - } else nodes = this[combinator](nodes); - if (tagName == "*") return nodes; - } - for (var i = 0, node; node = nodes[i]; i++) - if (node.tagName.toUpperCase() === uTagName) results.push(node); - return results; - } else return root.getElementsByTagName(tagName); - }, + var bWidth = this.get('border-box-width'); + if (bWidth <= 0) return 0; - id: function(nodes, root, id, combinator) { - var targetNode = $(id), h = Selector.handlers; + var bLeft = this.get('border-left'), + bRight = this.get('border-right'); - if (root == document) { - if (!targetNode) return []; - if (!nodes) return [targetNode]; - } else { - if (!root.sourceIndex || root.sourceIndex < 1) { - var nodes = root.getElementsByTagName('*'); - for (var j = 0, node; node = nodes[j]; j++) { - if (node.id === id) return [node]; - } - } + var pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + if (!this._preComputing) this._end(); + + return bWidth - bLeft - bRight - pLeft - pRight; + }, + + 'padding-box-height': function(element) { + var height = this.get('height'), + pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + return height + pTop + pBottom; + }, + + 'padding-box-width': function(element) { + var width = this.get('width'), + pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + return width + pLeft + pRight; + }, + + 'border-box-height': function(element) { + return element.offsetHeight; + }, + + 'border-box-width': function(element) { + return element.offsetWidth; + }, + + 'margin-box-height': function(element) { + var bHeight = this.get('border-box-height'), + mTop = this.get('margin-top'), + mBottom = this.get('margin-bottom'); + + if (bHeight <= 0) return 0; + + return bHeight + mTop + mBottom; + }, + + 'margin-box-width': function(element) { + var bWidth = this.get('border-box-width'), + mLeft = this.get('margin-left'), + mRight = this.get('margin-right'); + + if (bWidth <= 0) return 0; + + return bWidth + mLeft + mRight; + }, + + 'top': function(element) { + var offset = element.positionedOffset(); + return offset.top; + }, + + 'bottom': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pHeight = parent.measure('height'); + + var mHeight = this.get('border-box-height'); + + return pHeight - mHeight - offset.top; + }, + + 'left': function(element) { + var offset = element.positionedOffset(); + return offset.left; + }, + + 'right': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pWidth = parent.measure('width'); + + var mWidth = this.get('border-box-width'); + + return pWidth - mWidth - offset.left; + }, + + 'padding-top': function(element) { + return getPixelValue(element, 'paddingTop'); + }, + + 'padding-bottom': function(element) { + return getPixelValue(element, 'paddingBottom'); + }, + + 'padding-left': function(element) { + return getPixelValue(element, 'paddingLeft'); + }, + + 'padding-right': function(element) { + return getPixelValue(element, 'paddingRight'); + }, + + 'border-top': function(element) { + return Object.isNumber(element.clientTop) ? element.clientTop : + getPixelValue(element, 'borderTopWidth'); + }, + + 'border-bottom': function(element) { + return Object.isNumber(element.clientBottom) ? element.clientBottom : + getPixelValue(element, 'borderBottomWidth'); + }, + + 'border-left': function(element) { + return Object.isNumber(element.clientLeft) ? element.clientLeft : + getPixelValue(element, 'borderLeftWidth'); + }, + + 'border-right': function(element) { + return Object.isNumber(element.clientRight) ? element.clientRight : + getPixelValue(element, 'borderRightWidth'); + }, + + 'margin-top': function(element) { + return getPixelValue(element, 'marginTop'); + }, + + 'margin-bottom': function(element) { + return getPixelValue(element, 'marginBottom'); + }, + + 'margin-left': function(element) { + return getPixelValue(element, 'marginLeft'); + }, + + 'margin-right': function(element) { + return getPixelValue(element, 'marginRight'); } + } + }); - if (nodes) { - if (combinator) { - if (combinator == 'child') { - for (var i = 0, node; node = nodes[i]; i++) - if (targetNode.parentNode == node) return [targetNode]; - } else if (combinator == 'descendant') { - for (var i = 0, node; node = nodes[i]; i++) - if (Element.descendantOf(targetNode, node)) return [targetNode]; - } else if (combinator == 'adjacent') { - for (var i = 0, node; node = nodes[i]; i++) - if (Selector.handlers.previousElementSibling(targetNode) == node) - return [targetNode]; - } else nodes = h[combinator](nodes); - } - for (var i = 0, node; node = nodes[i]; i++) - if (node == targetNode) return [targetNode]; - return []; + if ('getBoundingClientRect' in document.documentElement) { + Object.extend(Element.Layout.COMPUTATIONS, { + 'right': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.right - rect.right).round(); + }, + + 'bottom': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.bottom - rect.bottom).round(); } - return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; - }, + }); + } - className: function(nodes, root, className, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - return Selector.handlers.byClassName(nodes, root, className); + Element.Offset = Class.create({ + initialize: function(left, top) { + this.left = left.round(); + this.top = top.round(); + + this[0] = this.left; + this[1] = this.top; }, - byClassName: function(nodes, root, className) { - if (!nodes) nodes = Selector.handlers.descendant([root]); - var needle = ' ' + className + ' '; - for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { - nodeClassName = node.className; - if (nodeClassName.length == 0) continue; - if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) - results.push(node); - } - return results; + relativeTo: function(offset) { + return new Element.Offset( + this.left - offset.left, + this.top - offset.top + ); }, - attrPresence: function(nodes, root, attr, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var results = []; - for (var i = 0, node; node = nodes[i]; i++) - if (Element.hasAttribute(node, attr)) results.push(node); - return results; + inspect: function() { + return "#".interpolate(this); }, - attr: function(nodes, root, attr, value, operator, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var handler = Selector.operators[operator], results = []; - for (var i = 0, node; node = nodes[i]; i++) { - var nodeValue = Element.readAttribute(node, attr); - if (nodeValue === null) continue; - if (handler(nodeValue, value)) results.push(node); - } - return results; + toString: function() { + return "[#{left}, #{top}]".interpolate(this); }, - pseudo: function(nodes, name, value, root, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - if (!nodes) nodes = root.getElementsByTagName("*"); - return Selector.pseudos[name](nodes, value, root); + toArray: function() { + return [this.left, this.top]; } - }, + }); - pseudos: { - 'first-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.previousElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'last-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.nextElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'only-child': function(nodes, value, root) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) - results.push(node); - return results; - }, - 'nth-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root); - }, - 'nth-last-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true); - }, - 'nth-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, false, true); - }, - 'nth-last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true, true); - }, - 'first-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, false, true); - }, - 'last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, true, true); - }, - 'only-of-type': function(nodes, formula, root) { - var p = Selector.pseudos; - return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); - }, + function getLayout(element, preCompute) { + return new Element.Layout(element, preCompute); + } - getIndices: function(a, b, total) { - if (a == 0) return b > 0 ? [b] : []; - return $R(1, total).inject([], function(memo, i) { - if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); - return memo; - }); - }, + function measure(element, property) { + return $(element).getLayout().get(property); + } - nth: function(nodes, formula, root, reverse, ofType) { - if (nodes.length == 0) return []; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - var h = Selector.handlers, results = [], indexed = [], m; - h.mark(nodes); - for (var i = 0, node; node = nodes[i]; i++) { - if (!node.parentNode._countedByPrototype) { - h.index(node.parentNode, reverse, ofType); - indexed.push(node.parentNode); - } + function getDimensions(element) { + var layout = $(element).getLayout(); + return { + width: layout.get('width'), + height: layout.get('height') + }; + } + + function getOffsetParent(element) { + if (isDetached(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); } - if (formula.match(/^\d+$/)) { // just a number - formula = Number(formula); - for (var i = 0, node; node = nodes[i]; i++) - if (node.nodeIndex == formula) results.push(node); - } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (m[1] == "-") m[1] = -1; - var a = m[1] ? Number(m[1]) : 1; - var b = m[2] ? Number(m[2]) : 0; - var indices = Selector.pseudos.getIndices(a, b, nodes.length); - for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { - for (var j = 0; j < l; j++) - if (node.nodeIndex == indices[j]) results.push(node); - } + } + + return $(document.body); + } + + + function cumulativeOffset(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return new Element.Offset(valueL, valueT); + } + + function positionedOffset(element) { + var layout = element.getLayout(); + + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if (isBody(element)) break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; } - h.unmark(nodes); - h.unmark(indexed); - return results; - }, + } while (element); + + valueL -= layout.get('margin-top'); + valueT -= layout.get('margin-left'); + + return new Element.Offset(valueL, valueT); + } + + function cumulativeScrollOffset(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return new Element.Offset(valueL, valueT); + } - 'empty': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (node.tagName == '!' || node.firstChild) continue; - results.push(node); + function viewportOffset(forElement) { + var valueT = 0, valueL = 0, docBody = document.body; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == docBody && + Element.getStyle(element, 'position') == 'absolute') break; + } while (element = element.offsetParent); + + element = forElement; + do { + if (element != docBody) { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; } - return results; - }, + } while (element = element.parentNode); + return new Element.Offset(valueL, valueT); + } - 'not': function(nodes, selector, root) { - var h = Selector.handlers, selectorType, m; - var exclusions = new Selector(selector).findElements(root); - h.mark(exclusions); - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node._countedByPrototype) results.push(node); - h.unmark(exclusions); - return results; - }, + function absolutize(element) { + element = $(element); - 'enabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node.disabled && (!node.type || node.type !== 'hidden')) - results.push(node); - return results; - }, + if (Element.getStyle(element, 'position') === 'absolute') { + return element; + } - 'disabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.disabled) results.push(node); - return results; - }, + var offsetParent = getOffsetParent(element); + var eOffset = element.viewportOffset(), + pOffset = offsetParent.viewportOffset(); - 'checked': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.checked) results.push(node); - return results; + var offset = eOffset.relativeTo(pOffset); + var layout = element.getLayout(); + + element.store('prototype_absolutize_original_styles', { + left: element.getStyle('left'), + top: element.getStyle('top'), + width: element.getStyle('width'), + height: element.getStyle('height') + }); + + element.setStyle({ + position: 'absolute', + top: offset.top + 'px', + left: offset.left + 'px', + width: layout.get('width') + 'px', + height: layout.get('height') + 'px' + }); + + return element; + } + + function relativize(element) { + element = $(element); + if (Element.getStyle(element, 'position') === 'relative') { + return element; } - }, - operators: { - '=': function(nv, v) { return nv == v; }, - '!=': function(nv, v) { return nv != v; }, - '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); }, - '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, - '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, - '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, - '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + - '-').include('-' + (v || "").toUpperCase() + '-'); } - }, + var originalStyles = + element.retrieve('prototype_absolutize_original_styles'); + + if (originalStyles) element.setStyle(originalStyles); + return element; + } + + Element.addMethods({ + getLayout: getLayout, + measure: measure, + getDimensions: getDimensions, + getOffsetParent: getOffsetParent, + cumulativeOffset: cumulativeOffset, + positionedOffset: positionedOffset, + cumulativeScrollOffset: cumulativeScrollOffset, + viewportOffset: viewportOffset, + absolutize: absolutize, + relativize: relativize + }); + + function isBody(element) { + return element.nodeName.toUpperCase() === 'BODY'; + } + + function isDetached(element) { + return element !== document.body && + !Element.descendantOf(element, document.body); + } + + if ('getBoundingClientRect' in document.documentElement) { + Element.addMethods({ + viewportOffset: function(element) { + element = $(element); + if (isDetached(element)) return new Element.Offset(0, 0); - split: function(expression) { - var expressions = []; - expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { - expressions.push(m[1].strip()); + 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); + } }); - return expressions; - }, + } +})(); +window.$$ = function() { + var expression = $A(arguments).join(', '); + return Prototype.Selector.select(expression, document); +}; - matchElements: function(elements, expression) { - var matches = $$(expression), h = Selector.handlers; - h.mark(matches); - for (var i = 0, results = [], element; element = elements[i]; i++) - if (element._countedByPrototype) results.push(element); - h.unmark(matches); - return results; - }, +Prototype.Selector = (function() { - findElement: function(elements, expression, index) { - if (Object.isNumber(expression)) { - index = expression; expression = false; + function select() { + throw new Error('Method "Prototype.Selector.select" must be defined.'); + } + + function match() { + throw new Error('Method "Prototype.Selector.match" must be defined.'); + } + + function find(elements, expression, index) { + index = index || 0; + var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i; + + for (i = 0; i < length; i++) { + if (match(elements[i], expression) && index == matchIndex++) { + return Element.extend(elements[i]); + } } - return Selector.matchElements(elements, expression || '*')[index || 0]; - }, + } - findChildElements: function(element, expressions) { - expressions = Selector.split(expressions.join(',')); - var results = [], h = Selector.handlers; - for (var i = 0, l = expressions.length, selector; i < l; i++) { - selector = new Selector(expressions[i].strip()); - h.concat(results, selector.findElements(element)); + function extendElements(elements) { + for (var i = 0, length = elements.length; i < length; i++) { + Element.extend(elements[i]); } - return (l > 1) ? h.unique(results) : results; + return elements; } + + + var K = Prototype.K; + + return { + select: select, + match: match, + find: find, + extendElements: (Element.extend === K) ? K : extendElements, + extendElement: Element.extend + }; +})(); +Prototype._original_property = window.Sizzle; +/*! + * Sizzle CSS Selector Engine - v1.0 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true; + +[0, 0].sort(function(){ + baseHasDuplicate = false; + return 0; }); -if (Prototype.Browser.IE) { - Object.extend(Selector.handlers, { - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - if (node.tagName !== "!") a.push(node); - return a; - } - }); +var Sizzle = function(selector, context, results, seed) { + results = results || []; + var origContext = context = context || document; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context), + soFar = selector; + + while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) + selector += parts.shift(); + + set = posProcess( selector, set ); + } + } + } else { + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + var ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; + } + + if ( context ) { + var ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray(set); + } else { + prune = false; + } + + while ( parts.length ) { + var cur = parts.pop(), pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + throw "Syntax error, unrecognized expression: " + (cur || selector); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + } else if ( context && context.nodeType === 1 ) { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + } else { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function(results){ + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort(sortOrder); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[i-1] ) { + results.splice(i--, 1); + } + } + } + } + + return results; +}; + +Sizzle.matches = function(expr, set){ + return Sizzle(expr, null, null, set); +}; + +Sizzle.find = function(expr, context, isXML){ + var set, match; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var type = Expr.order[i], match; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice(1,1); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace(/\\/g, ""); + set = Expr.find[ type ]( match, context, isXML ); + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = context.getElementsByTagName("*"); + } + + return {set: set, expr: expr}; +}; + +Sizzle.filter = function(expr, set, inplace, not){ + var old = expr, result = [], curLoop = set, match, anyFound, + isXMLFilter = set && set[0] && isXML(set[0]); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.match[ type ].exec( expr )) != null ) { + var filter = Expr.filter[ type ], found, item; + anyFound = false; + + if ( curLoop == result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + } else { + curLoop[i] = false; + } + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + if ( expr == old ) { + if ( anyFound == null ) { + throw "Syntax error, unrecognized expression: " + expr; + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + match: { + ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ + }, + leftMatch: {}, + attrMap: { + "class": "className", + "for": "htmlFor" + }, + attrHandle: { + href: function(elem){ + return elem.getAttribute("href"); + } + }, + relative: { + "+": function(checkSet, part, isXML){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !/\W/.test(part), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag && !isXML ) { + part = part.toUpperCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + ">": function(checkSet, part, isXML){ + var isPartStr = typeof part === "string"; + + if ( isPartStr && !/\W/.test(part) ) { + part = isXML ? part : part.toUpperCase(); + + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName === part ? parent : false; + } + } + } else { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + "": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( !/\W/.test(part) ) { + var nodeCheck = part = isXML ? part : part.toUpperCase(); + checkFn = dirNodeCheck; + } + + checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); + }, + "~": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( typeof part === "string" && !/\W/.test(part) ) { + var nodeCheck = part = isXML ? part : part.toUpperCase(); + checkFn = dirNodeCheck; + } + + checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); + } + }, + find: { + ID: function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? [m] : []; + } + }, + NAME: function(match, context, isXML){ + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], results = context.getElementsByName(match[1]); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + TAG: function(match, context){ + return context.getElementsByTagName(match[1]); + } + }, + preFilter: { + CLASS: function(match, curLoop, inplace, result, not, isXML){ + match = " " + match[1].replace(/\\/g, "") + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { + if ( !inplace ) + result.push( elem ); + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + ID: function(match){ + return match[1].replace(/\\/g, ""); + }, + TAG: function(match, curLoop){ + for ( var i = 0; curLoop[i] === false; i++ ){} + return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase(); + }, + CHILD: function(match){ + if ( match[1] == "nth" ) { + var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( + match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + + match[0] = done++; + + return match; + }, + ATTR: function(match, curLoop, inplace, result, not, isXML){ + var name = match[1].replace(/\\/g, ""); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + PSEUDO: function(match, curLoop, inplace, result, not){ + if ( match[1] === "not" ) { + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + if ( !inplace ) { + result.push.apply( result, ret ); + } + return false; + } + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + POS: function(match){ + match.unshift( true ); + return match; + } + }, + filters: { + enabled: function(elem){ + return elem.disabled === false && elem.type !== "hidden"; + }, + disabled: function(elem){ + return elem.disabled === true; + }, + checked: function(elem){ + return elem.checked === true; + }, + selected: function(elem){ + elem.parentNode.selectedIndex; + return elem.selected === true; + }, + parent: function(elem){ + return !!elem.firstChild; + }, + empty: function(elem){ + return !elem.firstChild; + }, + has: function(elem, i, match){ + return !!Sizzle( match[3], elem ).length; + }, + header: function(elem){ + return /h\d/i.test( elem.nodeName ); + }, + text: function(elem){ + return "text" === elem.type; + }, + radio: function(elem){ + return "radio" === elem.type; + }, + checkbox: function(elem){ + return "checkbox" === elem.type; + }, + file: function(elem){ + return "file" === elem.type; + }, + password: function(elem){ + return "password" === elem.type; + }, + submit: function(elem){ + return "submit" === elem.type; + }, + image: function(elem){ + return "image" === elem.type; + }, + reset: function(elem){ + return "reset" === elem.type; + }, + button: function(elem){ + return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; + }, + input: function(elem){ + return /input|select|textarea|button/i.test(elem.nodeName); + } + }, + setFilters: { + first: function(elem, i){ + return i === 0; + }, + last: function(elem, i, match, array){ + return i === array.length - 1; + }, + even: function(elem, i){ + return i % 2 === 0; + }, + odd: function(elem, i){ + return i % 2 === 1; + }, + lt: function(elem, i, match){ + return i < match[3] - 0; + }, + gt: function(elem, i, match){ + return i > match[3] - 0; + }, + nth: function(elem, i, match){ + return match[3] - 0 == i; + }, + eq: function(elem, i, match){ + return match[3] - 0 == i; + } + }, + filter: { + PSEUDO: function(elem, match, i, array){ + var name = match[1], filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; + } else if ( name === "not" ) { + var not = match[3]; + + for ( var i = 0, l = not.length; i < l; i++ ) { + if ( not[i] === elem ) { + return false; + } + } + + return true; + } + }, + CHILD: function(elem, match){ + var type = match[1], node = elem; + switch (type) { + case 'only': + case 'first': + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) return false; + } + if ( type == 'first') return true; + node = elem; + case 'last': + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) return false; + } + return true; + case 'nth': + var first = match[2], last = match[3]; + + if ( first == 1 && last == 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + if ( first == 0 ) { + return diff == 0; + } else { + return ( diff % first == 0 && diff / first >= 0 ); + } + } + }, + ID: function(elem, match){ + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + TAG: function(elem, match){ + return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; + }, + CLASS: function(elem, match){ + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + ATTR: function(elem, match){ + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value != check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + POS: function(elem, match, i, array){ + var name = match[2], filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source ); +} + +var makeArray = function(array, results) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 ); + +} catch(e){ + makeArray = function(array, results) { + var ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + } else { + if ( typeof array.length === "number" ) { + for ( var i = 0, l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + } else { + for ( var i = 0; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + if ( a == b ) { + hasDuplicate = true; + } + return 0; + } + + var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( "sourceIndex" in document.documentElement ) { + sortOrder = function( a, b ) { + if ( !a.sourceIndex || !b.sourceIndex ) { + if ( a == b ) { + hasDuplicate = true; + } + return 0; + } + + var ret = a.sourceIndex - b.sourceIndex; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( document.createRange ) { + sortOrder = function( a, b ) { + if ( !a.ownerDocument || !b.ownerDocument ) { + if ( a == b ) { + hasDuplicate = true; + } + return 0; + } + + var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); + aRange.setStart(a, 0); + aRange.setEnd(a, 0); + bRange.setStart(b, 0); + bRange.setEnd(b, 0); + var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} + +(function(){ + var form = document.createElement("div"), + id = "script" + (new Date).getTime(); + form.innerHTML = ""; + + var root = document.documentElement; + root.insertBefore( form, root.firstChild ); + + if ( !!document.getElementById( id ) ) { + Expr.find.ID = function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; + } + }; + + Expr.filter.ID = function(elem, match){ + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + root = form = null; // release memory in IE +})(); + +(function(){ + + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function(match, context){ + var results = context.getElementsByTagName(match[1]); + + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + div.innerHTML = ""; + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + Expr.attrHandle.href = function(elem){ + return elem.getAttribute("href", 2); + }; + } + + div = null; // release memory in IE +})(); + +if ( document.querySelectorAll ) (function(){ + var oldSizzle = Sizzle, div = document.createElement("div"); + div.innerHTML = "

"; + + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function(query, context, extra, seed){ + context = context || document; + + if ( !seed && context.nodeType === 9 && !isXML(context) ) { + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(e){} + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + div = null; // release memory in IE +})(); + +if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ + var div = document.createElement("div"); + div.innerHTML = "
"; + + if ( div.getElementsByClassName("e").length === 0 ) + return; + + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) + return; + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function(match, context, isXML) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + div = null; // release memory in IE +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + var sibDir = dir == "previousSibling" && !isXML; + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + if ( sibDir && elem.nodeType === 1 ){ + elem.sizcache = doneName; + elem.sizset = i; + } + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } } -function $$() { - return Selector.findChildElements(document, $A(arguments)); +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + var sibDir = dir == "previousSibling" && !isXML; + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + if ( sibDir && elem.nodeType === 1 ) { + elem.sizcache = doneName; + elem.sizset = i; + } + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } } +var contains = document.compareDocumentPosition ? function(a, b){ + return a.compareDocumentPosition(b) & 16; +} : function(a, b){ + return a !== b && (a.contains ? a.contains(b) : true); +}; + +var isXML = function(elem){ + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML"; +}; + +var posProcess = function(selector, context){ + var tmpSet = [], later = "", match, + root = context.nodeType ? [context] : context; + + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + + +window.Sizzle = Sizzle; + +})(); + +;(function(engine) { + var extendElements = Prototype.Selector.extendElements; + + function select(selector, scope) { + return extendElements(engine(selector, scope || document)); + } + + function match(element, selector) { + return engine.matches(selector, [element]).length == 1; + } + + Prototype.Selector.engine = engine; + Prototype.Selector.select = select; + Prototype.Selector.match = match; +})(Sizzle); + +window.Sizzle = Prototype._original_property; +delete Prototype._original_property; + var Form = { reset: function(form) { form = $(form); @@ -4334,8 +5372,12 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { function findElement(event, expression) { var element = Event.element(event); if (!expression) return element; - var elements = [element].concat(element.ancestors()); - return Selector.findElement(elements, expression, 0); + while (element) { + if (Object.isElement(element) && Prototype.Selector.match(element, expression)) { + return Element.extend(element); + } + element = element.parentNode; + } } function pointer(event) { @@ -4504,12 +5546,12 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { window.addEventListener('unload', Prototype.emptyFunction, false); - var _getDOMEventName = Prototype.K; + var _getDOMEventName = Prototype.K, + translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) { _getDOMEventName = function(eventName) { - var translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; - return eventName in translations ? translations[eventName] : eventName; + return (translations[eventName] || eventName); }; } @@ -4543,38 +5585,29 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { element = $(element); var registry = Element.retrieve(element, 'prototype_event_registry'); + if (!registry) return element; - if (Object.isUndefined(registry)) return element; - - if (eventName && !handler) { - var responders = registry.get(eventName); - - if (Object.isUndefined(responders)) return element; - - responders.each( function(r) { - Element.stopObserving(element, eventName, r.handler); - }); - return element; - } else if (!eventName) { + if (!eventName) { registry.each( function(pair) { - var eventName = pair.key, responders = pair.value; - - responders.each( function(r) { - Element.stopObserving(element, eventName, r.handler); - }); + var eventName = pair.key; + stopObserving(element, eventName); }); return element; } var responders = registry.get(eventName); + if (!responders) return element; - if (!responders) return; + if (!handler) { + responders.each(function(r) { + stopObserving(element, eventName, r.handler); + }); + return element; + } var responder = responders.find( function(r) { return r.handler === handler; }); if (!responder) return element; - var actualEventName = _getDOMEventName(eventName); - if (eventName.include(':')) { if (element.removeEventListener) element.removeEventListener("dataavailable", responder, false); @@ -4583,6 +5616,7 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { element.detachEvent("onfilterchange", responder); } } else { + var actualEventName = _getDOMEventName(eventName); if (element.removeEventListener) element.removeEventListener(actualEventName, responder, false); else @@ -4623,13 +5657,47 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { return Event.extend(event); } + Event.Handler = Class.create({ + initialize: function(element, eventName, selector, callback) { + this.element = $(element); + this.eventName = eventName; + this.selector = selector; + this.callback = callback; + this.handler = this.handleEvent.bind(this); + }, + + start: function() { + Event.observe(this.element, this.eventName, this.handler); + return this; + }, + + stop: function() { + Event.stopObserving(this.element, this.eventName, this.handler); + return this; + }, + + handleEvent: function(event) { + var element = event.findElement(this.selector); + if (element) this.callback.call(this.element, event, element); + } + }); + + function on(element, eventName, selector, callback) { + element = $(element); + if (Object.isFunction(selector) && Object.isUndefined(callback)) { + callback = selector, selector = null; + } + + return new Event.Handler(element, eventName, selector, callback).start(); + } Object.extend(Event, Event.Methods); Object.extend(Event, { fire: fire, observe: observe, - stopObserving: stopObserving + stopObserving: stopObserving, + on: on }); Element.addMethods({ @@ -4637,7 +5705,9 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { observe: observe, - stopObserving: stopObserving + stopObserving: stopObserving, + + on: on }); Object.extend(document, { @@ -4647,6 +5717,8 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { stopObserving: stopObserving.methodize(), + on: on.methodize(), + loaded: false }); @@ -4872,3 +5944,58 @@ Element.ClassNames.prototype = { Object.extend(Element.ClassNames.prototype, Enumerable); /*--------------------------------------------------------------------------*/ + +(function() { + window.Selector = Class.create({ + initialize: function(expression) { + this.expression = expression.strip(); + }, + + findElements: function(rootElement) { + return Prototype.Selector.select(this.expression, rootElement); + }, + + match: function(element) { + return Prototype.Selector.match(element, this.expression); + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Selector, { + matchElements: function(elements, expression) { + var match = Prototype.Selector.match, + results = []; + + for (var i = 0, length = elements.length; i < length; i++) { + var element = elements[i]; + if (match(element, expression)) { + results.push(Element.extend(element)); + } + } + return results; + }, + + findElement: function(elements, expression, index) { + index = index || 0; + var matchIndex = 0, element; + for (var i = 0, length = elements.length; i < length; i++) { + element = elements[i]; + if (Prototype.Selector.match(element, expression) && index === matchIndex++) { + return Element.extend(element); + } + } + }, + + findChildElements: function(element, expressions) { + var selector = expressions.toArray().join(', '); + return Prototype.Selector.select(selector, element || document); + } + }); +})(); -- cgit v1.2.3 From 59945678393b591e263cdee90a8e278a723f93df Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 14 Jul 2010 13:17:01 +0200 Subject: AR queying guide: let limit and offset be different numbers to help making clear what is what in the explanation, rewords also a bit --- railties/guides/source/active_record_querying.textile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index 2e23604838..5c4ed3a803 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -447,28 +447,28 @@ h4. Limit and Offset To apply +LIMIT+ to the SQL fired by the +Model.find+, you can specify the +LIMIT+ using +limit+ and +offset+ methods on the relation. -You can use +limit+ to specify the number of records to be retrieved, and use +offset+ to specify the number of records to skip before starting to return the records. For example: +You can use +limit+ to specify the number of records to be retrieved, and use +offset+ to specify the number of records to skip before starting to return the records. For example Client.limit(5) -This code will return a maximum of 5 clients and because it specifies no offset it will return the first 5 clients in the table. The SQL it executes will look like this: +will return a maximum of 5 clients and because it specifies no offset it will return the first 5 in the table. The SQL it executes looks like this: SELECT * FROM clients LIMIT 5 -Or chaining both +limit+ and +offset+: +Adding +offset+ to that -Client.limit(5).offset(5) +Client.limit(5).offset(30) -This code will return a maximum of 5 clients beginning with the 6th client in the clients table, skipping the first five clients as specified by the offset. The SQL looks like: +will return instead a maximum of 5 clients beginning with the 31st. The SQL looks like: -SELECT * FROM clients LIMIT 5, 5 +SELECT * FROM clients LIMIT 5, 30 h4. Group -- cgit v1.2.3 From 897f86a974636581b8409fd87b288581e2533c28 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 15 Jul 2010 09:47:00 +0200 Subject: layouts and rendering guide: use content_for? instead of the yield or yield idiom --- railties/guides/source/layouts_and_rendering.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile index f4ba6dd53b..b9a201e5f0 100644 --- a/railties/guides/source/layouts_and_rendering.textile +++ b/railties/guides/source/layouts_and_rendering.textile @@ -1168,7 +1168,7 @@ Suppose you have the following +ApplicationController+ layout:
Top menu items here
-
<%= yield(:content) or yield %>
+
<%= content_for?(:content) ? yield(:content) : yield %>
-- cgit v1.2.3 From 5543e8bd6bdc29cfd2e809bdf267b720310af5ec Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Thu, 15 Jul 2010 17:42:41 +0200 Subject: Migrations guide: minor typos corrected --- railties/guides/source/migrations.textile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index f05c0589b5..16f616a5bc 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -1,6 +1,6 @@ h2. Migrations -Migrations are a convenient way for you to alter your database in a structured and organized manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. +Migrations are a convenient way for you to alter your database in a structured and organized manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your +db/schema.rb+ file to match the structure of your database. @@ -234,7 +234,7 @@ end which creates a +products+ table with a column called +name+ (and as discussed below, an implicit +id+ column). -The object yielded to the block allows you create columns on the table. There are two ways of doing this: The first (traditional) form looks like +The object yielded to the block allows you to create columns on the table. There are two ways of doing this: The first (traditional) form looks like create_table :products do |t| @@ -250,7 +250,7 @@ create_table :products do |t| end -By default +create_table+ will create a primary key called +id+. You can change the name of the primary key with the +:primary_key+ option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass +:id => false+. If you need to pass database specific options you can place an SQL fragment in the +:options+ option. For example +By default +create_table+ will create a primary key called +id+. You can change the name of the primary key with the +:primary_key+ option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass +:id => false+. If you need to pass database specific options you can place a SQL fragment in the +:options+ option. For example create_table :products, :options => "ENGINE=BLACKHOLE" do |t| @@ -592,4 +592,5 @@ h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/6 +* July 15, 2010: minor typos corrected by "Jaime Iniesta":http://jaimeiniesta.com * September 14, 2008: initial version by "Frederick Cheung":credits.html#fcheung -- cgit v1.2.3 From 3952268929d72b691c1db925c5e9bf7dff8962ce Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 15 Jul 2010 16:18:25 -0300 Subject: Bump up to rack-mount 0.6.9 and rack-mount-0.6.6.pre removed from action_dispatch vendor --- railties/guides/source/initialization.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 7a44ef7c77..69383f9a73 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -149,7 +149,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This * mime-types-1.16.gem * polyglot-0.3.1.gem * rack-1.1.0.gem -* rack-mount-0.6.5.gem +* rack-mount-0.6.9.gem * rack-test-0.5.4.gem * rails-3.0.0.beta4.gem * railties-3.0.0.beta4.gem -- cgit v1.2.3 From fcb230144b041a95c8e743921e22f8df56949993 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 15 Jul 2010 16:37:12 -0300 Subject: Bump up nokogiri, memcache-client, sqlite3-ruby, fcgi, mail and tzinfo --- railties/guides/source/initialization.textile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 69383f9a73..305602e57d 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -141,20 +141,21 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This * activesupport-3.0.0.beta4.gem * arel-0.4.0.gem * builder-2.1.2.gem -* bundler-1.0.0.beta.2.gem +* bundler-1.0.0.beta.5.gem * erubis-2.6.6.gem * i18n-0.4.1.gem -* mail-2.2.4.gem -* memcache-client-1.8.3.gem +* mail-2.2.5.gem +* memcache-client-1.8.5.gem * mime-types-1.16.gem +* nokogiri-1.4.2.gem * polyglot-0.3.1.gem -* rack-1.1.0.gem +* rack-1.2.1.gem * rack-mount-0.6.9.gem * rack-test-0.5.4.gem * rails-3.0.0.beta4.gem * railties-3.0.0.beta4.gem * rake-0.8.7.gem -* sqlite3-ruby-1.3.0.gem +* sqlite3-ruby-1.3.1.gem * text-format-1.0.0.gem * text-hyphen-1.0.0.gem * thor-0.13.7.gem -- cgit v1.2.3 From 181e0d326de8ce212d951d0e8a8ca0ba478ac5a4 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 16 Jul 2010 00:47:34 +0200 Subject: AS guide: revised the docs of class_attribute and cattr_* macros --- .../source/active_support_core_extensions.textile | 56 ++++++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index 097d51e007..a0ed8d6a90 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -981,7 +981,9 @@ h3. Extensions to +Class+ h4. Class Attributes -The method +Class#class_attribute+ declares one or more inheritable class attributes that can be overridden at any level down the hierarchy: +h5. +class_attribute+ + +The method +class_attribute+ declares one or more inheritable class attributes that can be overridden at any level down the hierarchy: class A @@ -1005,17 +1007,50 @@ A.x # => :a B.x # => :b -For example that's the way the +allow_forgery_protection+ flag is implemented for controllers: +For example +ActionMailer::Base+ defines: + + +class_attribute :default_params +self.default_params = { + :mime_version => "1.0", + :charset => "UTF-8", + :content_type => "text/plain", + :parts_order => [ "text/plain", "text/enriched", "text/html" ] +}.freeze + + +They can be also accessed and overridden at the instance level: + + +A.x = 1 + +a1 = A.new +a2 = A.new +a2.x = 2 + +a1.x # => 1, comes from A +a2.x # => 2, overridden in a2 + + +The generation of the writer instance method can be prevented by setting the option +:instance_writer+ to false, as in -class_attribute :allow_forgery_protection -self.allow_forgery_protection = true +module AcitveRecord + class Base + class_attribute :table_name_prefix, :instance_writer => false + self.table_name_prefix = "" + end +end -For convenience +class_attribute+ defines also a predicate, so that declaration also generates +allow_forgery_protection?+. Such predicate returns the double boolean negation of the value. +A model may find that option useful as a way to prevent mass-assignment from setting the attribute. + +For convenience +class_attribute+ defines also an instance predicate which is the double negation of what the instance reader returns. In the examples above it would be called +x?+. NOTE: Defined in +active_support/core_ext/class/attribute.rb+ +h5. +cattr_reader+, +cattr_writer+, and +cattr_accessor+ + The macros +cattr_reader+, +cattr_writer+, and +cattr_accessor+ are analogous to their +attr_*+ counterparts but for classes. They initialize a class variable to +nil+ unless it already exists, and generate the corresponding class methods to access it: @@ -1026,17 +1061,18 @@ class MysqlAdapter < AbstractAdapter end -Instance methods are created as well for convenience. For example given +Instance methods are created as well for convenience, they are just proxies to the class attribute. So, instances can change the class attribute, but cannot override it as it happens with +class_attribute+ (see above). For example given -module ActionController +module ActionView class Base - cattr_accessor :logger + cattr_accessor :field_error_proc + @@field_error_proc = Proc.new{ ... } end end -we can access +logger+ in actions. The generation of the writer instance method can be prevented setting +:instance_writer+ to +false+ (not any false value, but exactly +false+): +we can access +field_error_proc+ in views. The generation of the writer instance method can be prevented by setting +:instance_writer+ to +false+ (not any false value, but exactly +false+): module ActiveRecord @@ -1047,7 +1083,7 @@ module ActiveRecord end -A model may find that option useful as a way to prevent mass-assignment from setting the attribute. +A model may find that option useful as a way to prevent mass-assignment from setting the attribute. NOTE: Defined in +active_support/core_ext/class/attribute_accessors.rb+. -- cgit v1.2.3 From 041e47b326badfbe1b5e33df172e123386c288ac Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 16 Jul 2010 07:32:40 +0800 Subject: Explain how to change javascript default files --- .../lib/rails/generators/rails/app/templates/config/application.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'railties') diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 67a38ea1d5..589f7e674a 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -39,6 +39,9 @@ module <%= app_const_base %> # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de + # Add your default javascripts + # config.action_view.javascript_expansions[:defaults] = %w(jquery rails) + # Configure generators values. Many other options are available, be sure to check the documentation. # config.generators do |g| # g.orm :active_record -- cgit v1.2.3 From 114fa4d43121cd05fc18a8449a4d1abd92978a72 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 16 Jul 2010 22:53:37 +0800 Subject: Adds application.js when app is generated with -J and clear javascript_expansions[:defaults] --- railties/lib/rails/generators/rails/app/app_generator.rb | 1 + .../lib/rails/generators/rails/app/templates/config/application.rb | 4 ++++ railties/test/generators/app_generator_test.rb | 7 ++++++- 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 7d50e7da67..c99aa3c0cd 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -115,6 +115,7 @@ module Rails directory "public/javascripts" else empty_directory_with_gitkeep "public/javascripts" + create_file "public/javascripts/application.js" end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 589f7e674a..ad8f523c28 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -40,7 +40,11 @@ module <%= app_const_base %> # config.i18n.default_locale = :de # Add your default javascripts +<% if options[:skip_prototype] -%> + config.action_view.javascript_expansions[:defaults] = [] +<% else -%> # config.action_view.javascript_expansions[:defaults] = %w(jquery rails) +<% end -%> # Configure generators values. Many other options are available, be sure to check the documentation. # config.generators do |g| diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 6a3b5de9de..9d7a976afd 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -129,14 +129,19 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_prototype_and_test_unit_are_added_by_default run_generator + 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/rails.js" assert_file "test" end def test_prototype_and_test_unit_are_skipped_if_required run_generator [destination_root, "--skip-prototype", "--skip-testunit"] + assert_file "config/application.rb", /^\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+\[\]/ + assert_file "public/javascripts/application.js" assert_no_file "public/javascripts/prototype.js" - assert_file "public/javascripts" + assert_no_file "public/javascripts/rails.js" assert_no_file "test" end -- cgit v1.2.3 From ef165b355d461dea476875c2f1b7bac18eee3046 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 16 Jul 2010 21:54:13 +0200 Subject: minor pass to generated code in application.rb related to :defaults --- .../lib/rails/generators/rails/app/templates/config/application.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index ad8f523c28..7a94d6e05c 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -22,7 +22,7 @@ module <%= app_const_base %> # -- all .rb files in that directory are automatically loaded. # Custom directories with classes and modules you want to be autoloadable. - # config.autoload_paths += %W( #{config.root}/extras ) + # config.autoload_paths += %W(#{config.root}/extras) # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named. @@ -39,9 +39,9 @@ module <%= app_const_base %> # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de - # Add your default javascripts + # JavaScript files you want as :defaults (application.js is always included). <% if options[:skip_prototype] -%> - config.action_view.javascript_expansions[:defaults] = [] + config.action_view.javascript_expansions[:defaults] = %w() <% else -%> # config.action_view.javascript_expansions[:defaults] = %w(jquery rails) <% end -%> -- cgit v1.2.3 From 38f1ea8fe267947f4baf832d3b15ee5b83f18f71 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 17 Jul 2010 05:22:48 +0800 Subject: Makes test use the new config.action_view.javascript_expansions[:defaults] initialization syntax --- railties/test/generators/app_generator_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 9d7a976afd..7018816af0 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -138,7 +138,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_prototype_and_test_unit_are_skipped_if_required run_generator [destination_root, "--skip-prototype", "--skip-testunit"] - assert_file "config/application.rb", /^\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+\[\]/ + assert_file "config/application.rb", /^\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(\)/ assert_file "public/javascripts/application.js" assert_no_file "public/javascripts/prototype.js" assert_no_file "public/javascripts/rails.js" -- cgit v1.2.3 From c6e20586372743ce200449bf0ac21aed04c6b81e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 17 Jul 2010 09:54:34 +0200 Subject: Add skip_eager_load!, skip_autoload! and friends to path objects. --- railties/lib/rails/paths.rb | 40 ++++++++++++---------------------------- railties/test/paths_test.rb | 24 ++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 30 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb index 7a65188a9a..d303212f52 100644 --- a/railties/lib/rails/paths.rb +++ b/railties/lib/rails/paths.rb @@ -116,36 +116,20 @@ module Rails @paths.concat paths end - def autoload_once! - @autoload_once = true - end - - def autoload_once? - @autoload_once - end - - def eager_load! - @eager_load = true - end - - def eager_load? - @eager_load - end - - def autoload! - @autoload = true - end - - def autoload? - @autoload - end + %w(autoload_once eager_load autoload load_path).each do |m| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{m}! + @#{m} = true + end - def load_path! - @load_path = true - end + def skip_#{m}! + @#{m} = false + end - def load_path? - @load_path + def #{m}? + @#{m} + end + RUBY end def paths diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb index 008247cb1a..80fae8c543 100644 --- a/railties/test/paths_test.rb +++ b/railties/test/paths_test.rb @@ -118,13 +118,23 @@ class PathsTest < ActiveSupport::TestCase assert_raise(RuntimeError) { @root << "/biz" } end - test "it is possible to add a path that should be loaded only once" do + test "it is possible to add a path that should be autoloaded only once" do @root.app = "/app" @root.app.autoload_once! assert @root.app.autoload_once? assert @root.autoload_once.include?(@root.app.paths.first) end + test "it is possible to remove a path that should be autoloaded only once" do + @root.app = "/app" + @root.app.autoload_once! + assert @root.app.autoload_once? + + @root.app.skip_autoload_once! + assert !@root.app.autoload_once? + assert !@root.autoload_once.include?(@root.app.paths.first) + end + test "it is possible to add a path without assignment and specify it should be loaded only once" do @root.app "/app", :autoload_once => true assert @root.app.autoload_once? @@ -152,13 +162,23 @@ class PathsTest < ActiveSupport::TestCase assert_equal 2, @root.autoload_once.size end - test "it is possible to mark a path as eager" do + test "it is possible to mark a path as eager loaded" do @root.app = "/app" @root.app.eager_load! assert @root.app.eager_load? assert @root.eager_load.include?(@root.app.paths.first) end + test "it is possible to skip a path from eager loading" do + @root.app = "/app" + @root.app.eager_load! + assert @root.app.eager_load? + + @root.app.skip_eager_load! + assert !@root.app.eager_load? + assert !@root.eager_load.include?(@root.app.paths.first) + end + test "it is possible to add a path without assignment and mark it as eager" do @root.app "/app", :eager_load => true assert @root.app.eager_load? -- cgit v1.2.3 From caddee253c7a865100af157525861ab58afd4715 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 17 Jul 2010 19:42:44 +0200 Subject: new guide: API Documentation Guidelines, ported and revised from the docrails github wiki --- .../source/api_documentation_guidelines.textile | 187 +++++++++++++++++++++ railties/guides/source/index.html.erb | 16 +- railties/guides/source/layout.html.erb | 5 +- 3 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 railties/guides/source/api_documentation_guidelines.textile (limited to 'railties') diff --git a/railties/guides/source/api_documentation_guidelines.textile b/railties/guides/source/api_documentation_guidelines.textile new file mode 100644 index 0000000000..d9a0d39d9d --- /dev/null +++ b/railties/guides/source/api_documentation_guidelines.textile @@ -0,0 +1,187 @@ +h2. API Documentation Guidelines + +This guide documents the Ruby on Rails API documentation guidelines. + +endprologue. + +h3. RDoc + +The Rails API documentation is generated with RDoc 2.5. Please consult the "RDoc documentation":http://rdoc.rubyforge.org/RDoc.htmlFor for help with its markup. + +h3. Wording + +Write simple, declarative sentences. Brevity is a plus: get to the point. + +Write in present tense: "Returns a hash that...", rather than "Returned a hash that..." or "Will return a hash that...". + +Start comments in upper case, follow regular punctuation rules: + + +# Declares an attribute reader backed by an internally-named instance variable. +def attr_internal_reader(*attrs) + ... +end + + +Communicate to the reader the current way of doing things, both explicitly and implicitly. Use the recommended idioms in edge, reorder sections to emphasize favored approaches if needed, etc. The documentation should be a model for best practices and canonical, modern Rails usage. + +Documentation has to be concise but comprehensive. Explore and document edge cases. What happens if a module is anonymous? What if a collection is empty? What if an argument is nil? + +The proper names of Rails components have a space in between the words, like "Active Support". +ActiveRecord+ is a Ruby module, whereas Active Record is an ORM. Historically there has been lack of consistency regarding this, but we checked with David when docrails started. All Rails documentation consistently refer to Rails components by their proper name, and if in your next blog post or presentation you remember this tidbit and take it into account that'd be fenomenal :). + +Spell names correctly: HTML, MySQL, JavaScript, ERb. + +h3. Example Code + +Choose meaningful examples that depict and cover the basics as well as interesting points or gotchas. + +Use two spaces to indent chunks of code.—that is two spaces with respect to the left margin; the examples +themselves should use "Rails code conventions":http://rails.lighthouseapp.com/projects/8994/source-style. + +Short docs do not need an explicit "Examples" label to introduce snippets, they just follow paragraphs: + + +# Converts a collection of elements into a formatted string by calling +# to_s on all elements and joining them. +# +# Blog.find(:all).to_formatted_s # => "First PostSecond PostThird Post" + + +On the other hand big chunks of structured documentation may have a separate "Examples" section: + + +# ==== Examples +# +# Person.exists?(5) +# Person.exists?('5') +# Person.exists?(:name => "David") +# Person.exists?(['name LIKE ?', "%#{query}%"]) + + +The result of expressions follow them and are introduced by "# => ", vertically aligned: + + +# For checking if a fixnum is even or odd. +# +# 1.even? # => false +# 1.odd? # => true +# 2.even? # => true +# 2.odd? # => false + + +If a line is too long, the comment may be placed on the next line: + + + # label(:post, :title) + # # => + # + # label(:post, :title, "A short title") + # # => + # + # label(:post, :title, "A short title", :class => "title_label") + # # => + + +Avoid using any printing methods like +puts+ or +p+ for that purpose. + +On the other hand, regular comments do not use an arrow: + + +# polymorphic_url(record) # same as comment_url(record) + + +h3. Filenames + +As a rule of thumb use filenames relative to the application root: + + +config/routes.rb # YES +routes.rb # NO +RAILS_ROOT/config/routes.rb # NO + + + +h3. Fonts + +h4. Fixed-width Font + +Use fixed-width fonts for: +* constants, in particular class and module names +* method names +* literals like +nil+, +false+, +true+, +self+ +* symbols +* method parameters +* file names + + +# Copies the instance variables of +object+ into +self+. +# +# Instance variable names in the +exclude+ array are ignored. If +object+ +# responds to protected_instance_variables the ones returned are +# also ignored. For example, Rails controllers implement that method. +# ... +def copy_instance_variables_from(object, exclude = []) + ... +end + + +WARNING: Using a pair of ++...++ for fixed-width font only works with *words*; that is: anything matching \A\w+\z. For anything else use +<tt>...</tt>+, notably symbols, setters, inline snippets, etc: + +h4. Regular Font + +When "true" and "false" are English words rather than Ruby keywords use a regular font: + + +# If reload_plugins? is false, add this to your plugin's init.rb +# to make it reloadable: +# +# Dependencies.load_once_paths.delete lib_path + + +h3. Description Lists + +In lists of options, parameters, etc. use a hyphen between the item and its description (reads better than a colon because normally options are symbols): + + +# * :allow_nil - Skip validation if attribute is +nil+. + + +The description starts in upper case and ends with a full stop—it's standard English. + +h3. Dynamically Generated Methods + +Methods created with +(module|class)_eval(STRING)+ have a comment by their side with an instance of the generated code. That comment is 2 spaces apart from the template: + + +for severity in Severity.constants + class_eval <<-EOT, __FILE__, __LINE__ + def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block) + add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block) + end # end + # + def #{severity.downcase}? # def debug? + #{severity} >= @level # DEBUG >= @level + end # end + EOT +end + + +If the resulting lines are too wide, say 200 columns or more, we put the comment above the call: + + +# def self.find_by_login_and_activated(*args) +# options = args.extract_options! +# ... +# end +self.class_eval %{ + def self.#{method_id}(*args) + options = args.extract_options! + ... + end +} + + +h3. Changelog + +* July 17, 2010: ported from the docrails wiki and revised by "Xavier Noria":credits.html#fxn + diff --git a/railties/guides/source/index.html.erb b/railties/guides/source/index.html.erb index a930db0f1d..254ee91ab4 100644 --- a/railties/guides/source/index.html.erb +++ b/railties/guides/source/index.html.erb @@ -123,10 +123,6 @@ Ruby on Rails Guides <%= guide("Caching with Rails", 'caching_with_rails.html', :ticket => 10) do %>

Various caching techniques provided by Rails.

<% end %> - -<%= guide("Contributing to Rails", 'contributing_to_rails.html') do %> -

Rails is not "somebody else's framework." This guide covers a variety of ways that you can get involved in the ongoing development of Rails.

-<% end %>

Extending Rails

@@ -147,6 +143,18 @@ Ruby on Rails Guides <% end %> +

Contributing to Rails

+ +
+ <%= guide("Contributing to Rails", 'contributing_to_rails.html') do %> +

Rails is not "somebody else's framework." This guide covers a variety of ways that you can get involved in the ongoing development of Rails.

+ <% end %> + + <%= guide('API Documentation Guidelines', 'api_documentation_guidelines.html') do %> +

This guide documents the Ruby on Rails API documentation guidelines.

+ <% end %> +
+

Release Notes

diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb index 501d8fef6d..c4758316ea 100644 --- a/railties/guides/source/layout.html.erb +++ b/railties/guides/source/layout.html.erb @@ -71,13 +71,16 @@
Configuring Rails Applications
Rails Command Line Tools and Rake Tasks
Caching with Rails
-
Contributing to Rails
Extending Rails
The Basics of Creating Rails Plugins
Rails on Rack
Adding a Generator to Your Plugin
+
Contributing to Rails
+
Contributing to Rails
+
API Documentation Guidelines
+
Release Notes
Ruby on Rails 3.0 Release Notes
Ruby on Rails 2.3 Release Notes
-- cgit v1.2.3 From fa98eca75bd8666719bf3d061c87638850a20fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 17 Jul 2010 10:59:41 +0200 Subject: Add console hook to force ActiveRecord::Base to be loaded when console starts avoiding reference loops. --- railties/lib/rails/application.rb | 13 +++++++++++++ railties/lib/rails/commands/console.rb | 5 +---- railties/lib/rails/railtie.rb | 18 ++++++++++-------- railties/test/application/console_test.rb | 21 ++++++++++++++++++--- railties/test/railties/railtie_test.rb | 16 ++++++++++++++++ 5 files changed, 58 insertions(+), 15 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 458177b954..3f9bca0bd6 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -149,6 +149,13 @@ module Rails self end + def load_console(sandbox=false) + initialize_console(sandbox) + railties.all { |r| r.load_console } + super() + self + end + def app @app ||= begin config.middleware = config.middleware.merge_into(default_middleware_stack) @@ -212,5 +219,11 @@ module Rails def initialize_generators require "rails/generators" end + + def initialize_console(sandbox=false) + require "rails/console/app" + require "rails/console/sandbox" if sandbox + require "rails/console/helpers" + end end end diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb index 50df6ba405..834a120c01 100644 --- a/railties/lib/rails/commands/console.rb +++ b/railties/lib/rails/commands/console.rb @@ -23,10 +23,7 @@ module Rails opt.parse!(ARGV) end - @app.initialize! - require "rails/console/app" - require "rails/console/sandbox" if options[:sandbox] - require "rails/console/helpers" + @app.load_console(options[:sandbox]) if options[:debugger] begin diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index dbdbfea509..1d6a2de87d 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -156,6 +156,12 @@ module Rails @rake_tasks end + def console(&blk) + @load_console ||= [] + @load_console << blk if blk + @load_console + end + def generators(&blk) @generators ||= [] @generators << blk if blk @@ -170,20 +176,16 @@ module Rails def eager_load! end - def rake_tasks - self.class.rake_tasks - end - - def generators - self.class.generators + def load_console + self.class.console.each(&:call) end def load_tasks - rake_tasks.each { |blk| blk.call } + self.class.rake_tasks.each(&:call) end def load_generators - generators.each { |blk| blk.call } + self.class.generators.each(&:call) end end end diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb index 8ff69f0208..a72e6916dd 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -9,10 +9,8 @@ class ConsoleTest < Test::Unit::TestCase end def load_environment - # Load steps taken from rails/commands/console.rb require "#{rails_root}/config/environment" - require 'rails/console/app' - require 'rails/console/helpers' + Rails.application.load_console end def test_app_method_should_return_integration_session @@ -75,4 +73,21 @@ class ConsoleTest < Test::Unit::TestCase assert_equal 'Once upon a time in a world...', helper.truncate('Once upon a time in a world far far away') end + + def test_active_record_does_not_panic_when_referencing_an_observed_constant + add_to_config "config.active_record.observers = :user_observer" + + app_file "app/models/user.rb", <<-MODEL + class User < ActiveRecord::Base + end + MODEL + + app_file "app/models/user_observer.rb", <<-MODEL + class UserObserver < ActiveRecord::Observer + end + MODEL + + load_environment + assert_nothing_raised { User } + end end diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb index c74cc01dc1..db0fd87491 100644 --- a/railties/test/railties/railtie_test.rb +++ b/railties/test/railties/railtie_test.rb @@ -103,6 +103,22 @@ module RailtiesTest assert $ran_block end + test "console block is executed when MyApp.load_console is called" do + $ran_block = false + + class MyTie < Rails::Railtie + console do + $ran_block = true + end + end + + require "#{app_path}/config/environment" + + assert !$ran_block + AppTemplate::Application.load_console + assert $ran_block + end + test "railtie can add initializers" do $ran_block = false -- cgit v1.2.3 From b22c11fa533fd523e8cadd36e75dd76b6a9f0488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 19 Jul 2010 15:14:26 +0200 Subject: Add missing entries and tidy up CHANGELOG. --- railties/CHANGELOG | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'railties') diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 8e2bac7f00..bdfaa0decf 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* +* Added console to Rails::Railtie as a hook called just after console starts. [José Valim] + * Rails no longer autoload code in lib for application. You need to explicitly require it. [José Valim] * Rails::LogSubscriber was renamed to ActiveSupport::LogSubscriber [José Valim] @@ -13,26 +15,26 @@ *Rails 3.0.0 [beta 4] (June 8th, 2010)* -* Version bump -* Removed Rails Metal [YK & JV]. +* Removed Rails Metal [Yehuda Kayz, José Valim]. + *Rails 3.0.0 [beta 3] (April 13th, 2010)* -* Renamed config.cookie_secret to config.secret_token and pass it as env key. [JV] +* Renamed config.cookie_secret to config.secret_token and pass it as env key. [José Valim] *Rails 3.0.0 [beta 2] (April 1st, 2010)* -* Session store configuration has changed [YK & CL] +* Session store configuration has changed [Yehuda Katz, Carl Lerche] config.session_store :cookie_store, {:key => "..."} config.cookie_secret = "fdsfhisdghfidugnfdlg" * railtie_name and engine_name are deprecated. You can now add any object to - the configuration object: config.your_plugin = {} [JV] + the configuration object: config.your_plugin = {} [José Valim] * Added config.generators.templates to provide alternative paths for the generators - to look for templates [JV] + to look for templates [José Valim] *Rails 3.0.0 [beta 1] (February 4, 2010)* -- cgit v1.2.3 From fc71d592195b6e04e04cdbb5e640716b0d909e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Sat, 17 Jul 2010 17:28:07 +0800 Subject: Introduced redefine_method --- .../source/active_support_core_extensions.textile | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index a0ed8d6a90..a895dbded2 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -781,6 +781,28 @@ end This may come in handy if you need to define a method that may already exist, since redefining a method issues a warning "method redefined; discarding old redefined_method_name". +h5. +redefine_method(method_name, &block)+ + +The method first removes method with given name (using +remove_possible_method+) and then defines new one. + + +class A; end + +A.class_eval do + redefine_method(:foobar) do |foo| + #do something here + end + + #Code above does the same as this: + + method_name = :foobar + remove_possible_method(method_name) + define_method(method_name) do |foo| + #do something here + end +end + + NOTE: Defined in +active_support/core_ext/module/remove_method.rb+. h4. Parents -- cgit v1.2.3 From 325592038ebf16eb0feb526191155d439e6e1a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 20 Jul 2010 00:22:34 -0700 Subject: Fix typo on CHANGELOG. --- railties/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/CHANGELOG b/railties/CHANGELOG index bdfaa0decf..6a8db7c4a6 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -15,7 +15,7 @@ *Rails 3.0.0 [beta 4] (June 8th, 2010)* -* Removed Rails Metal [Yehuda Kayz, José Valim]. +* Removed Rails Metal [Yehuda Katz, José Valim]. *Rails 3.0.0 [beta 3] (April 13th, 2010)* -- cgit v1.2.3 From d4151d7f0ac4a0823e788c0beed9ec2476e72386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 20 Jul 2010 21:20:19 +0200 Subject: Fix a failing test in Railtie and properly define all severity levels in MockLogger for LogSubscriber. --- .../application/initializers/notifications_test.rb | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'railties') diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb index fc8548af1f..7e035be764 100644 --- a/railties/test/application/initializers/notifications_test.rb +++ b/railties/test/application/initializers/notifications_test.rb @@ -1,17 +1,6 @@ require "isolation/abstract_unit" module ApplicationTests - class MockLogger - def method_missing(*args) - @logged ||= [] - @logged << args.last - end - - def logged - @logged.compact.map { |l| l.to_s.strip } - end - end - class NotificationsTest < Test::Unit::TestCase include ActiveSupport::Testing::Isolation @@ -34,15 +23,17 @@ module ApplicationTests RUBY require "#{app_path}/config/environment" + require "active_support/log_subscriber/test_helper" - ActiveRecord::Base.logger = logger = MockLogger.new + logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new + ActiveRecord::Base.logger = logger # Mimic Active Record notifications instrument "sql.active_record", :name => "SQL", :sql => "SHOW tables" wait - assert_equal 1, logger.logged.size - assert_match /SHOW tables/, logger.logged.last + assert_equal 1, logger.logged(:debug).size + assert_match /SHOW tables/, logger.logged(:debug).last end end end -- cgit v1.2.3 From 6ce761c8d98dafca0baeb298c10dd1fef5e4224f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 20 Jul 2010 17:02:37 -0300 Subject: This example is better for guides and gem/plugins docs --- .../lib/rails/generators/rails/app/templates/config/application.rb | 7 ------- 1 file changed, 7 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 7a94d6e05c..190ab04cf5 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -46,13 +46,6 @@ module <%= app_const_base %> # config.action_view.javascript_expansions[:defaults] = %w(jquery rails) <% end -%> - # Configure generators values. Many other options are available, be sure to check the documentation. - # config.generators do |g| - # g.orm :active_record - # g.template_engine :erb - # g.test_framework :test_unit, :fixture => true - # end - # Configure the default encoding used in templates for Ruby 1.9. config.encoding = "utf-8" -- cgit v1.2.3 From cd1536887bd144dc934dde7ff47b608b490d7766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 12:30:38 +0200 Subject: Improve contribuition guide. --- .../guides/source/contributing_to_rails.textile | 57 ++++++++++++++++------ 1 file changed, 42 insertions(+), 15 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile index 5590895508..f0e9a4b5ec 100644 --- a/railties/guides/source/contributing_to_rails.textile +++ b/railties/guides/source/contributing_to_rails.textile @@ -62,26 +62,39 @@ git clone git://github.com/rails/rails.git cd rails -h4. Pick a Branch +h4. Set up and Run the Tests -Currently, there is active work being done on both the 2-3-stable branch of Rails and on the master branch (which will become Rails 3.0). If you want to work with the master branch, you're all set. To work with 2.3, you'll need to set up and switch to your own local tracking branch: +All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. First, you need to install all Rails dependencies with bundler: -git branch --track 2-3-stable origin/2-3-stable -git checkout 2-3-stable +gem install bundler +bundle install --without db -TIP: You may want to "put your git branch name in your shell prompt":http://github.com/guides/put-your-git-branch-name-in-your-shell-prompt to make it easier to remember which version of the code you're working with. +The second command will install all dependencies, except MySQL and PostgreSQL. We will come back at these soon. With dependencies installed, you can run the whole Rails test suite with: -h4. Set up and Run the Tests + +rake test + -All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. Rails needs the +mocha+ gem for running some tests, so install it with: +You can also run tests for an specific framework, like Action Pack, by going into its directory and executing the same command: -gem install mocha +cd actionpack +rake test -For the tests that touch the database, this means creating test databases. If you're using MySQL, create a user named +rails+ with privileges on the test databases. +h4. Testing Active Record + +By default, when you run Active Record tests, it will execute the test suite three times, one for each of the main databases: SQLite3, MySQL and PostgreSQL. If you are adding a feature that is not specific to the database, you can run the test suite (or just one file) for just one of them. Here is an example for SQLite3: + + +cd activerecord +rake test_sqlite3 +rake test_sqlite3 TEST=test/cases/validations_test.rb + + +If you want to use another database, as MySQL, you need to create a user named +rails+ with privileges on the test databases. mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.* @@ -90,7 +103,13 @@ mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost'; -Enter this from the +activerecord+ directory to create the test databases: +Then ensure you run bundle install without the +--without db+ option: + + +bundle install + + +Finally, enter this from the +activerecord+ directory to create the test databases: rake mysql:build_databases @@ -100,18 +119,26 @@ NOTE: Using the rake task to create the test databases ensures they have the cor If you’re using another database, check the files under +activerecord/test/connections+ in the Rails source code for default connection information. You can edit these files if you _must_ on your machine to provide different credentials, but obviously you should not push any such changes back to Rails. -Now if you go back to the root of the Rails source on your machine and run +rake+ with no parameters, you should see every test in all of the Rails components pass. If you want to run the all ActiveRecord tests (or just a single one) with another database adapter, enter this from the +activerecord+ directory: +You can now run tests as you did for +sqlite3+: -rake test_sqlite3 -rake test_sqlite3 TEST=test/cases/validations_test.rb +rake test_mysql -You can replace +sqlite3+ with +jdbcmysql+, +jdbcsqlite3+, +jdbcpostgresql+, +mysql+ or +postgresql+. 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 +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. +h4. Older versions of Rails -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. +If you want to work add a fix to older versions of Rails, you'll need to set up and switch to your own local tracking branch. Here is an example to switch to Rails 2.3 branch: + + +git branch --track 2-3-stable origin/2-3-stable +git checkout 2-3-stable + + +TIP: You may want to "put your git branch name in your shell prompt":http://github.com/guides/put-your-git-branch-name-in-your-shell-prompt to make it easier to remember which version of the code you're working with. h3. Helping to Resolve Existing Issues -- cgit v1.2.3 From b70062f1e71dc8bda8e9b8159a1f202389a80a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 12:37:05 +0200 Subject: Rework a bit README files. --- railties/README | 280 +++--------------------------------------------------- railties/Rakefile | 7 -- 2 files changed, 12 insertions(+), 275 deletions(-) (limited to 'railties') diff --git a/railties/README b/railties/README index d8be15e346..a1718a7d96 100644 --- a/railties/README +++ b/railties/README @@ -1,281 +1,25 @@ -== Welcome to Rails += Railties -- Gluing the Engine to the Rails -Rails is a web-application framework that includes everything needed to create -database-backed web applications according to the Model-View-Control pattern. +Railties is responsible to glue all frameworks together. Overall, it: -This pattern splits the view (also called the presentation) into "dumb" -templates that are primarily responsible for inserting pre-built data in between -HTML tags. The model contains the "smart" domain objects (such as Account, -Product, Person, Post) that holds all the business logic and knows how to -persist themselves to a database. The controller handles the incoming requests -(such as Save New Account, Update Product, Show Post) by manipulating the model -and directing data to the view. +* handles all the bootstrapping process for a Rails application; -In Rails, the model is handled by what's called an object-relational mapping -layer entitled Active Record. This layer allows you to present the data from -database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in -link:files/vendor/rails/activerecord/README.html. +* manager rails command line interface; -The controller and view are handled by the Action Pack, which handles both -layers by its two parts: Action View and Action Controller. These two layers -are bundled in a single package due to their heavy interdependence. This is -unlike the relationship between the Active Record and Action Pack that is much -more separate. Each of these packages can be used independently outside of -Rails. You can read more about Action Pack in -link:files/vendor/rails/actionpack/README.html. +* provides Rails generators core; -== Getting Started +== Download -1. At the command prompt, create a new Rails application: - rails new myapp (where myapp is the application name) +The latest version of Railties can be installed with Rubygems: -2. Change directory to myapp and start the web server: - cd myapp; rails server (run with --help for options) +* gem install railties -3. Go to http://localhost:3000/ and you'll see: - "Welcome aboard: You're riding Ruby on Rails!" +Documentation can be found at -4. Follow the guidelines to start developing your application. You can find -the following resources handy: +* http://api.rubyonrails.org -* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html -* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ +== License -== Web Servers - -By default, Rails will try to use Mongrel if it's installed when started with -rails server, otherwise Rails will use WEBrick, the web server that -ships with Ruby. - -Mongrel is a Ruby-based web server with a C component (which requires -compilation) that is suitable for development. If you have Ruby Gems installed, -getting up and running with mongrel is as easy as: - sudo gem install mongrel. - -You can find more info at: http://mongrel.rubyforge.org - -You can alternatively run Rails applications with other Ruby web servers, e.g., -{Thin}[http://code.macournoyer.com/thin/], {Ebb}[http://ebb.rubyforge.org/], and -Apache with {mod_rails}[http://www.modrails.com/]. However, rails server -doesn't search for or start them. - -For production use, often a web/proxy server, e.g., {Apache}[http://apache.org], -{Nginx}[http://nginx.net/], {LiteSpeed}[http://litespeedtech.com/], -{Lighttpd}[http://www.lighttpd.net/], or {IIS}[http://www.iis.net/], is deployed -as the front end server with the chosen Ruby web server running in the back end -and receiving the proxied requests via one of several protocols (HTTP, CGI, FCGI). - - -== Debugging Rails - -Sometimes your application goes wrong. Fortunately there are a lot of tools that -will help you debug it and get it back on the rails. - -First area to check is the application log files. Have "tail -f" commands -running on the server.log and development.log. Rails will automatically display -debugging and runtime information to these files. Debugging info will also be -shown in the browser on requests from 127.0.0.1. - -You can also log your own messages directly into the log file from your code -using the Ruby logger class from inside your controllers. Example: - - class WeblogController < ActionController::Base - def destroy - @weblog = Weblog.find(params[:id]) - @weblog.destroy - logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") - end - end - -The result will be a message in your log file along the lines of: - - Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! - -More information on how to use the logger is at http://www.ruby-doc.org/core/ - -Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are -several books available online as well: - -* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) -* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) - -These two books will bring you up to speed on the Ruby language and also on -programming in general. - - -== Debugger - -Debugger support is available through the debugger command when you start your -Mongrel or WEBrick server with --debugger. This means that you can break out of -execution at any point in the code, investigate and change the model, and then, -resume execution! You need to install ruby-debug to run the server in debugging -mode. With gems, use sudo gem install ruby-debug. Example: - - class WeblogController < ActionController::Base - def index - @posts = Post.find(:all) - debugger - end - end - -So the controller will accept the action, run the first line, then present you -with a IRB prompt in the server window. Here you can do things like: - - >> @posts.inspect - => "[#nil, "body"=>nil, "id"=>"1"}>, - #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" - >> @posts.first.title = "hello from a debugger" - => "hello from a debugger" - -...and even better, you can examine how your runtime objects actually work: - - >> f = @posts.first - => #nil, "body"=>nil, "id"=>"1"}> - >> f. - Display all 152 possibilities? (y or n) - -Finally, when you're ready to resume execution, you can enter "cont". - - -== Console - -The console is a Ruby shell, which allows you to interact with your -application's domain model. Here you'll have all parts of the application -configured, just like it is when the application is running. You can inspect -domain models, change values, and save to the database. Starting the script -without arguments will launch it in the development environment. - -To start the console, run rails console from the application -directory. - -Options: - -* Passing the -s, --sandbox argument will rollback any modifications - made to the database. -* Passing an environment name as an argument will load the corresponding - environment. Example: rails console production. - -To reload your controllers and models after launching the console run -reload! - -More information about irb can be found at: -link:http://www.rubycentral.com/pickaxe/irb.html - - -== dbconsole - -You can go to the command line of your database directly through rails -dbconsole. You would be connected to the database with the credentials -defined in database.yml. Starting the script without arguments will connect you -to the development database. Passing an argument will connect you to a different -database, like rails dbconsole production. Currently works for MySQL, -PostgreSQL and SQLite 3. - -== Description of Contents - -The default directory structure of a generated Ruby on Rails application: - - |-- app - | |-- controllers - | |-- helpers - | |-- models - | `-- views - | `-- layouts - |-- config - | |-- environments - | |-- initializers - | `-- locales - |-- db - |-- doc - |-- lib - | `-- tasks - |-- log - |-- public - | |-- images - | |-- javascripts - | `-- stylesheets - |-- script - | `-- performance - |-- test - | |-- fixtures - | |-- functional - | |-- integration - | |-- performance - | `-- unit - |-- tmp - | |-- cache - | |-- pids - | |-- sessions - | `-- sockets - `-- vendor - `-- plugins - -app - Holds all the code that's specific to this particular application. - -app/controllers - Holds controllers that should be named like weblogs_controller.rb for - automated URL mapping. All controllers should descend from - ApplicationController which itself descends from ActionController::Base. - -app/models - Holds models that should be named like post.rb. Models descend from - ActiveRecord::Base by default. - -app/views - Holds the template files for the view that should be named like - weblogs/index.html.erb for the WeblogsController#index action. All views use - eRuby syntax by default. - -app/views/layouts - Holds the template files for layouts to be used with views. This models the - common header/footer method of wrapping views. In your views, define a layout - using the layout :default and create a file named default.html.erb. - Inside default.html.erb, call <% yield %> to render the view using this - layout. - -app/helpers - Holds view helpers that should be named like weblogs_helper.rb. These are - generated for you automatically when using generators for controllers. - Helpers can be used to wrap functionality for your views into methods. - -config - Configuration files for the Rails environment, the routing map, the database, - and other dependencies. - -db - Contains the database schema in schema.rb. db/migrate contains all the - sequence of Migrations for your schema. - -doc - This directory is where your application documentation will be stored when - generated using rake doc:app - -lib - Application specific libraries. Basically, any kind of custom code that - doesn't belong under controllers, models, or helpers. This directory is in - the load path. - -public - The directory available for the web server. Contains subdirectories for - images, stylesheets, and javascripts. Also contains the dispatchers and the - default HTML files. This should be set as the DOCUMENT_ROOT of your web - server. - -script - Helper scripts for automation and generation. - -test - Unit and functional tests along with fixtures. When using the rails generate - command, template test files will be generated for you and placed in this - directory. - -vendor - External libraries that the application depends on. Also includes the plugins - subdirectory. If the app has frozen rails, those gems also go here, under - vendor/rails/. This directory is in the load path. +Railties is released under the MIT license. diff --git a/railties/Rakefile b/railties/Rakefile index ddc872e18b..ae9db3c022 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -35,13 +35,6 @@ end # Update spinoffs ------------------------------------------------------------------- -desc "Updates application README to the latest version Railties README" -task :update_readme do - readme = "lib/rails/generators/rails/app/templates/README" - rm readme - cp "./README", readme -end - desc 'Generate guides (for authors), use ONLY=foo to process just "foo.textile"' task :generate_guides do ENV["WARN_BROKEN_LINKS"] = "1" # authors can't disable this -- cgit v1.2.3 From 508fba9e070e09f0a321f2dd7acf7938967468f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 12:51:14 +0200 Subject: Add .rdoc extension to README files. --- railties/README | 25 ------------------------- railties/README.rdoc | 25 +++++++++++++++++++++++++ railties/Rakefile | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 railties/README create mode 100644 railties/README.rdoc (limited to 'railties') diff --git a/railties/README b/railties/README deleted file mode 100644 index a1718a7d96..0000000000 --- a/railties/README +++ /dev/null @@ -1,25 +0,0 @@ -= Railties -- Gluing the Engine to the Rails - -Railties is responsible to glue all frameworks together. Overall, it: - -* handles all the bootstrapping process for a Rails application; - -* manager rails command line interface; - -* provides Rails generators core; - - -== Download - -The latest version of Railties can be installed with Rubygems: - -* gem install railties - -Documentation can be found at - -* http://api.rubyonrails.org - - -== License - -Railties is released under the MIT license. diff --git a/railties/README.rdoc b/railties/README.rdoc new file mode 100644 index 0000000000..a1718a7d96 --- /dev/null +++ b/railties/README.rdoc @@ -0,0 +1,25 @@ += Railties -- Gluing the Engine to the Rails + +Railties is responsible to glue all frameworks together. Overall, it: + +* handles all the bootstrapping process for a Rails application; + +* manager rails command line interface; + +* provides Rails generators core; + + +== Download + +The latest version of Railties can be installed with Rubygems: + +* gem install railties + +Documentation can be found at + +* http://api.rubyonrails.org + + +== License + +Railties is released under the MIT license. diff --git a/railties/Rakefile b/railties/Rakefile index ae9db3c022..19c860f257 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -59,7 +59,7 @@ Rake::RDocTask.new { |rdoc| rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' - rdoc.rdoc_files.include('README', 'CHANGELOG') + rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG') rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.exclude('lib/rails/generators/**/templates/*') } -- cgit v1.2.3 From 79d6f314c68a4d833a47d318b392808d530925c4 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 21 Jul 2010 11:02:13 -0300 Subject: We are doing the same in this conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/configuration.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 0becb780de..74b2bcaeb1 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -56,9 +56,7 @@ module Rails return @options[method] if args.empty? - if method == :rails - namespace, configuration = :rails, args.shift - elsif args.first.is_a?(Hash) + if method == :rails || args.first.is_a?(Hash) namespace, configuration = method, args.shift else namespace, configuration = args.shift, args.shift -- cgit v1.2.3 From e107c208f0422b83df473666e30256f837c263eb Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 21 Jul 2010 11:37:03 -0300 Subject: Make config.generators accept string namespaces, you can do now config.generators.test_framework 'rspec' for instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/configuration.rb | 1 + railties/test/application/generators_test.rb | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'railties') diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 74b2bcaeb1..e5af12b901 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -60,6 +60,7 @@ module Rails namespace, configuration = method, args.shift else namespace, configuration = args.shift, args.shift + namespace = namespace.to_sym if namespace.respond_to?(:to_sym) @options[:rails][method] = namespace end diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index cbf0decd07..d258625f42 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -103,5 +103,20 @@ module ApplicationTests assert_equal({ :plugin => { :generator => "-g" } }, c.generators.aliases) end end + + test "generators with string and hash for options should generate symbol keys" do + with_bare_config do |c| + c.generators do |g| + g.orm 'datamapper', :migration => false + end + + expected = { + :rails => { :orm => :datamapper }, + :datamapper => { :migration => false } + } + + assert_equal expected, c.generators.options + end + end end end -- cgit v1.2.3 From d16c5cc99b4ac5a5517b643aabb3b31bf0f0f1b6 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 01:00:01 +0800 Subject: Change some missing README -> README.rdoc --- railties/lib/rails/tasks/documentation.rake | 16 ++++++++-------- railties/railties.gemspec | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake index 492f05e3cc..843d2b4e82 100644 --- a/railties/lib/rails/tasks/documentation.rake +++ b/railties/lib/rails/tasks/documentation.rake @@ -55,46 +55,46 @@ namespace :doc do rdoc.template = "#{ENV['template']}.rb" if ENV['template'] rdoc.title = "Rails Framework Documentation" rdoc.options << '--line-numbers' << '--inline-source' - rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('README.rdoc') gem_path('actionmailer') do |actionmailer| - %w(README CHANGELOG MIT-LICENSE lib/action_mailer/base.rb).each do |file| + %w(README.rdoc CHANGELOG MIT-LICENSE lib/action_mailer/base.rb).each do |file| rdoc.rdoc_files.include("#{actionmailer}/#{file}") end end gem_path('actionpack') do |actionpack| - %w(README CHANGELOG MIT-LICENSE lib/action_controller/**/*.rb lib/action_view/**/*.rb).each do |file| + %w(README.rdoc CHANGELOG MIT-LICENSE lib/action_controller/**/*.rb lib/action_view/**/*.rb).each do |file| rdoc.rdoc_files.include("#{actionpack}/#{file}") end end gem_path('activemodel') do |activemodel| - %w(README CHANGELOG MIT-LICENSE lib/active_model/**/*.rb).each do |file| + %w(README.rdoc CHANGELOG MIT-LICENSE lib/active_model/**/*.rb).each do |file| rdoc.rdoc_files.include("#{activemodel}/#{file}") end end gem_path('activerecord') do |activerecord| - %w(README CHANGELOG lib/active_record/**/*.rb).each do |file| + %w(README.rdoc CHANGELOG lib/active_record/**/*.rb).each do |file| rdoc.rdoc_files.include("#{activerecord}/#{file}") end end gem_path('activeresource') do |activeresource| - %w(README CHANGELOG lib/active_resource.rb lib/active_resource/*).each do |file| + %w(README.rdoc CHANGELOG lib/active_resource.rb lib/active_resource/*).each do |file| rdoc.rdoc_files.include("#{activeresource}/#{file}") end end gem_path('activesupport') do |activesupport| - %w(README CHANGELOG lib/active_support/**/*.rb).each do |file| + %w(README.rdoc CHANGELOG lib/active_support/**/*.rb).each do |file| rdoc.rdoc_files.include("#{activesupport}/#{file}") end end gem_path('railties') do |railties| - %w(README CHANGELOG lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file| + %w(README.rdoc CHANGELOG lib/{*.rb,commands/*.rb,generators/*.rb}).each do |file| rdoc.rdoc_files.include("#{railties}/#{file}") end end diff --git a/railties/railties.gemspec b/railties/railties.gemspec index 247b926af9..38dcb17ff1 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -13,7 +13,7 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'rails' - s.files = Dir['CHANGELOG', 'README', 'bin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}'] + s.files = Dir['CHANGELOG', 'README.rdoc', 'bin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}'] s.require_path = 'lib' s.rdoc_options << '--exclude' << '.' -- cgit v1.2.3 From 402aaa56f5cd4470b9b8032d715e6fb0bdb1bf73 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Tue, 20 Jul 2010 14:22:19 +0200 Subject: Active Record Validations and Callbacks guide: Fixed typos and rephrased some paragraphs for clarity --- .../active_record_validations_callbacks.textile | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 84c33e34f9..be9917868f 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -1,22 +1,22 @@ h2. Active Record Validations and Callbacks -This guide teaches you how to hook into the lifecycle of your Active Record objects. You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object lifecycle. +This guide teaches you how to hook into the life cycle of your Active Record objects. You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object life cycle. After reading this guide and trying out the presented concepts, we hope that you'll be able to: -* Understand the lifecycle of Active Record objects +* Understand the life cycle of Active Record objects * Use the built-in Active Record validation helpers * Create your own custom validation methods * Work with the error messages generated by the validation process -* Create callback methods that respond to events in the object lifecycle +* Create callback methods that respond to events in the object life cycle * Create special classes that encapsulate common behavior for your callbacks -* Create Observers that respond to lifecycle events outside of the original class +* Create Observers that respond to life cycle events outside of the original class endprologue. -h3. The Object Lifecycle +h3. The Object Life Cycle -During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this object lifecycle so that you can control your application and its data. +During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this object life cycle so that you can control your application and its data. Validations allow you to ensure that only valid data is stored in your database. Callbacks and observers allow you to trigger logic before or after an alteration of an object's state. @@ -57,7 +57,7 @@ We can see how it works by looking at some +rails console+ output: => false -Creating and saving a new record will send an SQL +INSERT+ operation to the database. Updating an existing record will send an SQL +UPDATE+ operation instead. Validations are typically run before these commands are sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the +INSERT+ or +UPDATE+ operation. This helps to avoid storing an invalid object in the database. You can choose to have specific validations run when an object is created, saved, or updated. +Creating and saving a new record will send a SQL +INSERT+ operation to the database. Updating an existing record will send a SQL +UPDATE+ operation instead. Validations are typically run before these commands are sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the +INSERT+ or +UPDATE+ operation. This helps to avoid storing an invalid object in the database. You can choose to have specific validations run when an object is created, saved, or updated. CAUTION: There are many ways to change the state of an object in the database. Some methods will trigger validations, but some will not. This means that it's possible to save an object in the database in an invalid state if you aren't careful. @@ -838,7 +838,7 @@ This will result in something like the following: h3. Callbacks Overview -Callbacks are methods that get called at certain moments of an object's lifecycle. With callbacks it's possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database. +Callbacks are methods that get called at certain moments of an object's life cycle. With callbacks it's possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database. h4. Callback Registration @@ -984,7 +984,7 @@ h3. Halting Execution As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed. -The whole callback chain is wrapped in a transaction. If any before callback method returns exactly +false+ or raises an exception the execution chain gets halted and a ROLLBACK is issued. After callbacks can only accomplish that by raising an exception. +The whole callback chain is wrapped in a transaction. If any before callback method returns exactly +false+ or raises an exception the execution chain gets halted and a ROLLBACK is issued; after callbacks can only accomplish that by raising an exception. WARNING. Raising an arbitrary exception may break code that expects +save+ and friends not to fail like that. The +ActiveRecord::Rollback+ exception is thought precisely to tell Active Record a rollback is going on. That one is internally captured but not reraised. @@ -1020,7 +1020,7 @@ Like in validations, we can also make our callbacks conditional, calling them on h4. Using +:if+ and +:unless+ with a Symbol -You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. If this method returns +false+ the callback won't be executed. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check if the callback should be executed. +You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. When using the +:if+ option, the callback won't be executed if the method returns +false+; when using the +:unless+ option, the callback won't be executed if the method returns +true+. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check if the callback should be executed. class Order < ActiveRecord::Base @@ -1135,7 +1135,7 @@ Observers are conventionally placed inside of your +app/models+ directory and re config.active_record.observers = :user_observer -As usual, settings in +config/environments+ take precedence over those in +config/environment.rb+. So, if you prefer that an observer not run in all environments, you can simply register it in a specific environment instead. +As usual, settings in +config/environments+ take precedence over those in +config/environment.rb+. So, if you prefer that an observer doesn't run in all environments, you can simply register it in a specific environment instead. h4. Sharing Observers @@ -1162,6 +1162,7 @@ h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks +* July 20, 2010: Fixed typos and rephrased some paragraphs for clarity. "Jaime Iniesta":http://jaimeiniesta.com * May 24, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com * May 15, 2010: Validation Errors section updated by "Emili Parreño":http://www.eparreno.com * March 7, 2009: Callbacks revision by Trevor Turk -- cgit v1.2.3 From 6f7402d32b7b867844fc4ffff891474dc348d0d1 Mon Sep 17 00:00:00 2001 From: Jaime Iniesta Date: Tue, 20 Jul 2010 18:17:29 +0200 Subject: non-singleton true and false should go on regular font --- railties/guides/source/active_record_validations_callbacks.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index be9917868f..c7ba130a90 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -1020,7 +1020,7 @@ Like in validations, we can also make our callbacks conditional, calling them on h4. Using +:if+ and +:unless+ with a Symbol -You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. When using the +:if+ option, the callback won't be executed if the method returns +false+; when using the +:unless+ option, the callback won't be executed if the method returns +true+. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check if the callback should be executed. +You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. When using the +:if+ option, the callback won't be executed if the method returns false; when using the +:unless+ option, the callback won't be executed if the method returns true. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check if the callback should be executed. class Order < ActiveRecord::Base -- cgit v1.2.3 From e9127ce7e89ccaddb04a5c2724e18eba2491a053 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 22 Jul 2010 00:16:26 +0200 Subject: routing guide: a "photo" resource has by convention paths under "photos", in plural --- railties/guides/source/routing.textile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 72a76e25bb..ae80ba77e4 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -441,13 +441,13 @@ h4. Segment Constraints You can use the +:constraints+ option to enforce a format for a dynamic segment: -match 'photo/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ } +match 'photos/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ } -This route would match URLs such as +/photo/A12345+. You can more succinctly express the same route this way: +This route would match URLs such as +/photos/A12345+. You can more succinctly express the same route this way: -match 'photo/:id' => 'photos#show', :id => /[A-Z]\d{5}/ +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: @@ -472,7 +472,7 @@ You can also constrain a route based on any method on the {:subdomain => "admin"} +match "photos", :constraints => {:subdomain => "admin"} You can also specify constrains in a block form: @@ -511,10 +511,10 @@ h4. Route Globbing Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example -match 'photo/*other' => 'photos#unknown' +match 'photos/*other' => 'photos#unknown' -This route would match +photo/12+ or +/photo/long/path/to/12+, setting +params[:other]+ to +"12"+ or +"long/path/to/12"+. +This route would match +photos/12+ or +/photos/long/path/to/12+, setting +params[:other]+ to +"12"+ or +"long/path/to/12"+. h4. Redirection -- cgit v1.2.3 From b72cc472f762a6201e744b2def7467afb363b625 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 22 Jul 2010 00:32:39 +0200 Subject: routing guide: say "path" when you mean path --- railties/guides/source/routing.textile | 72 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index ae80ba77e4..7e6d6b5b34 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -5,14 +5,14 @@ This guide covers the user-facing features of Rails routing. By referring to thi * Understand the code in +routes.rb+ * Construct your own routes, using either the preferred resourceful style or with the @match@ method * Identify what parameters to expect an action to receive -* Automatically create URLs using route helpers +* Automatically create paths and URLs using route helpers * Use advanced techniques such as constraints and Rack endpoints endprologue. h3. The Purpose of the Rails Router -The Rails router recognizes URLs and dispatches them to a controller's action. It can also generate URLs, avoiding the need to hardcode URL strings in your views. +The Rails router recognizes URLs and dispatches them to a controller's action. It can also generate paths and URLs, avoiding the need to hardcode strings in your views. h4. Connecting URLs to Code @@ -30,9 +30,9 @@ match "/patients/:id" => "patients#show" the request is dispatched to the +patients+ controller's +show+ action with { :id => "17" } in +params+. -h4. Generating URLs from Code +h4. Generating Paths and URLs from Code -You can also generate URLs. If your application contains this code: +You can also generate paths and URLs. If your application contains this code: @patient = Patient.find(17) @@ -76,7 +76,7 @@ resources :photos creates seven different routes in your application, all mapping to the +Photos+ controller: -|_. Verb |_.URL |_.action |_.used for| +|_. 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| @@ -85,7 +85,7 @@ creates seven different routes in your application, all mapping to the +Photos+ |PUT |/photos/:id |update |update a specific photo| |DELETE |/photos/:id |destroy |delete a specific photo| -h4. URLs and Paths +h4. Paths and URLs Creating a resourceful route will also expose a number of helpers to the controllers in your application. In the case of +resources :photos+: @@ -130,7 +130,7 @@ resource :geocoder creates six different routes in your application, all mapping to the +Geocoders+ controller: -|_. Verb |_.URL |_.action |_.used for| +|_. 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| @@ -160,7 +160,7 @@ end This will create a number of routes for each of the +posts+ and +comments+ controller. For +Admin::PostsController+, Rails will create: -|_. Verb |_.URL |_.action |_. helper | +|_. Verb |_.Path |_.action |_. helper | |GET |/admin/photos |index | admin_photos_path | |GET |/admin/photos/new |new | new_admin_photos_path | |POST |/admin/photos |create | admin_photos_path | @@ -197,16 +197,16 @@ or, for a single case resources :posts, :path => "/admin" -In each of these cases, the named routes remain the same as if you did not use +scope+. In the last case, the following URLs map to +PostsController+: +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 |_.URL |_.action |_. helper | -|GET |photos |index | photos_path | -|GET |photos/new |new | photos_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) | +|_. Verb |_.Path |_.action |_. helper | +|GET |/admin/photos |index | photos_path | +|GET |/admin/photos/new |new | photos_path | +|POST |/admin/photos |create | photos_path | +|GET |/admin/photos/1 |show | photo_path(id) | +|GET |/admin/photos/1/edit |edit | edit_photo_path(id) | +|PUT |/admin/photos/1 |update | photo_path(id) | +|DELETE |/admin/photos/1 |destroy | photo_path(id) | h4. Nested Resources @@ -232,7 +232,7 @@ end In addition to the routes for magazines, this declaration will also route ads to an +AdsController+. The ad URLs require a magazine: -|_.Verb |_.URL |_.action |_.used for| +|_.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| @@ -256,7 +256,7 @@ resources :publishers do end -Deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize URLs such as +Deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize paths such as
 /publishers/1/magazines/2/photos/3
@@ -266,9 +266,9 @@ The corresponding route helper would be +publisher_magazine_photo_url+, requirin
 
 TIP: _Resources should never be nested more than 1 level deep._
 
-h4. Creating URLs From Objects
+h4. Creating Paths and URLs From Objects
 
-In addition to using the routing helpers, Rails can also create URLs from an array of parameters. For example, suppose you have this set of routes:
+In addition to using the routing helpers, Rails can also create paths and URLs from an array of parameters. For example, suppose you have this set of routes:
 
 
 resources :magazines do
@@ -340,7 +340,7 @@ resources :photos do
 end
 
 
-This will enable Rails to recognize URLs such as +/photos/search+ with GET, and route to the +search+ action of +PhotosController+. It will also create the +search_photos_url+ and +search_photos_path+ route helpers.
+This will enable Rails to recognize paths such as +/photos/search+ with GET, and route to the +search+ action of +PhotosController+. It will also create the +search_photos_url+ and +search_photos_path+ route helpers.
 
 Just as with member routes, you can pass +:on+ to a route:
 
@@ -380,7 +380,7 @@ You can set up as many dynamic segments within a regular route as you like. Anyt
 match ':controller/:action/:id/:user_id'
 
 
-An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +PhotosController+. +params[:id]+ will be +"1"+, and +params[:user_id]+ will be +"2"+.
+An incoming path of +/photos/show/1/2+ will be dispatched to the +show+ action of the +PhotosController+. +params[:id]+ will be +"1"+, and +params[:user_id]+ will be +"2"+.
 
 NOTE: You can't use +namespace+ or +:module+ with a +:controller+ path segment. If you need to do this then use a constraint on :controller that matches the namespace you require. e.g:
 
@@ -396,7 +396,7 @@ You can specify static segments when creating a route:
 match ':controller/:action/:id/with_user/:user_id'
 
 
-This route would respond to URLs such as +/photos/show/1/with_user/2+. In this case, +params+ would be { :controller => "photos", :action => "show", :id => "1", :user_id => "2" }.
+This route would respond to paths such as +/photos/show/1/with_user/2+. In this case, +params+ would be { :controller => "photos", :action => "show", :id => "1", :user_id => "2" }.
 
 h4. The Query String
 
@@ -406,7 +406,7 @@ The +params+ will also include any parameters from the query string. For example
 match ':controller/:action/:id'
 
 
-An incoming URL of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params+ will be { :controller => "photos", :action => "show", :id => "1", :user_id => "2" }.
+An incoming path of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params+ will be { :controller => "photos", :action => "show", :id => "1", :user_id => "2" }.
 
 h4. Defining Defaults
 
@@ -416,7 +416,7 @@ You do not need to explicitly use the +:controller+ and +:action+ symbols within
 match 'photos/:id' => 'photos#show'
 
 
-With this route, Rails will match an incoming URL of +/photos/12+ to the +show+ action of  +PhotosController+.
+With this route, Rails will match an incoming path of +/photos/12+ to the +show+ action of  +PhotosController+.
 
 You can also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that you do not specify as dynamic segments. For example:
 
@@ -444,7 +444,7 @@ You can use the +:constraints+ option to enforce a format for a dynamic segment:
 match 'photos/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ }
 
 
-This route would match URLs such as +/photos/A12345+. You can more succinctly express the same route this way:
+This route would match paths such as +/photos/A12345+. You can more succinctly express the same route this way:
 
 
 match 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
@@ -573,9 +573,9 @@ The +:controller+ option lets you explicitly specify a controller to use for the
 resources :photos, :controller => "images"
 
 
-will recognize incoming URLs beginning with +/photo+ but route to the +Images+ controller:
+will recognize incoming paths beginning with +/photo+ but route to the +Images+ controller:
 
-|_. Verb |_.URL          |_.action |
+|_. Verb |_.Path         |_.action |
 |GET     |/photos        |index    |
 |GET     |/photos/new    |new      |
 |POST    |/photos        |create   |
@@ -584,7 +584,7 @@ will recognize incoming URLs beginning with +/photo+ but route to the +Images+ c
 |PUT     |/photos/1      |update   |
 |DELETE  |/photos/1      |destroy  |
 
-NOTE: Use +photos_path+, +new_photos_path+, etc. to generate URLs for this resource.
+NOTE: Use +photos_path+, +new_photos_path+, etc. to generate paths for this resource.
 
 h4. Specifying Constraints
 
@@ -615,9 +615,9 @@ The +:as+ option lets you override the normal naming for the named route helpers
 resources :photos, :as => "images"
 
 
-will recognize incoming URLs beginning with +/photos+ and route the requests to +PhotosController+:
+will recognize incoming paths beginning with +/photos+ and route the requests to +PhotosController+:
 
-|_.HTTP verb|_.URL           |_.action |_.named helper   |
+|_.HTTP verb|_.Path          |_.action |_.named helper   |
 |GET        |/photos         |index    | images_path     |
 |GET        |/photos/new     |new      | new_image_path  |
 |POST       |/photos         |create   | images_path     |
@@ -628,20 +628,20 @@ will recognize incoming URLs beginning with +/photos+ and route the requests to
 
 h4. Overriding the +new+ and +edit+ Segments
 
-The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in URLs:
+The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in paths:
 
 
 resources :photos, :path_names => { :new => 'make', :edit => 'change' }
 
 
-This would cause the routing to recognize URLs such as
+This would cause the routing to recognize paths such as
 
 
 /photos/make
 /photos/1/change
 
 
-NOTE: The actual action names aren't changed by this option. The two URLs shown would still route to the new and edit actions.
+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:
 
@@ -709,7 +709,7 @@ end
 
 Rails now creates routes to the +CategoriesControlleR+.
 
-|_.HTTP verb|_.URL                      |_.action |
+|_.HTTP verb|_.Path                     |_.action |
 |GET        |/kategorien                |index    |
 |GET        |/kategorien/neu            |new      |
 |POST       |/kategorien                |create   |
-- 
cgit v1.2.3


From b456877cfb7e0cb0bab9ffd5674abd23caba0ab4 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Thu, 22 Jul 2010 01:27:02 +0200
Subject: camelize and underscore are sort of inverse of each other, but not in
 a mathematical sense [#5174 state:resolved]

---
 railties/guides/source/active_support_core_extensions.textile | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index a895dbded2..e53c7715bb 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -1491,13 +1491,15 @@ end
 
 That may be handy to compute method names in a language that follows that convention, for example JavaScript.
 
+INFO: As a rule of thumb you can think of +camelize+ as the inverse of +underscore+, though there are cases where that does not hold: "SSLError".underscore.camelize gives back "SslError".
+
 +camelize+ is aliased to +camelcase+.
 
 NOTE: Defined in +active_support/core_ext/string/inflections.rb+.
 
 h5. +underscore+
 
-The method +underscore+ is the inverse of +camelize+, explained above:
+The method +underscore+ goes the other way around, from camel case to paths:
 
 
 "Product".underscore   # => "product"
@@ -1530,6 +1532,8 @@ def load_missing_constant(from_mod, const_name)
 end
 
 
+INFO: As a rule of thumb you can think of +underscore+ as the inverse of +camelize+, though there are cases where that does not hold. For example, "SSLError".underscore.camelize gives back "SslError".
+
 NOTE: Defined in +active_support/core_ext/string/inflections.rb+.
 
 h5. +titleize+
-- 
cgit v1.2.3


From 4ed9bd6431d1733d83de0b461280f77f78a9c4c6 Mon Sep 17 00:00:00 2001
From: Bobby Wilson 
Date: Wed, 21 Jul 2010 20:34:00 -0700
Subject: Changed code style that was incorrectly rendering block style instead
 of inline.

---
 railties/guides/source/routing.textile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 7e6d6b5b34..7b665d81e7 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 with 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
-- 
cgit v1.2.3


From 2d2bde9f543a1508e9b149751a4566780033e3f0 Mon Sep 17 00:00:00 2001
From: Ivan Torres 
Date: Thu, 22 Jul 2010 10:10:38 -0500
Subject: [PATCH] Update guides after Jeremy Kemper's changes on
 fieldWithErrors to field_with_errors [#157 state:resolved]

---
 railties/guides/source/active_record_validations_callbacks.textile | 4 ++--
 railties/guides/source/configuring.textile                         | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile
index c7ba130a90..37a65d211e 100644
--- a/railties/guides/source/active_record_validations_callbacks.textile
+++ b/railties/guides/source/active_record_validations_callbacks.textile
@@ -799,7 +799,7 @@ h4. Customizing the Error Messages CSS
 
 The selectors to customize the style of error messages are:
 
-* +.fieldWithErrors+ - Style for the form fields and labels with errors.
+* +.field_with_errors+ - Style for the form fields and labels with errors.
 * +#errorExplanation+ - Style for the +div+ element with the error messages.
 * +#errorExplanation h2+ - Style for the header of the +div+ element.
 * +#errorExplanation p+ - Style for the paragraph that holds the message that appears right below the header of the +div+ element.
@@ -811,7 +811,7 @@ The name of the class and the id can be changed with the +:class+ and +:id+ opti
 
 h4. Customizing the Error Messages HTML
 
-By default, form fields with errors are displayed enclosed by a +div+ element with the +fieldWithErrors+ CSS class. However, it's possible to override that. 
+By default, form fields with errors are displayed enclosed by a +div+ element with the +field_with_errors+ CSS class. However, it's possible to override that. 
 
 The way form fields with errors are treated is defined by +ActionView::Base.field_error_proc+. This is a +Proc+ that receives two parameters:
 
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 86655746e4..2ab28596d8 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -181,7 +181,7 @@ There are only a few configuration options for Action View, starting with four o
 
 * +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| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }
+* +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| "<div class=\"field_with_errors\">#{html_tag}</div>" }
 
 * +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 b378b19430a4d2ee54cbe7f1935fbd4f8b3b331b Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Thu, 22 Jul 2010 18:56:00 -0300
Subject: Makes Rakefile activate rdoc >= 2.5.9

Signed-off-by: Xavier Noria 
---
 railties/Rakefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/Rakefile b/railties/Rakefile
index 19c860f257..1600fc8871 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -1,4 +1,4 @@
-gem 'rdoc', '= 2.2'
+gem 'rdoc', '>= 2.5.9'
 require 'rdoc'
 require 'rake'
 require 'rake/testtask'
-- 
cgit v1.2.3


From b50635a59f7d2fab2cdba348c4169536fbbb77f4 Mon Sep 17 00:00:00 2001
From: Aaron Patterson 
Date: Fri, 23 Jul 2010 21:11:29 +0200
Subject: update Rakefiles for RDoc 2.5

---
 railties/Rakefile | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

(limited to 'railties')

diff --git a/railties/Rakefile b/railties/Rakefile
index 1600fc8871..8e78d2ff4a 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -2,7 +2,7 @@ gem 'rdoc', '>= 2.5.9'
 require 'rdoc'
 require 'rake'
 require 'rake/testtask'
-require 'rake/rdoctask'
+require 'rdoc/task'
 require 'rake/gempackagetask'
 
 require 'date'
@@ -35,6 +35,13 @@ end
 
 # Update spinoffs -------------------------------------------------------------------
 
+desc "Updates application README to the latest version Railties README"
+task :update_readme do
+  readme = "lib/rails/generators/rails/app/templates/README"
+  rm readme
+  cp "./README.rdoc", readme
+end
+
 desc 'Generate guides (for authors), use ONLY=foo to process just "foo.textile"'
 task :generate_guides do
   ENV["WARN_BROKEN_LINKS"] = "1" # authors can't disable this
@@ -53,12 +60,12 @@ end
 
 # Generate documentation ------------------------------------------------------------------
 
-Rake::RDocTask.new { |rdoc|
+RDoc::Task.new { |rdoc|
   rdoc.rdoc_dir = 'doc'
   rdoc.title    = "Railties -- Gluing the Engine to the Rails"
-  rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=object'
+  rdoc.options << '-f' << 'horo'
+  rdoc.options << '--main' << 'README.rdoc'
   rdoc.options << '--charset' << 'utf-8'
-  rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
   rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG')
   rdoc.rdoc_files.include('lib/**/*.rb')
   rdoc.rdoc_files.exclude('lib/rails/generators/**/templates/*')
-- 
cgit v1.2.3


From 9a9fb12623c5d7eb7b3165c56985fbedfcebf886 Mon Sep 17 00:00:00 2001
From: Jack Dempsey 
Date: Sat, 24 Jul 2010 15:28:45 -0400
Subject: Fix small middlewares typo

---
 railties/lib/rails/railtie.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 1d6a2de87d..f0d9d95fc4 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -70,7 +70,7 @@ module Rails
   #
   #   class MyRailtie < Rails::Railtie
   #     initializer "my_railtie.configure_rails_initialization" do |app|
-  #       app.middlewares.use MyRailtie::Middleware
+  #       app.middleware.use MyRailtie::Middleware
   #     end
   #   end
   #
-- 
cgit v1.2.3


From 92669b8320a45e3f0497bfb83c0c8e55d515be0c Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Sun, 25 Jul 2010 22:08:34 +0200
Subject: application generation: --skip-testunit and --skip-activerecord
 renamed to --skip-test-unit and --skip-active-record respectively

Reason is their proper spellings are "Test::Unit" and "Active Record".
Option names and descriptions have been revised, as well as some method
names and minor details here and there.
---
 railties/CHANGELOG                                     |  3 +++
 .../lib/rails/generators/rails/app/app_generator.rb    | 18 +++++++++---------
 .../rails/app/templates/config/application.rb          |  2 +-
 .../rails/app/templates/test/test_helper.rb.tt         |  2 +-
 railties/lib/rails/generators/test_case.rb             |  6 +++---
 railties/test/application/middleware_test.rb           |  2 +-
 railties/test/generators/app_generator_test.rb         | 12 ++++++------
 railties/test/generators_test.rb                       |  2 +-
 8 files changed, 25 insertions(+), 22 deletions(-)

(limited to 'railties')

diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 6a8db7c4a6..3cd9647799 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,8 @@
 *Rails 3.0.0 [Release Candidate] (unreleased)*
 
+* Application generation: --skip-testunit and --skip-activerecord become --skip-test-unit
+  and --skip-active-record respectively. [fxn]
+
 * Added console to Rails::Railtie as a hook called just after console starts. [José Valim]
 
 * Rails no longer autoload code in lib for application. You need to explicitly require it. [José Valim]
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index c99aa3c0cd..1324cc1f67 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -168,7 +168,7 @@ module Rails
                                         :desc => "Path to an application builder (can be a filesystem path or URL)"
 
       class_option :template,           :type => :string, :aliases => "-m",
-                                        :desc => "Path to an application template (can be a filesystem path or URL)."
+                                        :desc => "Path to an application template (can be a filesystem path or URL)"
 
       class_option :dev,                :type => :boolean, :default => false,
                                         :desc => "Setup the application with Gemfile pointing to your Rails checkout"
@@ -179,11 +179,11 @@ module Rails
       class_option :skip_gemfile,       :type => :boolean, :default => false,
                                         :desc => "Don't create a Gemfile"
 
-      class_option :skip_activerecord,  :type => :boolean, :aliases => "-O", :default => false,
-                                        :desc => "Skip ActiveRecord files"
+      class_option :skip_active_record, :type => :boolean, :aliases => "-O", :default => false,
+                                        :desc => "Skip Active Record files"
 
-      class_option :skip_testunit,      :type => :boolean, :aliases => "-T", :default => false,
-                                        :desc => "Skip TestUnit files"
+      class_option :skip_test_unit,     :type => :boolean, :aliases => "-T", :default => false,
+                                        :desc => "Skip Test::Unit files"
 
       class_option :skip_prototype,     :type => :boolean, :aliases => "-J", :default => false,
                                         :desc => "Skip Prototype files"
@@ -205,7 +205,7 @@ module Rails
 
         super
 
-        if !options[:skip_activerecord] && !DATABASES.include?(options[:database])
+        if !options[:skip_active_record] && !DATABASES.include?(options[:database])
           raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
         end
       end
@@ -239,8 +239,8 @@ module Rails
         template "config/boot.rb"
       end
 
-      def create_activerecord_files
-        return if options[:skip_activerecord]
+      def create_active_record_files
+        return if options[:skip_active_record]
         build(:database_yml)
       end
 
@@ -281,7 +281,7 @@ module Rails
       end
 
       def create_test_files
-        build(:test) unless options[:skip_testunit]
+        build(:test) unless options[:skip_test_unit]
       end
 
       def create_tmp_files
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 190ab04cf5..7d63e99e05 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -1,6 +1,6 @@
 require File.expand_path('../boot', __FILE__)
 
-<% unless options[:skip_activerecord] -%>
+<% unless options[:skip_active_record] -%>
 require 'rails/all'
 <% else -%>
 # Pick the frameworks you want:
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
index 86564031f5..a8f7aeac7d 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
@@ -3,7 +3,7 @@ require File.expand_path('../../config/environment', __FILE__)
 require 'rails/test_help'
 
 class ActiveSupport::TestCase
-<% unless options[:skip_activerecord] -%>
+<% unless options[:skip_active_record] -%>
   # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
   #
   # Note: You'll currently still have to declare fixtures explicitly in integration tests
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
index 0dfb5cd1c9..36bc542ffe 100644
--- a/railties/lib/rails/generators/test_case.rb
+++ b/railties/lib/rails/generators/test_case.rb
@@ -51,7 +51,7 @@ module Rails
       # Sets default arguments on generator invocation. This can be overwritten when
       # invoking it.
       #
-      #   arguments %w(app_name --skip-activerecord)
+      #   arguments %w(app_name --skip-active-record)
       #
       def self.arguments(array)
         self.default_arguments = array
@@ -214,8 +214,8 @@ module Rails
       #     destination File.expand_path("../tmp", File.dirname(__FILE__))
       #     teardown :cleanup_destination_root
       #
-      #     test "database.yml is not created when skipping activerecord" do
-      #       run_generator %w(myapp --skip-activerecord)
+      #     test "database.yml is not created when skipping Active Record" do
+      #       run_generator %w(myapp --skip-active-record)
       #       assert_no_file "config/database.yml"
       #     end
       #   end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index ed06b4c767..db9f06be5c 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -38,7 +38,7 @@ module ApplicationTests
       ], middleware
     end
 
-    test "removing activerecord omits its middleware" do
+    test "removing Active Record omits its middleware" do
       use_frameworks []
       boot!
       assert !middleware.include?("ActiveRecord::ConnectionAdapters::ConnectionManagement")
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 7018816af0..aca30e92b6 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -65,7 +65,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
   end
 
   def test_options_before_application_name_raises_an_error
-    content = capture(:stderr){ run_generator(["--skip-activerecord", destination_root]) }
+    content = capture(:stderr){ run_generator(["--skip-active-record", destination_root]) }
     assert_equal "Options should be given after the application name. For details run: rails --help\n", content
   end
 
@@ -117,13 +117,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
     assert_file "Gemfile", /^gem\s+["']mysql["']$/
   end
 
-  def test_config_database_is_not_added_if_skip_activerecord_is_given
-    run_generator [destination_root, "--skip-activerecord"]
+  def test_config_database_is_not_added_if_skip_active_record_is_given
+    run_generator [destination_root, "--skip-active-record"]
     assert_no_file "config/database.yml"
   end
 
-  def test_activerecord_is_removed_from_frameworks_if_skip_activerecord_is_given
-    run_generator [destination_root, "--skip-activerecord"]
+  def test_active_record_is_removed_from_frameworks_if_skip_active_record_is_given
+    run_generator [destination_root, "--skip-active-record"]
     assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
   end
 
@@ -137,7 +137,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
   end
 
   def test_prototype_and_test_unit_are_skipped_if_required
-    run_generator [destination_root, "--skip-prototype", "--skip-testunit"]
+    run_generator [destination_root, "--skip-prototype", "--skip-test-unit"]
     assert_file "config/application.rb", /^\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(\)/
     assert_file "public/javascripts/application.js"
     assert_no_file "public/javascripts/prototype.js"
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 74a09d4bde..f93800a5ae 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -108,7 +108,7 @@ class GeneratorsTest < Rails::Generators::TestCase
     assert_match /^  fixjour$/, output
   end
 
-  def test_rails_generators_does_not_show_activerecord_hooks
+  def test_rails_generators_does_not_show_active_record_hooks
     output = capture(:stdout){ Rails::Generators.help }
     assert_match /ActiveRecord:/, output
     assert_match /^  active_record:fixjour$/, output
-- 
cgit v1.2.3


From b0b9bf320409b66c6c6b680371aca590297cd4cc Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Sun, 25 Jul 2010 18:12:20 -0300
Subject: Object#returning removed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: José Valim 
---
 railties/lib/rails/info.rb | 1 -
 1 file changed, 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb
index e9c3ebe685..96f2d9296e 100644
--- a/railties/lib/rails/info.rb
+++ b/railties/lib/rails/info.rb
@@ -1,4 +1,3 @@
-require "active_support/core_ext/object/misc"
 require "cgi"
 require "active_support/core_ext/cgi"
 
-- 
cgit v1.2.3


From 06853cb0a9ff77c67ec30e70b4007c4f9d11838b Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Sun, 25 Jul 2010 23:42:05 +0200
Subject: AS guide: Object#returning is gone

---
 .../guides/source/active_support_core_extensions.textile  | 15 ---------------
 1 file changed, 15 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index e53c7715bb..d14a531abe 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -157,21 +157,6 @@ WARNING. Using +duplicable?+ is discouraged because it depends on a hard-coded l
 
 NOTE: Defined in +active_support/core_ext/object/duplicable.rb+.
 
-h4. +returning+
-
-The method +returning+ yields its argument to a block and returns it. You typically use it with a mutable object that gets modified in the block:
-
-
-def html_options_for_form(url_for_options, options, *parameters_for_url)
-  returning options.stringify_keys do |html_options|
-    html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
-    html_options["action"]  = url_for(url_for_options, *parameters_for_url)
-  end
-end
-
-
-NOTE: Defined in +active_support/core_ext/object/returning.rb+.
-
 h4. +try+
 
 Sometimes you want to call a method provided the receiver object is not +nil+, which is something you usually check first.
-- 
cgit v1.2.3


From 9789d221373123d5d9c26173985080881fcb536a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= 
Date: Mon, 26 Jul 2010 11:41:52 +0200
Subject: Update to latest thor.

---
 railties/railties.gemspec                      | 2 +-
 railties/test/generators/app_generator_test.rb | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

(limited to 'railties')

diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index 38dcb17ff1..d76b74190b 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
   s.has_rdoc = false
 
   s.add_dependency('rake',          '>= 0.8.3')
-  s.add_dependency('thor',          '~> 0.13.7')
+  s.add_dependency('thor',          '~> 0.14.0')
   s.add_dependency('activesupport', version)
   s.add_dependency('actionpack',    version)
 end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index aca30e92b6..67a878e926 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -172,7 +172,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
     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 }
+    assert_match /It works!/, silence(:stdout){ generator.invoke_all }
   end
 
   def test_usage_read_from_file
@@ -196,14 +196,14 @@ class AppGeneratorTest < Rails::Generators::TestCase
 
   def test_dev_option
     generator([destination_root], :dev => true).expects(:run).with("#{@bundle_command} install")
-    silence(:stdout){ generator.invoke }
+    silence(:stdout){ generator.invoke_all }
     rails_path = File.expand_path('../../..', Rails.root)
     assert_file 'Gemfile', /^gem\s+["']rails["'],\s+:path\s+=>\s+["']#{Regexp.escape(rails_path)}["']$/
   end
 
   def test_edge_option
     generator([destination_root], :edge => true).expects(:run).with("#{@bundle_command} install")
-    silence(:stdout){ generator.invoke }
+    silence(:stdout){ generator.invoke_all }
     assert_file 'Gemfile', /^gem\s+["']rails["'],\s+:git\s+=>\s+["']#{Regexp.escape("git://github.com/rails/rails.git")}["']$/
   end
 
@@ -267,7 +267,7 @@ class CustomAppGeneratorTest < Rails::Generators::TestCase
     template.instance_eval "def read; self; end" # Make the string respond to read
 
     generator([destination_root], :builder => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
-    capture(:stdout) { generator.invoke }
+    capture(:stdout) { generator.invoke_all }
 
     DEFAULT_APP_FILES.each{ |path| assert_no_file path }
   end
-- 
cgit v1.2.3


From 514624e53c7b3008e3c492ef01f4d85188cdbbd7 Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Mon, 26 Jul 2010 12:16:36 -0300
Subject: Fixes usage message when running rails without --dev on a cloned
 rails repo

---
 railties/lib/rails/generators/base.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 67a9a6030d..fdfceb14ce 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -3,7 +3,7 @@ begin
 rescue LoadError
   puts "Thor is not available.\nIf you ran this command from a git checkout " \
        "of Rails, please make sure thor is installed,\nand run this command " \
-       "as `ruby /path/to/rails new myapp --dev`"
+       "as `ruby #{$0} #{ARGV.join(" ")} --dev`"
   exit
 end
 
-- 
cgit v1.2.3


From fd589db8633ef3eda398365f2a6d551edfed547e Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Sun, 25 Jul 2010 20:59:18 -0300
Subject: Changed returning with tap on guides

---
 railties/guides/source/plugins.textile  | 2 +-
 railties/guides/source/security.textile | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index e853ba79e9..82f2276153 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -1274,7 +1274,7 @@ class YaffleMigrationGenerator < Rails::Generator::NamedBase
     end
 
     def yaffle_local_assigns
-      returning(assigns = {}) do
+      {}.tap do |assigns|
         assigns[:migration_action] = "add"
         assigns[:class_name] = "add_yaffle_fields_to_#{custom_file_name}"
         assigns[:table_name] = custom_file_name
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index 60108d5ab3..8ce0001080 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -286,7 +286,7 @@ When filtering user input file names, _(highlight)don't try to remove malicious
 
 
 def sanitize_filename(filename)
-  returning filename.strip do |name|
+  filename.strip.tap do |name|
     # NOTE: File.basename doesn't work right with Windows paths on Unix
     # get only the filename, not the whole path
     name.sub! /\A.*(\\|\/)/, ''
-- 
cgit v1.2.3


From 856fc4bbc379b330d11702adbc2b26850dca6206 Mon Sep 17 00:00:00 2001
From: David Heinemeier Hansson 
Date: Mon, 26 Jul 2010 12:52:34 -0500
Subject: Prep for RC

---
 railties/CHANGELOG            | 2 +-
 railties/lib/rails/version.rb | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'railties')

diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 3cd9647799..fc1ec340c7 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,4 +1,4 @@
-*Rails 3.0.0 [Release Candidate] (unreleased)*
+*Rails 3.0.0 [release candidate] (July 26th, 2010)*
 
 * Application generation: --skip-testunit and --skip-activerecord become --skip-test-unit
   and --skip-active-record respectively. [fxn]
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index 34d96fd0f3..c5d1d02bc1 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -3,7 +3,7 @@ module Rails
     MAJOR = 3
     MINOR = 0
     TINY  = 0
-    BUILD = "beta4"
+    BUILD = "rc"
 
     STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
   end
-- 
cgit v1.2.3


From d283ca34a95fddc5d7e982432e0b5ea61269c133 Mon Sep 17 00:00:00 2001
From: David Heinemeier Hansson 
Date: Mon, 26 Jul 2010 16:35:19 -0500
Subject: Fix deprecation message on info screen

---
 railties/lib/rails/info.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb
index 96f2d9296e..6cbd1f21c0 100644
--- a/railties/lib/rails/info.rb
+++ b/railties/lib/rails/info.rb
@@ -88,7 +88,7 @@ module Rails
     end
 
     property 'Middleware' do
-      Rails.configuration.middleware.active.map(&:inspect)
+      Rails.configuration.middleware.map(&:inspect)
     end
 
     # The application's location on the filesystem.
-- 
cgit v1.2.3


From b6f99325613baf0534c10677da9c82ac27b628c0 Mon Sep 17 00:00:00 2001
From: Chad Fowler 
Date: Mon, 26 Jul 2010 15:35:54 -0600
Subject: fix bad logic in determining if in rails app subdir

Signed-off-by: David Heinemeier Hansson 
---
 railties/lib/rails/script_rails_loader.rb | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/script_rails_loader.rb b/railties/lib/rails/script_rails_loader.rb
index 8fbd3bf492..91672e5d81 100644
--- a/railties/lib/rails/script_rails_loader.rb
+++ b/railties/lib/rails/script_rails_loader.rb
@@ -7,6 +7,7 @@ module Rails
 
     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
@@ -18,7 +19,7 @@ module Rails
     end
     
     def self.in_rails_application?
-      File.exists?(SCRIPT_RAILS) || in_rails_application_subdirectory?
+      File.exists?(SCRIPT_RAILS)
     end
     
     def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd))
-- 
cgit v1.2.3


From 2e3a86af8cec2d16e093b51a2f846ec9efeff96b Mon Sep 17 00:00:00 2001
From: Arkadiusz Holko 
Date: Mon, 26 Jul 2010 15:00:23 -0700
Subject: Fixed field_error_proc default example on guides

---
 railties/guides/source/configuring.textile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 2ab28596d8..e2487fa36e 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -181,7 +181,7 @@ There are only a few configuration options for Action View, starting with four o
 
 * +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| "<div class=\"field_with_errors\">#{html_tag}</div>" }
+* +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| "<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 10815e6e25d5feb7d84d8bfb8b6203802268a590 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Tue, 27 Jul 2010 00:06:31 +0200
Subject: prefer %Q() over escaped double quotes

---
 railties/guides/source/configuring.textile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index e2487fa36e..9e0c7cd060 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -181,7 +181,7 @@ There are only a few configuration options for Action View, starting with four o
 
 * +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| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
+* +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 5d3e8ee2bdd2225fe6e610c7e465bb69234871ed Mon Sep 17 00:00:00 2001
From: rohit 
Date: Tue, 27 Jul 2010 14:57:46 +0530
Subject: Fix app generator so that it uses the right app_name during
 rails:update rake task. [#5207 state:committed]
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: José Valim 
---
 railties/lib/rails/tasks/framework.rake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index e7bd0c38dc..443dacd739 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -54,7 +54,7 @@ namespace :rails do
 
   namespace :update do
     def invoke_from_app_generator(method)
-      app_generator.invoke(method)
+      app_generator.send(method)
     end
 
     def app_generator
-- 
cgit v1.2.3


From fd484601351440d56251a5665a3773018350e35e Mon Sep 17 00:00:00 2001
From: paulccarey 
Date: Tue, 27 Jul 2010 12:09:24 +0100
Subject: amended Contributing to Rails guide to advise to use bundler v1.0 to
 avoid error messages with bundler

---
 .../guides/source/contributing_to_rails.textile    | 25 ++++++++++++----------
 1 file changed, 14 insertions(+), 11 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile
index f0e9a4b5ec..094a4ef1a9 100644
--- a/railties/guides/source/contributing_to_rails.textile
+++ b/railties/guides/source/contributing_to_rails.textile
@@ -48,7 +48,7 @@ h4. Install git
 
 Rails uses git for source code control. You won’t be able to do anything without the Rails source code, and this is a prerequisite. The "git homepage":http://git-scm.com/ has installation instructions. If you’re on OS X, use the "Git for OS X":http://code.google.com/p/git-osx-installer/ installer. If you're unfamiliar with git, there are a variety of resources on the net that will help you learn more:
 
-* "Everyday Git":http://www.kernel.org/pub/software/scm/git/docs/everyday.html will teach you just enough about git to get by. 
+* "Everyday Git":http://www.kernel.org/pub/software/scm/git/docs/everyday.html will teach you just enough about git to get by.
 * The "PeepCode screencast":https://peepcode.com/products/git on git ($9) is easier to follow.
 * "GitHub":http://github.com/guides/home offers links to a variety of git resources.
 * "Pro Git":http://progit.org/book/ is an entire book about git with a Creative Commons license.
@@ -58,7 +58,7 @@ h4. Get the Rails Source Code
 Don’t fork the main Rails repository. Instead, you want to clone it to your own computer. Navigate to the folder where you want the source code (it will create its own /rails subdirectory) and run:
 
 
-git clone git://github.com/rails/rails.git  
+git clone git://github.com/rails/rails.git
 cd rails
 
 
@@ -66,8 +66,10 @@ h4. Set up and Run the Tests
 
 All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. First, you need to install all Rails dependencies with bundler:
 
+NOTE: Ensure you install bundler v1.0
+
 
-gem install bundler
+gem install -v=1.0.0.rc.1 bundler
 bundle install --without db
 
 
@@ -90,7 +92,7 @@ By default, when you run Active Record tests, it will execute the test suite thr
 
 
 cd activerecord
-rake test_sqlite3 
+rake test_sqlite3
 rake test_sqlite3 TEST=test/cases/validations_test.rb
 
 
@@ -258,15 +260,15 @@ h4. Update Rails
 Update your copy of Rails. It’s pretty likely that other changes to core Rails have happened while you were working. Go get them:
 
 
-git checkout master  
-git pull  
+git checkout master
+git pull
 
 
 Now reapply your patch on top of the latest changes:
 
 
-git checkout my_new_branch  
-git rebase master  
+git checkout my_new_branch
+git rebase master
 
 
 No conflicts? Tests still pass? Change still seems reasonable to you? Then move on.
@@ -276,8 +278,8 @@ h4. Create a Patch
 Now you can create a patch file to share with other developers (and with the Rails core team). Still in your branch, run
 
 
-git commit -a  
-git format-patch master --stdout > my_new_patch.diff  
+git commit -a
+git format-patch master --stdout > my_new_patch.diff
 
 
 Sanity check the results of this operation: open the diff file in your text editor of choice and make sure that no unintended changes crept in.
@@ -302,4 +304,5 @@ h3. Changelog
 
 * April 6, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
 * August 1, 2009: Updates/amplifications by "Mike Gunderloy":credits.html#mgunderloy
-* March 2, 2009: Initial draft by "Mike Gunderloy":credits.html#mgunderloy
\ No newline at end of file
+* March 2, 2009: Initial draft by "Mike Gunderloy":credits.html#mgunderloy
+
-- 
cgit v1.2.3


From e17e08efef68e9865cfbd5c0155b3d9734339192 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Wed, 28 Jul 2010 03:49:04 +0200
Subject: adds test coverage for edge-cases of Array.wrap, and better
 documentation for how it differs from Kernel#Array

---
 railties/guides/source/active_support_core_extensions.textile | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index d14a531abe..fc59df9fe4 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -2229,7 +2229,14 @@ NOTE: Defined in +active_support/core_ext/array/conversions.rb+.
 
 h4. Wrapping
 
-The class method +Array.wrap+ behaves like the function +Array()+ except that it does not try to call +to_a+ on its argument. That changes the behavior for enumerables:
+The class method +Array.wrap+ behaves like the function +Array()+ except:
+
+* If the argument responds to +to_ary+ the method is invoked. Kernel#Array moves on to try +to_a+ if the returned value is +nil+, but Arraw.wrap returns such a +nil+ right away.
+* If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, Kernel#Array raises an exception, while Array.wrap does not, it just returns the value.
+* It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array.
+
+
+ that it does not try to call +to_a+ on its argument. That changes the behavior for enumerables:
 
 
 Array.wrap(:foo => :bar) # => [{:foo => :bar}]
-- 
cgit v1.2.3


From 123eb25fd12c5a5ec63e18082dcdda6318bc942e Mon Sep 17 00:00:00 2001
From: wycats 
Date: Tue, 27 Jul 2010 19:24:56 -0700
Subject: Add a header that tells Internet Explorer (all versions) to use the
 best available standards support. This ensures that IE doesn't go into quirks
 mode because it has been blacklisted by too many users pressing the
 incompatible button. It also tells IE to use the ChromeFrame renderer, if the
 user has installed the plugin.

This guarantees that the best available standards support will be used on the client.
---
 railties/lib/rails/application.rb          | 1 +
 railties/lib/rails/commands.rb             | 2 +-
 railties/lib/rails/engine/configuration.rb | 2 +-
 railties/test/application/routing_test.rb  | 1 +
 4 files changed, 4 insertions(+), 2 deletions(-)

(limited to 'railties')

diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 3f9bca0bd6..6622cfdd2f 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -205,6 +205,7 @@ module Rails
         middleware.use ::ActionDispatch::ParamsParser
         middleware.use ::Rack::MethodOverride
         middleware.use ::ActionDispatch::Head
+        middleware.use ::ActionDispatch::BestStandardsSupport if config.action_dispatch.best_standards_support
       end
     end
 
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index b9353ba336..60a93c9848 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -70,4 +70,4 @@ In addition to those, there are:
 
 All commands can be run with -h for more information.
   EOT
-end
\ No newline at end of file
+end
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 2f465670cf..521ed95447 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -50,4 +50,4 @@ module Rails
       end
     end
   end
-end
\ No newline at end of file
+end
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index f0268164d0..a10a39ef40 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -40,6 +40,7 @@ module ApplicationTests
 
       get '/foo'
       assert_equal 'foo', last_response.body
+      assert_equal "IE=Edge,chrome=1", last_response.headers["X-UA-Compatible"]
     end
 
     test "simple controller with helper" do
-- 
cgit v1.2.3


From eff074951ea245fac47edcc5c3414d4787db5c76 Mon Sep 17 00:00:00 2001
From: wycats 
Date: Tue, 27 Jul 2010 21:10:08 -0700
Subject: Fix middleware test

---
 railties/test/application/middleware_test.rb | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index db9f06be5c..1f127cee78 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -34,7 +34,8 @@ module ApplicationTests
         "ActionDispatch::Flash",
         "ActionDispatch::ParamsParser",
         "Rack::MethodOverride",
-        "ActionDispatch::Head"
+        "ActionDispatch::Head",
+        "ActionDispatch::BestStandardsSupport"
       ], middleware
     end
 
-- 
cgit v1.2.3


From c2341ee71ae0e5d3ec2873ad8e034d8a4e88ed54 Mon Sep 17 00:00:00 2001
From: Neeraj Singh 
Date: Wed, 28 Jul 2010 21:43:29 -0400
Subject: superclass_delegating_accessor has been deprecated.

I am working on removing all traces of superclass_delegating_accessor from rails code base.

https://rails.lighthouseapp.com/projects/8994/tickets/2914-superclass_delegating_accessor-needs-to-be-deprecated-nicely
---
 .../guides/source/active_support_core_extensions.textile  | 15 ---------------
 1 file changed, 15 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index d14a531abe..6ee7a4220b 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -1135,21 +1135,6 @@ Since values are copied when a subclass is defined, if the base class changes th
 
 NOTE: Defined in +active_support/core_ext/class/inheritable_attributes.rb+.
 
-There's a related macro called +superclass_delegating_accessor+, however, that does not copy the value when the base class is subclassed. Instead, it delegates reading to the superclass as long as the attribute is not set via its own writer. For example, +ActionMailer::Base+ defines +delivery_method+ this way:
-
-
-module ActionMailer
-  class Base
-    superclass_delegating_accessor :delivery_method
-    self.delivery_method = :smtp
-  end
-end
-
-
-If for whatever reason an application loads the definition of a mailer class and after that sets +ActionMailer::Base.delivery_method+, the mailer class will still see the new value. In addition, the mailer class is able to change the +delivery_method+ without affecting the value in the parent using its own inherited class attribute writer.
-
-NOTE: Defined in +active_support/core_ext/class/delegating_attributes.rb+.
-
 h4. Subclasses & Descendants
 
 h5. +subclasses+
-- 
cgit v1.2.3


From c178a26ec784f34e82f08e26064d9798edb09e72 Mon Sep 17 00:00:00 2001
From: paulccarey 
Date: Thu, 29 Jul 2010 15:33:33 +0100
Subject: amended rails upgrade plugin instructions to be rails 2.3.x
 compatible instead of former rails 3 format

---
 railties/guides/source/3_0_release_notes.textile | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile
index 7dcaf508c6..14da650e83 100644
--- a/railties/guides/source/3_0_release_notes.textile
+++ b/railties/guides/source/3_0_release_notes.textile
@@ -1,6 +1,6 @@
 h2. Ruby on Rails 3.0 Release Notes
 
-Rails 3.0 is ponies and rainbows! It's going to cook you dinner and fold your laundry. You're going to wonder how life was ever possible before it arrived. It's the Best Version of Rails We've Ever Done! 
+Rails 3.0 is ponies and rainbows! It's going to cook you dinner and fold your laundry. You're going to wonder how life was ever possible before it arrived. It's the Best Version of Rails We've Ever Done!
 
 But seriously now, it's really good stuff. There are all the good ideas brought over from when the Merb team joined the party and brought a focus on framework agnosticism, slimmer and faster internals, and a handful of tasty APIs. If you're coming to Rails 3.0 from Merb 1.x, you should recognize lots. If you're coming from Rails 2.x, you're going to love it too.
 
@@ -12,7 +12,7 @@ Even if you don't give a hoot about any of our internal cleanups, Rails 3.0 is g
 * Unobtrusive JavaScript helpers with drivers for Prototype, jQuery, and more coming (end of inline JS)
 * Explicit dependency management with Bundler
 
-On top of all that, we've tried our best to deprecate the old APIs with nice warnings. That means that you can move your existing application to Rails 3 without immediately rewriting all your old code to the latest best practices. 
+On top of all that, we've tried our best to deprecate the old APIs with nice warnings. That means that you can move your existing application to Rails 3 without immediately rewriting all your old code to the latest best practices.
 
 These release notes cover the major upgrades, but don't include every little bug fix and change. Rails 3.0 consists of almost 4,000 commits by more than 250 authors! If you want to see everything, check out the "list of commits":http://github.com/rails/rails/commits/master in the main Rails repository on GitHub.
 
@@ -66,7 +66,7 @@ To help with the upgrade process, a plugin named "Rails Upgrade":http://github.c
 Simply install the plugin, then run +rake rails:upgrade:check+ to check your app for pieces that need to be updated (with links to information on how to update them).  It also offers a task to generate a +Gemfile+ based on your current +config.gem+ calls and a task to generate a new routes file from your current one.  To get the plugin, simply run the following:
 
 
-rails plugin install git://github.com/rails/rails_upgrade.git
+ruby script/plugin install git://github.com/rails/rails_upgrade.git
 
 
 You can see an example of how that works at "Rails Upgrade is now an Official Plugin":http://omgbloglol.com/post/364624593/rails-upgrade-is-now-an-official-plugin
@@ -596,3 +596,4 @@ h3. Credits
 See the "full list of contributors to Rails":http://contributors.rubyonrails.org/ for the many people who spent many hours making Rails 3.  Kudos to all of them.
 
 Rails 3.0 Release Notes were compiled by "Mikel Lindsaar":http://lindsaar.net.
+
-- 
cgit v1.2.3


From a506d5a5241507afda5f57ca8953fbcbf4b91231 Mon Sep 17 00:00:00 2001
From: Neeraj Singh 
Date: Thu, 29 Jul 2010 12:52:15 -0400
Subject: updating documentation about lazy hooks in ActiveSupport

---
 railties/guides/source/initialization.textile | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index 305602e57d..28afdb3bd0 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -258,28 +258,23 @@ This file goes on to define some classes that will be automatically loaded using
 
 h4. Lazy Hooks
 
-At the top if the +ActiveSupport::Autoload+ module is the +def self.extended+ method:
-
-
-  def self.extended(base)
-     base.extend(LazyLoadHooks)
-   end
-
-
-This is called when we extend this module into one of our classes or modules, such is the case later on when we call +extend ActiveSupport::LazyLoadHooks+ not only in the +ActiveSupport+ module, but in all of the Railtie modules (+ActiveRecord+ and so on), as well as in a couple of places.
-
 +ActiveSupport::LazyLoadHooks+ is responsible for defining methods used for running hooks that are defined during the initialization process, such as the one defined inside the +active_record.initialize_timezone+ initializer:
 
 
   initializer "active_record.initialize_timezone" do
-    ActiveRecord.base_hook do
+    ActiveSupport.on_load(:active_record) do
       self.time_zone_aware_attributes = true
       self.default_timezone = :utc
     end
   end
 
 
-When the initializer is ran it defines a +base_hook+ for +ActiveRecord+ and will only run it when +run_base_hooks+ is called, which in the case of Active Record, is ran after the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated.
+When the initializer runs it invokes method +on_load+ for +ActiveRecord+ and the block passed to it would be called only when +run_load_hooks+ is called.
+When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked. The very last line of +activerecord/lib/active_record/base.rb+ is:
+
+
+ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
+
 
 h4. +require 'active_support'+ cont'd.
 
-- 
cgit v1.2.3


From 755af497555fde16db86f7e51f6462b0aca79b49 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Fri, 30 Jul 2010 02:30:04 +0200
Subject: edit pass to apply API guideline wrt the use of "# =>" in example
 code

---
 railties/lib/rails/generators/active_model.rb | 6 +++---
 railties/lib/rails/generators/test_case.rb    | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

(limited to 'railties')

diff --git a/railties/lib/rails/generators/active_model.rb b/railties/lib/rails/generators/active_model.rb
index fe6321af30..4b828340d2 100644
--- a/railties/lib/rails/generators/active_model.rb
+++ b/railties/lib/rails/generators/active_model.rb
@@ -9,16 +9,16 @@ module Rails
     # For example:
     #
     #   ActiveRecord::Generators::ActiveModel.find(Foo, "params[:id]")
-    #   #=> "Foo.find(params[:id])"
+    #   # => "Foo.find(params[:id])"
     #
     #   Datamapper::Generators::ActiveModel.find(Foo, "params[:id]")
-    #   #=> "Foo.get(params[:id])"
+    #   # => "Foo.get(params[:id])"
     #
     # On initialization, the ActiveModel accepts the instance name that will
     # receive the calls:
     #
     #   builder = ActiveRecord::Generators::ActiveModel.new "@foo"
-    #   builder.save #=> "@foo.save"
+    #   builder.save # => "@foo.save"
     #
     # The only exception in ActiveModel for ActiveRecord is the use of self.build
     # instead of self.new.
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
index 36bc542ffe..3376b422cb 100644
--- a/railties/lib/rails/generators/test_case.rb
+++ b/railties/lib/rails/generators/test_case.rb
@@ -68,7 +68,7 @@ module Rails
       # Captures the given stream and returns it:
       #
       #   stream = capture(:stdout){ puts "Cool" }
-      #   stream #=> "Cool\n"
+      #   stream # => "Cool\n"
       #
       def capture(stream)
         begin
-- 
cgit v1.2.3


From 55028a7326b833d51d0ab060828cf5f6983c93d8 Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Thu, 29 Jul 2010 23:44:31 -0300
Subject: Bump up nokogiri to 1.4.3.1

---
 railties/guides/source/initialization.textile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index 28afdb3bd0..19bc33c454 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -147,7 +147,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This
 * mail-2.2.5.gem
 * memcache-client-1.8.5.gem
 * mime-types-1.16.gem
-* nokogiri-1.4.2.gem
+* nokogiri-1.4.3.1.gem
 * polyglot-0.3.1.gem
 * rack-1.2.1.gem
 * rack-mount-0.6.9.gem
-- 
cgit v1.2.3


From e68e88e3ada6b500012f893156b03f548a2f87e3 Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Thu, 29 Jul 2010 23:46:25 -0300
Subject: This version is going to be outdated when new nokogiri versions are
 released

---
 railties/guides/source/initialization.textile             | 2 +-
 railties/lib/rails/generators/rails/app/templates/Gemfile | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index 19bc33c454..2f45aeaa8a 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -118,7 +118,7 @@ Now with Rails 3 we have a Gemfile which defines the basics our application need
 
   # Bundle the extra gems:
   # gem 'bj'
-  # gem 'nokogiri', '1.4.1'
+  # gem 'nokogiri'
   # gem 'sqlite3-ruby', :require => 'sqlite3'
   # gem 'aws-s3', :require => 'aws/s3'
 
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index a108968b97..1980684a94 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -28,7 +28,7 @@ gem '<%= gem_for_database %>'<% if require_for_database %>, :require => '<%= req
 
 # Bundle the extra gems:
 # gem 'bj'
-# gem 'nokogiri', '1.4.1'
+# gem 'nokogiri'
 # gem 'sqlite3-ruby', :require => 'sqlite3'
 # gem 'aws-s3', :require => 'aws/s3'
 
-- 
cgit v1.2.3


From 03fc2f03cbc9b3354267e599f5fb6a42d58cd194 Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Thu, 29 Jul 2010 23:48:47 -0300
Subject: Bump up bundler version to rc.2

---
 railties/guides/source/contributing_to_rails.textile | 2 +-
 railties/guides/source/initialization.textile        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile
index 094a4ef1a9..fb81bab98d 100644
--- a/railties/guides/source/contributing_to_rails.textile
+++ b/railties/guides/source/contributing_to_rails.textile
@@ -69,7 +69,7 @@ All of the Rails tests must pass with any code you submit, otherwise you have no
 NOTE: Ensure you install bundler v1.0
 
 
-gem install -v=1.0.0.rc.1 bundler
+gem install -v=1.0.0.rc.2 bundler
 bundle install --without db
 
 
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index 2f45aeaa8a..f80c00b280 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -141,7 +141,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This
 * activesupport-3.0.0.beta4.gem
 * arel-0.4.0.gem
 * builder-2.1.2.gem
-* bundler-1.0.0.beta.5.gem
+* bundler-1.0.0.rc.2.gem
 * erubis-2.6.6.gem
 * i18n-0.4.1.gem
 * mail-2.2.5.gem
-- 
cgit v1.2.3


From b823e50a54156f688dd0892e7a8671b730814bc6 Mon Sep 17 00:00:00 2001
From: Steve Agalloco 
Date: Thu, 29 Jul 2010 22:38:45 -0400
Subject: fix for rails app generator when using --pretend option

[#5245 state:committed]

Signed-off-by: Santiago Pastorino 
---
 railties/lib/rails/generators/rails/app/app_generator.rb | 2 +-
 railties/test/generators/app_generator_test.rb           | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 1324cc1f67..96c49a81bb 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -216,7 +216,7 @@ module Rails
 
         empty_directory '.'
         set_default_accessors!
-        FileUtils.cd(destination_root)
+        FileUtils.cd(destination_root) unless options[:pretend]
       end
 
       def create_root_files
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 67a878e926..1e0b3bf4c7 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -58,6 +58,12 @@ class AppGeneratorTest < Rails::Generators::TestCase
     DEFAULT_APP_FILES.each{ |path| assert_file path }
   end
 
+  def test_application_generate_pretend
+    run_generator ["testapp", "--pretend"]
+
+    DEFAULT_APP_FILES.each{ |path| assert_no_file path }
+  end
+
   def test_application_controller_and_layout_files
     run_generator
     assert_file "app/views/layouts/application.html.erb", /stylesheet_link_tag :all/
-- 
cgit v1.2.3


From 88286514202dcdeede735329e453b6a6c59800d1 Mon Sep 17 00:00:00 2001
From: Arkadiusz Holko 
Date: Fri, 30 Jul 2010 09:19:01 -0700
Subject: Command Line Guide: changes all 'rails app' commands to 'rails new
 app'

---
 railties/guides/source/command_line.textile | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile
index 983c04c3d9..fb625f7a44 100644
--- a/railties/guides/source/command_line.textile
+++ b/railties/guides/source/command_line.textile
@@ -23,18 +23,18 @@ There are a few commands that are absolutely critical to your everyday usage of
 * rake
 * rails generate
 * rails dbconsole
-* rails app_name
+* rails new app_name
 
 Let's create a simple Rails application to step through each of these commands in context.
 
-h4. +rails+
+h4. +rails new+
 
-The first thing we'll want to do is create a new Rails application by running the +rails+ command after installing Rails.
+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
 
 
-$ rails commandsapp
+$ rails new commandsapp
      create  
      create  README
      create  .gitignore
@@ -352,7 +352,7 @@ $ mkdir gitapp
 $ cd gitapp
 $ git init
 Initialized empty Git repository in .git/
-$ rails . --git --database=postgresql
+$ rails new . --git --database=postgresql
       exists
       create  app/controllers
       create  app/helpers
@@ -397,7 +397,7 @@ development:
 ...
 
 
-It also generated some lines in our database.yml configuration corresponding to our choice of PostgreSQL for database. The only catch with using the SCM options is that you have to make your application's directory first, then initialize your SCM, then you can run the +rails+ command to generate the basis of your app.
+It also generated some lines in our database.yml configuration corresponding to our choice of PostgreSQL for database. The only catch with using the SCM options is that you have to make your application's directory first, then initialize your SCM, then you can run the +rails new+ command to generate the basis of your app.
 
 h4. +server+ with Different Backends
 
-- 
cgit v1.2.3


From 05e0fa9cfb9afd5ce4e9299c81f824d16373fc03 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Fri, 30 Jul 2010 20:11:59 +0200
Subject: the main page of the API generated by the doc:rails task is README

---
 railties/lib/rails/tasks/documentation.rake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index 843d2b4e82..c1b1a41d48 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -55,7 +55,7 @@ namespace :doc do
     rdoc.template = "#{ENV['template']}.rb" if ENV['template']
     rdoc.title    = "Rails Framework Documentation"
     rdoc.options << '--line-numbers' << '--inline-source'
-    rdoc.rdoc_files.include('README.rdoc')
+    rdoc.rdoc_files.include('README')
 
     gem_path('actionmailer') do |actionmailer|
       %w(README.rdoc CHANGELOG MIT-LICENSE lib/action_mailer/base.rb).each do |file|
-- 
cgit v1.2.3


From 87a28e34aef3f68de8e344aadc47a516e28c0b09 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Fri, 30 Jul 2010 21:50:38 +0200
Subject: if there's a Gemfile and doc:guides can't load RedCloth, print
 instructions for bundler

---
 railties/guides/rails_guides.rb | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/rails_guides.rb b/railties/guides/rails_guides.rb
index e6f0b694a6..dfbb06cc76 100644
--- a/railties/guides/rails_guides.rb
+++ b/railties/guides/rails_guides.rb
@@ -1,6 +1,13 @@
 pwd = File.dirname(__FILE__)
 $:.unshift pwd
 
+# This is a predicate useful for the doc:guides task of applications.
+def bundler?
+  # Note that rake sets the cwd to the one that contains the Rakefile
+  # being executed.
+  File.exists?('Gemfile')
+end
+
 # Loading Action Pack requires rack and erubis.
 require 'rubygems'
 
@@ -20,7 +27,19 @@ begin
   gem 'RedCloth', '>= 4.1.1'
   require 'redcloth'
 rescue Gem::LoadError
-  $stderr.puts %(Generating Guides requires RedCloth 4.1.1+)
+  $stderr.puts('Generating guides requires RedCloth 4.1.1+.')
+  $stderr.puts(<= 4.1.1'
+
+to the Gemfile, run
+
+  bundle install
+
+and try again.
+ERROR
+
   exit 1
 end
 
-- 
cgit v1.2.3


From 96f8325116148848cf2a6ec7d8561df8955ffaa3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Johan=20Lundstr=C3=B6m?= 
Date: Sat, 31 Jul 2010 14:49:08 +0200
Subject: DHH forgot to remove JS, CSS in generated index.html [#5261
 state:resolved]

Signed-off-by: Santiago Pastorino 
---
 .../rails/app/templates/public/index.html          | 23 ----------------------
 1 file changed, 23 deletions(-)

(limited to 'railties')

diff --git a/railties/lib/rails/generators/rails/app/templates/public/index.html b/railties/lib/rails/generators/rails/app/templates/public/index.html
index c65593e8bc..75d5edd06d 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/index.html
+++ b/railties/lib/rails/generators/rails/app/templates/public/index.html
@@ -151,19 +151,6 @@
       }
 
 
-      #search {
-        margin: 0;
-        padding-top: 10px;
-        padding-bottom: 10px;
-        font-size: 11px;
-      }
-      #search input {
-        font-size: 11px;
-        margin: 2px;
-      }
-      #search-text {width: 170px}
-
-
       #sidebar ul {
         margin-left: 0;
         padding-left: 0;
@@ -194,16 +181,6 @@
         info.innerHTML = xhr.responseText;
         info.style.display = 'block'
       }
-
-      function prepend() {
-        search = document.getElementById('search-text');
-        text = search.value;
-        search.value = 'site:rubyonrails.org ' + text;
-      }
-
-      window.onload = function() {
-        document.getElementById('search-text').value = '';
-      }
     
   
   
-- 
cgit v1.2.3


From f78de6864998369002a5b1906dad151b6c787c24 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Sun, 1 Aug 2010 03:19:30 +0200
Subject: explains Array.wrap directly, rather by comparison with Kernel#Array
 which is too obscure, leaves the comparison to document the differences, and
 adds a comparison with the related idiom that uses the splat operator

---
 .../source/active_support_core_extensions.textile  | 29 +++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index fcf4ae29ba..136fcde82a 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -2214,14 +2214,27 @@ NOTE: Defined in +active_support/core_ext/array/conversions.rb+.
 
 h4. Wrapping
 
-The class method +Array.wrap+ behaves like the function +Array()+ except:
+The method +Array.wrap+ wraps its argument in an array unless it is already an array (or array-like).
+
+Specifically:
+
+* If the argument is +nil+ an empty list is returned.
+* Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
+* Otherwise, returns an array with the argument as its single element.
+
+
+Array.wrap(nil)       # => []
+Array.wrap([1, 2, 3]) # => [1, 2, 3]
+Array.wrap(0)         # => [0]
+
+
+This method is similar in purpose to Kernel#Array, but there are some differences:
 
 * If the argument responds to +to_ary+ the method is invoked. Kernel#Array moves on to try +to_a+ if the returned value is +nil+, but Arraw.wrap returns such a +nil+ right away.
 * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, Kernel#Array raises an exception, while Array.wrap does not, it just returns the value.
 * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array.
 
-
- that it does not try to call +to_a+ on its argument. That changes the behavior for enumerables:
+The last point is particularly worth comparing for some enumerables:
 
 
 Array.wrap(:foo => :bar) # => [{:foo => :bar}]
@@ -2231,6 +2244,16 @@ Array.wrap("foo\nbar")   # => ["foo\nbar"]
 Array("foo\nbar")        # => ["foo\n", "bar"], in Ruby 1.8
 
 
+There's also a related idiom that uses the splat operator:
+
+
+[*object]
+
+
+which returns +[nil]+ for +nil+, and calls to Array(object) otherwise
+
+Thus, in this case the behavior is different for +nil+, and the differences with Kernel#Array explained above apply to the rest of +object+s.
+
 NOTE: Defined in +active_support/core_ext/array/wrap.rb+.
 
 h4. Grouping
-- 
cgit v1.2.3


From 3b44b52fb0f09e437645d12d2feb6b6d37a804ee Mon Sep 17 00:00:00 2001
From: "Prashant P. Shah" 
Date: Fri, 30 Jul 2010 15:27:59 +0530
Subject: Corrected the rake test:units and test:functionals description [#5251
 state:committed]

Signed-off-by: Santiago Pastorino 
---
 railties/lib/rails/test_unit/testing.rake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index ecd513c2c8..38c14fcd6b 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -70,7 +70,7 @@ module Kernel
   end
 end
 
-desc 'Runs test:unit, test:functional, test:integration together (also available: test:benchmark, test:profile, test:plugins)'
+desc 'Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile, test:plugins)'
 task :test do
   errors = %w(test:units test:functionals test:integration).collect do |task|
     begin
-- 
cgit v1.2.3


From c544fcc8eb891e9066bae5eb1478c6823b1f6203 Mon Sep 17 00:00:00 2001
From: rohit 
Date: Mon, 2 Aug 2010 13:42:35 +0530
Subject: Failing test to check for route file corruption if legacy map
 parameter is used. [#5263 state:open]

---
 railties/test/generators/scaffold_generator_test.rb | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

(limited to 'railties')

diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index ea469cb3c8..f12445ae35 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -216,4 +216,19 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
     # Stylesheets (should not be removed)
     assert_file "public/stylesheets/scaffold.css"
   end
+
+  def test_scaffold_generator_on_revoke_does_not_mutilate_legacy_map_parameter
+    run_generator
+
+    # Add a |map| parameter to the routes block manually
+    route_path = File.expand_path("config/routes.rb", destination_root)
+    content = File.read(route_path).gsub(/\.routes\.draw do/) do |match|
+      "#{match} |map|"
+    end
+    File.open(route_path, "wb") { |file| file.write(content) }
+
+    run_generator ["product_line"], :behavior => :revoke
+
+    assert_file "config/routes.rb", /\.routes\.draw do\s*\|map\|\s*$/
+  end
 end
-- 
cgit v1.2.3


From e6331b1e97d608c46aaadd1814a1e370bdcdd40a Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Sat, 31 Jul 2010 22:33:22 -0300
Subject: Makes rails destroy scaffold don't duplicate routes.draw do |map|
 |map| when using the deprecated syntax

[#5263 state:committed]
---
 railties/lib/rails/generators/actions.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 2280cc1507..668ef48892 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -275,7 +275,7 @@ module Rails
       #
       def route(routing_code)
         log :route, routing_code
-        sentinel = /\.routes\.draw do(\s*\|map\|)?\s*$/
+        sentinel = /\.routes\.draw do(?:\s*\|map\|)?\s*$/
 
         in_root do
           inject_into_file 'config/routes.rb', "\n  #{routing_code}\n", { :after => sentinel, :verbose => false }
-- 
cgit v1.2.3


From cdad483dff4fef1b640dc3c750719c325b252f89 Mon Sep 17 00:00:00 2001
From: Fred Wu 
Date: Wed, 28 Jul 2010 22:55:57 +1000
Subject: Improved how AppGenerator generates the application name. It now
 detects the current app name whenever possible. This means that renaming the
 residing directory will not effect the app name generated by AppGenerator.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

[#5225 state:resolved]

Signed-off-by: José Valim 
---
 .../rails/generators/rails/app/app_generator.rb    |  6 +++++-
 railties/test/generators/app_generator_test.rb     | 24 ++++++++++++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 96c49a81bb..dd18588b39 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -356,8 +356,12 @@ module Rails
         @app_name ||= File.basename(destination_root)
       end
 
+      def defined_app_const_base
+        Rails.application.class.name.sub(/::Application$/, "") if Rails.application.instance_of?(Rails::Application)
+      end
+
       def app_const_base
-        @app_const_base ||= app_name.gsub(/\W/, '_').squeeze('_').camelize
+        @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, '_').squeeze('_').camelize
       end
 
       def app_const
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 1e0b3bf4c7..21725a380c 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -106,6 +106,30 @@ class AppGeneratorTest < Rails::Generators::TestCase
     assert_file "things-43/config/application.rb", /^module Things43$/
   end
 
+  def test_application_name_is_detected_if_it_exists_and_app_folder_renamed
+    app_root       = File.join(destination_root, "myapp")
+    app_moved_root = File.join(destination_root, "myapp_moved")
+
+    run_generator [app_root]
+
+    Rails.application.config.root = app_moved_root
+    Rails.application.class.stubs(:name).returns("Myapp")
+    Rails.application.stubs(:instance_of?).returns(Rails::Application)
+
+    FileUtils.mv(app_root, app_moved_root)
+
+    # forces the shell to automatically overwrite all files
+    Thor::Base.shell.send(:attr_accessor, :always_force)
+    shell = Thor::Base.shell.new
+    shell.send(:always_force=, true)
+
+    generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true },
+                                                               :destination_root => app_moved_root, :shell => shell
+    generator.send(:app_const)
+    silence(:stdout){ generator.send(:create_config_files) }
+    assert_file "myapp_moved/config/environment.rb", /Myapp::Application\.initialize!/
+  end
+
   def test_application_names_are_not_singularized
     run_generator [File.join(destination_root, "hats")]
     assert_file "hats/config/environment.rb", /Hats::Application\.initialize!/
-- 
cgit v1.2.3


From 558ee6e95ccd6c2098595f2edfa59e8aa9108167 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= 
Date: Mon, 2 Aug 2010 16:40:02 +0200
Subject: Handle edge cases in the previous patch.

---
 railties/lib/rails/generators/rails/app/app_generator.rb | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index dd18588b39..a90f109844 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -357,7 +357,8 @@ module Rails
       end
 
       def defined_app_const_base
-        Rails.application.class.name.sub(/::Application$/, "") if Rails.application.instance_of?(Rails::Application)
+        Rails.respond_to?(:application) && defined?(Rails::Application) &&
+          Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "")
       end
 
       def app_const_base
-- 
cgit v1.2.3


From 1466f312bad35809fa9fe343068d2fc8a814ffe6 Mon Sep 17 00:00:00 2001
From: Curtis Cablegram 
Date: Wed, 28 Jul 2010 19:34:04 -0500
Subject: Change log-tailer to properly track multi-byte characters.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

When end-of-line is represented within a file as "\r\n",
it is represented in memory as a single "\n".  This patch eliminates
the discrepancy between size on disk and size in memory.

Signed-off-by: José Valim 
---
 railties/lib/rails/rack/log_tailer.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/rack/log_tailer.rb b/railties/lib/rails/rack/log_tailer.rb
index 3fa45156c3..2ca6b5f8da 100644
--- a/railties/lib/rails/rack/log_tailer.rb
+++ b/railties/lib/rails/rack/log_tailer.rb
@@ -24,7 +24,7 @@ module Rails
         if mod > @last_checked
           contents = @file.read
           @last_checked = mod
-          @cursor += contents.size
+          @cursor = @file.tell
           $stdout.print contents
         end
       end
-- 
cgit v1.2.3


From e1142dfcae036bd8c6400962f9e07112b750b730 Mon Sep 17 00:00:00 2001
From: Curtis Cablegram 
Date: Wed, 28 Jul 2010 19:47:53 -0500
Subject: Refactor log-tailer to depend on File#eof? rather than File#mtime
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

[#5220 state:resolved]

Eliminate 1 instance variable and 1 local variable.

Signed-off-by: José Valim 
---
 railties/lib/rails/rack/log_tailer.rb | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

(limited to 'railties')

diff --git a/railties/lib/rails/rack/log_tailer.rb b/railties/lib/rails/rack/log_tailer.rb
index 2ca6b5f8da..011ac6cecc 100644
--- a/railties/lib/rails/rack/log_tailer.rb
+++ b/railties/lib/rails/rack/log_tailer.rb
@@ -6,7 +6,6 @@ module Rails
 
         path = Pathname.new(log || "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath
         @cursor = ::File.size(path)
-        @last_checked = Time.now.to_f
 
         @file = ::File.open(path, 'r')
       end
@@ -20,10 +19,8 @@ module Rails
       def tail!
         @file.seek @cursor
 
-        mod = @file.mtime.to_f
-        if mod > @last_checked
+        if !@file.eof?
           contents = @file.read
-          @last_checked = mod
           @cursor = @file.tell
           $stdout.print contents
         end
-- 
cgit v1.2.3


From b8634dddabb84f74a1e5314ea1f26833a6feebc4 Mon Sep 17 00:00:00 2001
From: Santiago Pastorino 
Date: Tue, 3 Aug 2010 02:05:00 -0300
Subject: Stub is_a? not instance_of? here

---
 railties/test/generators/app_generator_test.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 21725a380c..08cfb585f9 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -114,7 +114,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
 
     Rails.application.config.root = app_moved_root
     Rails.application.class.stubs(:name).returns("Myapp")
-    Rails.application.stubs(:instance_of?).returns(Rails::Application)
+    Rails.application.stubs(:is_a?).returns(Rails::Application)
 
     FileUtils.mv(app_root, app_moved_root)
 
-- 
cgit v1.2.3


From 02a5842cd09bd75de4c2fdb6b474c6c0ff163ebf Mon Sep 17 00:00:00 2001
From: wycats 
Date: Tue, 3 Aug 2010 14:54:28 -0700
Subject: Put lib back on the autoload path

---
 railties/lib/rails/engine/configuration.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 521ed95447..db0b20df33 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -20,7 +20,7 @@ module Rails
           paths.app.models          "app/models",          :eager_load => true
           paths.app.mailers         "app/mailers",         :eager_load => true
           paths.app.views           "app/views"
-          paths.lib                 "lib",                 :load_path => true
+          paths.lib                 "lib",                 :eager_load => true
           paths.lib.tasks           "lib/tasks",           :glob => "**/*.rake"
           paths.config              "config"
           paths.config.initializers "config/initializers", :glob => "**/*.rb"
-- 
cgit v1.2.3


From 7745f716a175b680f140c7b735261e5a3064e7e9 Mon Sep 17 00:00:00 2001
From: Simone Carletti 
Date: Mon, 21 Jun 2010 12:07:13 +0200
Subject: Fixed Railtie Rdoc examples not properly formatted [#4918
 state:resolved]

Signed-off-by: Simone Carletti 
---
 railties/lib/rails/railtie.rb | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

(limited to 'railties')

diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index f0d9d95fc4..8a6e2716dc 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -6,53 +6,53 @@ require 'active_support/deprecation'
 module Rails
   # Railtie is the core of the Rails Framework and provides several hooks to extend
   # Rails and/or modify the initialization process.
-  # 
+  #
   # Every major component of Rails (Action Mailer, Action Controller,
   # Action View, Active Record and Active Resource) are all Railties, so each of
   # them is responsible to set their own initialization. This makes, for example,
   # Rails absent of any Active Record hook, allowing any other ORM framework to hook in.
-  # 
+  #
   # Developing a Rails extension does _not_ require any implementation of
   # Railtie, but if you need to interact with the Rails framework during
   # or after boot, then Railtie is what you need to do that interaction.
-  # 
+  #
   # For example, the following would need you to implement Railtie in your
   # plugin:
-  # 
+  #
   # * creating initializers
   # * configuring a Rails framework or the Application, like setting a generator
   # * adding Rails config.* keys to the environment
   # * setting up a subscriber to the Rails +ActiveSupport::Notifications+
   # * adding rake tasks into rails
-  # 
+  #
   # == Creating your Railtie
   #
   # Implementing Railtie in your Rails extension is done by creating a class
   # Railtie that has your extension name and making sure that this gets loaded
   # during boot time of the Rails stack.
-  # 
+  #
   # You can do this however you wish, but here is an example if you want to provide
   # it for a gem that can be used with or without Rails:
-  # 
+  #
   # * Create a file (say, lib/my_gem/railtie.rb) which contains class Railtie inheriting from
   #   Rails::Railtie and is namespaced to your gem:
   #
-  #   # lib/my_gem/railtie.rb
-  #   module MyGem
-  #     class Railtie < Rails::Railtie
+  #     # lib/my_gem/railtie.rb
+  #     module MyGem
+  #       class Railtie < Rails::Railtie
+  #       end
   #     end
-  #   end
-  # 
+  #
   # * Require your own gem as well as rails in this file:
-  # 
-  #   # lib/my_gem/railtie.rb
-  #   require 'my_gem'
-  #   require 'rails'
-  # 
-  #   module MyGem
-  #     class Railtie < Rails::Railtie
+  #
+  #     # lib/my_gem/railtie.rb
+  #     require 'my_gem'
+  #     require 'rails'
+  #     
+  #     module MyGem
+  #       class Railtie < Rails::Railtie
+  #       end
   #     end
-  #   end
   #
   # == Initializers
   #
@@ -65,7 +65,7 @@ module Rails
   #     end
   #   end
   #
-  # If specified, the block can also receive the application object, in case you 
+  # If specified, the block can also receive the application object, in case you
   # need to access some application specific configuration, like middleware:
   #
   #   class MyRailtie < Rails::Railtie
-- 
cgit v1.2.3


From 7e2399a42feb47407ad0cb0688815812f68804de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= 
Date: Wed, 4 Aug 2010 14:13:50 +0200
Subject: Revert "Put lib back on the autoload path"

This was causing engines/gems to eager load everything in lib. Another fix is comming soon.

This reverts commit 02a5842cd09bd75de4c2fdb6b474c6c0ff163ebf.
---
 railties/lib/rails/engine/configuration.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index db0b20df33..521ed95447 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -20,7 +20,7 @@ module Rails
           paths.app.models          "app/models",          :eager_load => true
           paths.app.mailers         "app/mailers",         :eager_load => true
           paths.app.views           "app/views"
-          paths.lib                 "lib",                 :eager_load => true
+          paths.lib                 "lib",                 :load_path => true
           paths.lib.tasks           "lib/tasks",           :glob => "**/*.rake"
           paths.config              "config"
           paths.config.initializers "config/initializers", :glob => "**/*.rb"
-- 
cgit v1.2.3


From d191db76e04f065e1b0cff3766c818f9b8e2f43a Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Thu, 5 Aug 2010 01:09:09 +0200
Subject: standarizes the use of the article "an" for "SQL" and "SQLite"

---
 railties/guides/source/api_documentation_guidelines.textile           | 2 +-
 railties/guides/source/getting_started.textile                        | 4 ++--
 .../rails/generators/rails/app/templates/config/databases/oracle.yml  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/api_documentation_guidelines.textile b/railties/guides/source/api_documentation_guidelines.textile
index d9a0d39d9d..9f201de49b 100644
--- a/railties/guides/source/api_documentation_guidelines.textile
+++ b/railties/guides/source/api_documentation_guidelines.textile
@@ -29,7 +29,7 @@ Documentation has to be concise but comprehensive. Explore and document edge cas
 
 The proper names of Rails components have a space in between the words, like "Active Support". +ActiveRecord+ is a Ruby module, whereas Active Record is an ORM. Historically there has been lack of consistency regarding this, but we checked with David when docrails started. All Rails documentation consistently refer to Rails components by their proper name, and if in your next blog post or presentation you remember this tidbit and take it into account that'd be fenomenal :).
 
-Spell names correctly: HTML, MySQL, JavaScript, ERb.
+Spell names correctly: HTML, MySQL, JavaScript, ERb. Use the article "an" for "SQL", as in "an SQL statement". Also "an SQLite database".
 
 h3. Example Code
 
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 12f2bb146b..ffb0310816 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -213,9 +213,9 @@ If you open this file in a new Rails application, you'll see a default database
 * The +test+ environment is used to run automated tests
 * The +production+ environment is used when you deploy your application for the world to use.
 
-h5. Configuring a SQLite3 Database
+h5. Configuring an SQLite3 Database
 
-Rails comes with built-in support for "SQLite3":http://www.sqlite.org, which is a lightweight serverless database application. While a busy production environment may overload SQLite, it works well for development and testing. Rails defaults to using a SQLite database when creating a new project, but you can always change it later.
+Rails comes with built-in support for "SQLite3":http://www.sqlite.org, which is a lightweight serverless database application. While a busy production environment may overload SQLite, it works well for development and testing. Rails defaults to using an SQLite database when creating a new project, but you can always change it later.
 
 Here's the section of the default configuration file (config/database.yml) with connection information for the development environment:
 
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
index f99ee937f3..fddf8b8144 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
@@ -4,7 +4,7 @@
 #  http://rubyforge.org/projects/ruby-oci8/
 #
 # Specify your database using any valid connection syntax, such as a
-# tnsnames.ora service name, or a SQL connect url string of the form:
+# tnsnames.ora service name, or an SQL connect string of the form:
 #
 #   //host:[port][/service name]
 #
-- 
cgit v1.2.3


From 02572399a588110709c2988fab66e2d65d735bfc Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Thu, 5 Aug 2010 13:51:45 +0200
Subject: AS guide: documents DateTime#advance

---
 .../source/active_support_core_extensions.textile  | 37 ++++++++++++++++++++--
 1 file changed, 34 insertions(+), 3 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index 136fcde82a..d7d14236f6 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -2979,11 +2979,11 @@ Note in the previous example that increments may be negative.
 
 To perform the computation the method first increments years, then months, then weeks, and finally days. This order is important towards the end of months. Say for example we are at the end of February of 2010, and we want to move one month and one day forward.
 
-The method +advance+ advances first one month, and the one day, the result is:
+The method +advance+ advances first one month, and then one day, the result is:
 
 
-Date.new(2010, 2, 28).advance(:months => 1, :day => 1)
-# => Sun, 28 Mar 2010
+Date.new(2010, 2, 28).advance(:months => 1, :days => 1)
+# => Sun, 29 Mar 2010
 
 
 While if it did it the other way around the result would be different:
@@ -3132,6 +3132,37 @@ now.utc?           # => false
 now.utc.utc?       # => true
 
 
+h6(#datetime-advance). +advance+
+
+The most generic way to jump to another datetime is +advance+. This method receives a hash with keys +:years+, +:months+, +:weeks+, +:days+, +:hours+, +:minutes+, and +:seconds+, and returns a datetime advanced as much as the present keys indicate.
+
+
+d = DateTime.current
+# => Thu, 05 Aug 2010 11:33:31 +0000
+d.advance(:years => 1, :months => 1, :days => 1, :hours => 1, :minutes => 1, :seconds => 1)
+# => Tue, 06 Sep 2011 12:34:32 +0000
+
+
+This method first computes the destination date passing +:years+, +:months+, +:weeks+, and +:days+ to +Date#advance+ documented above. After that, it adjusts the time calling +since+ with the number of seconds to advance. This order is relevant, a different ordering would give different datetimes in some edge-cases. The example in +Date#advance+ applies, and we can extend it to show order relevance related to the time bits.
+
+If we first move the date bits (that have also a relative order of processing, as documented before), and then the time bits we get for example the following computation:
+
+
+d = DateTime.new(2010, 2, 28, 23, 59, 59)
+# => Sun, 28 Feb 2010 23:59:59 +0000
+d.advance(:months => 1, :seconds => 1)
+# => Mon, 29 Mar 2010 00:00:00 +0000
+
+
+but if we computed them the other way around, the result would be different:
+
+
+d.advance(:seconds => 1).advance(:months => 1)
+# => Thu, 01 Apr 2010 00:00:00 +0000
+
+
+WARNING: Since +DateTime+ is not DST-aware you can end up in a non-existing point in time with no warning or error telling you so.
+
 h5(#datetime-changing-components). Changing Components
 
 The method +change+ allows you to get a new datetime which is the same as the receiver except for the given options, which may include +:year+, +:month+, +:day+, +:hour+, +:min+, +:sec+, +:offset+, +:start+:
-- 
cgit v1.2.3


From 23abf46112289d379ad539528ab739f74b1e23aa Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Fri, 6 Aug 2010 13:33:02 +0200
Subject: AR guide: fixes a query

---
 railties/guides/source/active_record_querying.textile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 5c4ed3a803..53095a2bd3 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -764,7 +764,7 @@ This loads all the posts and the associated category and comments for each post.
 h5. Nested Associations Hash
 
 
-Category.find(1).includes(:posts => [{:comments => :guest}, :tags])
+Category.includes(:posts => [{:comments => :guest}, :tags]).find(1)
 
 
 This will find the category with id 1 and eager load all of the associated posts, the associated posts' tags and comments, and every comment's guest association.
-- 
cgit v1.2.3


From 62bb83d0a27e83b0800e67676cfa0d0c47453f8e Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Fri, 6 Aug 2010 17:29:52 +0200
Subject: AS guide: documents calculations with Time objects

---
 .../source/active_support_core_extensions.textile  | 112 ++++++++++++++++++---
 1 file changed, 99 insertions(+), 13 deletions(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index d7d14236f6..9d9053d856 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -3053,30 +3053,30 @@ h4(#date-conversions). Conversions
 
 h3. Extensions to +DateTime+
 
-NOTE: All the following methods are defined in +active_support/core_ext/date_time/calculations.rb+.
-
 WARNING: +DateTime+ is not aware of DST rules and so some of these methods have edge cases when a DST change is going on. For example +seconds_since_midnight+ might not return the real amount in such a day.
 
 h4(#calculations-datetime). Calculations
 
+NOTE: All the following methods are defined in +active_support/core_ext/date_time/calculations.rb+.
+
 The class +DateTime+ is a subclass of +Date+ so by loading +active_support/core_ext/date/calculations.rb+ you inherit these methods and their aliases, except that they will always return datetimes:
 
 
 yesterday
 tomorrow
-beginning_of_week
-end_on_week
+beginning_of_week (monday, at_beginning_of_week)
+end_on_week (at_end_of_week)
 next_week
 months_ago
 months_since
-beginning_of_month
-end_of_month
+beginning_of_month (at_beginning_of_month)
+end_of_month (at_end_of_month)
 prev_month
 next_month
-beginning_of_quarter
-end_of_quarter
-beginning_of_year
-end_of_year
+beginning_of_quarter (at_beginning_of_quarter)
+end_of_quarter (at_end_of_quarter)
+beginning_of_year (at_beginning_of_year)
+end_of_year (at_end_of_year)
 years_ago
 years_since
 prev_year
@@ -3086,10 +3086,10 @@ next_year
 The following methods are reimplemented so you do *not* need to load +active_support/core_ext/date/calculations.rb+ for these ones:
 
 
-beginning_of_day
+beginning_of_day (midnight, at_midnight, at_beginning_of_day)
 end_of_day
 ago
-since
+since (in)
 
 
 On the other hand, +advance+ and +change+ are also defined and support more options, they are documented below.
@@ -3199,7 +3199,93 @@ h4(#datetime-conversions). Conversions
 
 h3. Extensions to +Time+
 
-...
+h4(#time-calculations). Calculations
+
+NOTE: All the following methods are defined in +active_support/core_ext/date_time/calculations.rb+.
+
+Active Support adds to +Time+ many of the methods available for +DateTime+:
+
+
+past?
+today?
+future?
+yesterday
+tomorrow
+seconds_since_midnight
+change
+advance
+ago
+since (in)
+beginning_of_day (midnight, at_midnight, at_beginning_of_day)
+end_of_day
+beginning_of_week (monday, at_beginning_of_week)
+end_on_week (at_end_of_week)
+next_week
+months_ago
+months_since
+beginning_of_month (at_beginning_of_month)
+end_of_month (at_end_of_month)
+prev_month
+next_month
+beginning_of_quarter (at_beginning_of_quarter)
+end_of_quarter (at_end_of_quarter)
+beginning_of_year (at_beginning_of_year)
+end_of_year (at_end_of_year)
+years_ago
+years_since
+prev_year
+next_year
+
+
+They are analogous. Please refer to their documentation above and take into account the following differences:
+
+* +change+ accepts an additional +:usec+ option.
+* +Time+ understands DST, so you get correct DST calculations as in
+
+
+# In Barcelona, 2010/03/28 02:00 +0100 becomes 2010/03/28 03:00 +0200 due to DST.
+t = Time.local_time(2010, 3, 28, 1, 59, 59)
+# => Sun Mar 28 01:59:59 +0100 2010
+t.advance(:seconds => 1)
+# => Sun Mar 28 03:00:00 +0200 2010
+
+
+* If +since+ or +ago+ jump to a time that can't be expressed with +Time+ a +DateTime+ object is returned instead.
+
+h4. Time Constructors
+
+Active Support defines +Time.current+ to be +Time.zone.now+ if there's a user time zone defined, with fallback to +Time.now+:
+
+
+Time.zone_default
+# => #
+Time.current
+# => Fri, 06 Aug 2010 17:11:58 CEST +02:00
+
+
+Analogously to +DateTime+, the predicates +past?+, and +future?+ are relative to +Time.current+.
+
+Use the +local_time+ class method to create time objects honoring the user time zone:
+
+
+Time.zone_default
+# => #
+Time.local_time(2010, 8, 15)
+# => Sun Aug 15 00:00:00 +0200 2010
+
+
+The +utc_time+ class method returns a time in UTC:
+
+
+Time.zone_default
+# => #
+Time.utc_time(2010, 8, 15)
+# => Sun Aug 15 00:00:00 UTC 2010
+
+
+Both +local_time+ and +utc_time+ accept up to seven positional arguments: year, month, day, hour, min, sec, usec. Year is mandatory, month and day default to 1, and the rest default to 0.
+
+If the time to be constructed lies beyond the range supported by +Time+ in the runtime platform, usecs are discarded and a +DateTime+ object is returned instead.
 
 h3. Extensions to +Process+
 
-- 
cgit v1.2.3


From 672c796ac4fe857fde817e7603f74790d4aca0b8 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Fri, 6 Aug 2010 22:06:13 +0200
Subject: fix reference to time/calculations.rb in AS guide

---
 railties/guides/source/active_support_core_extensions.textile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'railties')

diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index 9d9053d856..9a449debae 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -3201,7 +3201,7 @@ h3. Extensions to +Time+
 
 h4(#time-calculations). Calculations
 
-NOTE: All the following methods are defined in +active_support/core_ext/date_time/calculations.rb+.
+NOTE: All the following methods are defined in +active_support/core_ext/time/calculations.rb+.
 
 Active Support adds to +Time+ many of the methods available for +DateTime+:
 
-- 
cgit v1.2.3


From ddeaf6c8877599b18b371232e72ed7150f5bb688 Mon Sep 17 00:00:00 2001
From: Xavier Noria 
Date: Sun, 8 Aug 2010 18:29:58 +0200
Subject: routing guide: documents the CONTROLLER environment variable
 understood by the routes task

---
 railties/guides/source/routing.textile | 6 ++++++
 1 file changed, 6 insertions(+)

(limited to 'railties')

diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 7b665d81e7..625941ba31 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -762,6 +762,12 @@ formatted_users GET  /users.:format  {:controller=>"users", :action=>"index"}
                 POST /users.:format  {:controller=>"users", :action=>"create"}
 
+You may restrict the listing to the routes that map to a particular controller setting the +CONTROLLER+ environment variable: + + +$ CONTROLLER=users rake routes + + TIP: You'll find that the output from +rake routes+ is much more readable if you widen your terminal window until the output lines don't wrap. h4. Testing Routes -- cgit v1.2.3 From 678aeb7e48fef9cd3107f22fb4ae9d1ecec27a59 Mon Sep 17 00:00:00 2001 From: Daniel McNevin Date: Sun, 8 Aug 2010 19:57:42 -0400 Subject: updated the action_controller guide with the new session configuration options --- .../source/action_controller_overview.textile | 41 +++++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile index 038ca903c1..ff112608ff 100644 --- a/railties/guides/source/action_controller_overview.textile +++ b/railties/guides/source/action_controller_overview.textile @@ -159,23 +159,38 @@ Read more about session storage in the "Security Guide":security.html. If you need a different session storage mechanism, you can change it in the +config/initializers/session_store.rb+ file: -# Use the database for sessions instead of the cookie-based default, -# which shouldn't be used to store highly confidential information -# (create the session table with "rake db:sessions:create") -# ActionController::Base.session_store = :active_record_store + # Use the database for sessions instead of the cookie-based default, + # which shouldn't be used to store highly confidential information + # (create the session table with "rake db:sessions:create") + # YourApp::Application.config.session_store :active_record_store -Rails sets up a session key (the name of the cookie) and (for the CookieStore) a secret key used when signing the session data. These can also be changed in +config/initializers/session_store.rb+: +Rails sets up a session key (the name of the cookie) when signing the session data. These can also be changed in +config/initializers/session_store.rb+: -# Your secret key for verifying cookie session data integrity. -# If you change this key, all old sessions will become invalid! -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -ActionController::Base.session = { - :key => '_yourappname_session', - :secret => '4f50711b8f0f49572...' -} + # Be sure to restart your server when you modify this file. + + YourApp::Application.config.session_store :cookie_store, :key => '_your_app_session' + + +You can also pass a +:domain+ key and specify the domain name for the cookie: + + + # Be sure to restart your server when you modify this file. + + YourApp::Application.config.session_store :cookie_store, :key => '_your_app_session', :domain => ".test.com" + + +Rails sets up (for the CookieStore) a secret key used for signing the session data. This can be changed in +config/initializers/secret_token.rb+ + + + # Be sure to restart your server when you modify this file. + + # Your secret key for verifying the integrity of signed cookies. + # If you change this key, all old signed cookies will become invalid! + # Make sure the secret is at least 30 characters and all random, + # no regular words or you'll be exposed to dictionary attacks. + YourApp::Application.config.secret_token = '49d3f3de9ed86c74b94ad6bd0...' NOTE: Changing the secret when using the CookieStore will invalidate all existing sessions. -- cgit v1.2.3 From d87c57bf3e5718c6995ecc73cf8d7396e3ba4b19 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 9 Aug 2010 12:21:21 +0200 Subject: AC guide: commit review, block examples go at column 0, use .example.com as example domain --- .../source/action_controller_overview.textile | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile index ff112608ff..ec2d5b2787 100644 --- a/railties/guides/source/action_controller_overview.textile +++ b/railties/guides/source/action_controller_overview.textile @@ -159,41 +159,41 @@ Read more about session storage in the "Security Guide":security.html. If you need a different session storage mechanism, you can change it in the +config/initializers/session_store.rb+ file: - # Use the database for sessions instead of the cookie-based default, - # which shouldn't be used to store highly confidential information - # (create the session table with "rake db:sessions:create") - # YourApp::Application.config.session_store :active_record_store +# Use the database for sessions instead of the cookie-based default, +# which shouldn't be used to store highly confidential information +# (create the session table with "rake db:sessions:create") +# YourApp::Application.config.session_store :active_record_store Rails sets up a session key (the name of the cookie) when signing the session data. These can also be changed in +config/initializers/session_store.rb+: - # Be sure to restart your server when you modify this file. +# Be sure to restart your server when you modify this file. - YourApp::Application.config.session_store :cookie_store, :key => '_your_app_session' +YourApp::Application.config.session_store :cookie_store, :key => '_your_app_session' You can also pass a +:domain+ key and specify the domain name for the cookie: - # Be sure to restart your server when you modify this file. +# Be sure to restart your server when you modify this file. - YourApp::Application.config.session_store :cookie_store, :key => '_your_app_session', :domain => ".test.com" +YourApp::Application.config.session_store :cookie_store, :key => '_your_app_session', :domain => ".example.com" Rails sets up (for the CookieStore) a secret key used for signing the session data. This can be changed in +config/initializers/secret_token.rb+ - # Be sure to restart your server when you modify this file. +# Be sure to restart your server when you modify this file. - # Your secret key for verifying the integrity of signed cookies. - # If you change this key, all old signed cookies will become invalid! - # Make sure the secret is at least 30 characters and all random, - # no regular words or you'll be exposed to dictionary attacks. - YourApp::Application.config.secret_token = '49d3f3de9ed86c74b94ad6bd0...' +# Your secret key for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +YourApp::Application.config.secret_token = '49d3f3de9ed86c74b94ad6bd0...' -NOTE: Changing the secret when using the CookieStore will invalidate all existing sessions. +NOTE: Changing the secret when using the +CookieStore+ will invalidate all existing sessions. h4. Accessing the Session -- cgit v1.2.3 From 505546af709082f25444e323ee0f2141a86bbc63 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 9 Aug 2010 14:11:05 +0200 Subject: form helpers guide: fixes an example --- railties/guides/source/form_helpers.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile index 1f1b7d076e..146b75da3f 100644 --- a/railties/guides/source/form_helpers.textile +++ b/railties/guides/source/form_helpers.textile @@ -647,7 +647,7 @@ the +params+ hash will contain {'person' => {'name' => 'Henry'}} -and +params["name"]+ will retrieve the submitted value in the controller. +and +params[:person][:name]+ will retrieve the submitted value in the controller. Hashes can be nested as many levels as required, for example -- cgit v1.2.3 From 6767946374353f90ce05e68d38bcb93dcb8bae56 Mon Sep 17 00:00:00 2001 From: wycats Date: Mon, 9 Aug 2010 11:48:31 -0700 Subject: Improve best_standards_support to use only IE=Edge in development mode --- railties/lib/rails/application.rb | 2 +- .../config/environments/development.rb.tt | 4 + railties/test/application/routing_test.rb | 96 +++++++++++++++------- 3 files changed, 70 insertions(+), 32 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 6622cfdd2f..5b26333486 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -205,7 +205,7 @@ module Rails middleware.use ::ActionDispatch::ParamsParser middleware.use ::Rack::MethodOverride middleware.use ::ActionDispatch::Head - middleware.use ::ActionDispatch::BestStandardsSupport if config.action_dispatch.best_standards_support + middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support if config.action_dispatch.best_standards_support end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index 99758dfcf7..7616614aff 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -19,4 +19,8 @@ # Print deprecation notices to the Rails logger config.active_support.deprecation = :log + + # Only use best-standards-support built into browsers + config.action_dispatch.best_standards_support = :builtin end + diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index a10a39ef40..febc53bac9 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -11,19 +11,19 @@ module ApplicationTests extend Rack::Test::Methods end - def app + def app(env = "production") + old_env = ENV["RAILS_ENV"] + @app ||= begin + ENV["RAILS_ENV"] = env require "#{app_path}/config/environment" Rails.application end + ensure + ENV["RAILS_ENV"] = old_env end - test "rails/info/properties" do - get "/rails/info/properties" - assert_equal 200, last_response.status - end - - test "simple controller" do + def simple_controller controller :foo, <<-RUBY class FooController < ApplicationController def index @@ -37,12 +37,42 @@ module ApplicationTests match ':controller(/:action)' end RUBY + end + + test "rails/info/properties in development" do + app("development") + get "/rails/info/properties" + assert_equal 200, last_response.status + end + + test "rails/info/properties in production" do + app("production") + get "/rails/info/properties" + assert_equal 404, last_response.status + end + + test "simple controller" do + simple_controller get '/foo' assert_equal 'foo', last_response.body + end + + test "simple controller in production mode returns best standards" do + simple_controller + + get '/foo' assert_equal "IE=Edge,chrome=1", last_response.headers["X-UA-Compatible"] end + test "simple controller in development mode leaves out Chrome" do + simple_controller + app("development") + + get "/foo" + assert_equal "IE=Edge", last_response.headers["X-UA-Compatible"] + end + test "simple controller with helper" do controller :foo, <<-RUBY class FooController < ApplicationController @@ -147,38 +177,42 @@ module ApplicationTests assert_equal 'admin::foo', last_response.body end - test "reloads routes when configuration is changed" do - controller :foo, <<-RUBY - class FooController < ApplicationController - def bar - render :text => "bar" + {"development" => "baz", "production" => "bar"}.each do |mode, expected| + test "reloads routes when configuration is changed in #{mode}" do + controller :foo, <<-RUBY + class FooController < ApplicationController + def bar + render :text => "bar" + end + + def baz + render :text => "baz" + end end + RUBY - def baz - render :text => "baz" + app_file 'config/routes.rb', <<-RUBY + AppTemplate::Application.routes.draw do |map| + match 'foo', :to => 'foo#bar' end - end - RUBY + RUBY - app_file 'config/routes.rb', <<-RUBY - AppTemplate::Application.routes.draw do |map| - match 'foo', :to => 'foo#bar' - end - RUBY + app(mode) - get '/foo' - assert_equal 'bar', last_response.body + get '/foo' + assert_equal 'bar', last_response.body - app_file 'config/routes.rb', <<-RUBY - AppTemplate::Application.routes.draw do |map| - match 'foo', :to => 'foo#baz' - end - RUBY + app_file 'config/routes.rb', <<-RUBY + AppTemplate::Application.routes.draw do |map| + match 'foo', :to => 'foo#baz' + end + RUBY - sleep 0.1 + sleep 0.1 - get '/foo' - assert_equal 'baz', last_response.body + get '/foo' + assert_equal expected, last_response.body + end end test 'resource routing with irrigular inflection' do -- cgit v1.2.3 From 898bd38d7a89c39a5e89f0e6be208e6622b07e3d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 10 Aug 2010 01:30:37 +0200 Subject: AS guide: documents date/datetime/time arithmetic with durations --- .../source/active_support_core_extensions.textile | 63 +++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index 9a449debae..fbb617715f 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -3009,6 +3009,26 @@ Date.new(2010, 1, 31).change(:month => 2) # => ArgumentError: invalid date +h5. Durations + +Durations can be added and substracted to dates: + + +d = Date.current +# => Mon, 09 Aug 2010 +d + 1.year +# => Tue, 09 Aug 2011 +d - 3.hours +# => Sun, 08 Aug 2010 21:00:00 UTC +00:00 + + +They translate to calls to +since+ or +advance+. For example here we get the correct jump in the calendar reform: + + +Date.new(1582, 10, 4) + 1.day +# => Fri, 15 Oct 1582 + + h5. Timestamps INFO: The following methods return a +Time+ object if possible, otherwise a +DateTime+. If set, they honor the user time zone. @@ -3195,7 +3215,25 @@ DateTime.current.change(:month => 2, :day => 30) # => ArgumentError: invalid date -h4(#datetime-conversions). Conversions +h5. Durations + +Durations can be added and substracted to datetimes: + + +now = DateTime.current +# => Mon, 09 Aug 2010 23:15:17 +0000 +now + 1.year +# => Tue, 09 Aug 2011 23:15:17 +0000 +now - 1.week +# => Mon, 02 Aug 2010 23:15:17 +0000 + + +They translate to calls to +since+ or +advance+. For example here we get the correct jump in the calendar reform: + + +DateTime.new(1582, 10, 4, 23) + 1.hour +# => Fri, 15 Oct 1582 00:00:00 +0000 + h3. Extensions to +Time+ @@ -3243,6 +3281,9 @@ They are analogous. Please refer to their documentation above and take into acco * +Time+ understands DST, so you get correct DST calculations as in +Time.zone_default +# => # + # In Barcelona, 2010/03/28 02:00 +0100 becomes 2010/03/28 03:00 +0200 due to DST. t = Time.local_time(2010, 3, 28, 1, 59, 59) # => Sun Mar 28 01:59:59 +0100 2010 @@ -3287,6 +3328,26 @@ Both +local_time+ and +utc_time+ accept up to seven positional arguments: year, If the time to be constructed lies beyond the range supported by +Time+ in the runtime platform, usecs are discarded and a +DateTime+ object is returned instead. +h5. Durations + +Durations can be added and substracted to time objects: + + +now = Time.current +# => Mon, 09 Aug 2010 23:20:05 UTC +00:00 +now + 1.year +# => Tue, 09 Aug 2011 23:21:11 UTC +00:00 +now - 1.week +# => Mon, 02 Aug 2010 23:21:11 UTC +00:00 + + +They translate to calls to +since+ or +advance+. For example here we get the correct jump in the calendar reform: + + +Time.utc_time(1582, 10, 3) + 5.days +# => Mon Oct 18 00:00:00 UTC 1582 + + h3. Extensions to +Process+ ... -- cgit v1.2.3 From 41328815c06b6fb5e0747604389f7f4c8bd60483 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 10 Aug 2010 01:53:53 +0200 Subject: AS guide: documents Process.daemon --- railties/guides/source/active_support_core_extensions.textile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index fbb617715f..01eb09f3d3 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -3350,7 +3350,9 @@ Time.utc_time(1582, 10, 3) + 5.days h3. Extensions to +Process+ -... +h4. +daemon+ + +Ruby 1.9 provides +Process.daemon+, and Active Support defines it for previous versions. It accepts the same two arguments, whether it should chdir to the root directory (default, true), and whether it should inherit the standard file descriptors from the parent (default, false). h3. Extensions to +File+ -- cgit v1.2.3 From a5d401aa998c085670fb94387304cc4e1b098412 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 10 Aug 2010 03:28:59 +0200 Subject: AS guide: documents Module#delegate --- .../source/active_support_core_extensions.textile | 82 ++++++++++++++++++++++ 1 file changed, 82 insertions(+) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index 01eb09f3d3..672bf750bf 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -984,6 +984,88 @@ though an anonymous module is unreachable by definition. NOTE: Defined in +active_support/core_ext/module/anonymous.rb+. +h4. Delegation + +The +delegate+ macro declares that some instance method has to be forwarded to some object. + +Let's imagine that users in some application have login information in the +User+ model but name and other data in a separate +Profile+ model: + + +class User < ActiveRecord::Base + has_one :profile +end + + +With that configuration you get a user's name via his profile, +user.profile.name+, but you could write a shortcut so that client code can read it directly: + + +class User < ActiveRecord::Base + has_one :profile + + def name + profile.name + end +end + + +That is what +delegate+ does for you: + + +class User < ActiveRecord::Base + has_one :profile + + delegate :name, :to => :profile +end + + +When interpolated into a string, the +:to+ option should become an expression that evaluates to the object the method is delegated to: + + +delegate :logger, :to => :Rails +delegate :table_name, :to => 'self.class' + + +WARNING: If the +:prefix+ option is +true+ this is less generic, see below. + +By default, if the delegation raises +NoMethodError+ and the target is +nil+ the exception is propagated. You can ask that +nil+ is returned instead with the +:allow_nil+ option: + + +class User < ActiveRecord::Base + has_one :profile + + delegate :name, :to => :profile, :allow_nil => true +end + + +With +:allow_nil+ the call +user.name+ returns +nil+ if the user has no profile instead of raising an exception. + +The option +:prefix+ adds a prefix to the name of the generated method. This may be handy for example to get a better name: + + +class Account < ActiveRecord::Base + has_one :address + + delegate :street, :to => :address, :prefix => true +end + + +The previous example generates +Account#address_street+ rather than +Account#street+. + +WARNING: Since in this case the name of the generated method is composed of the target object and target method names, the +:to+ option must be a method name. + +A custom prefix may also be configured: + + +class User < ActiveRecord::Base + has_one :attachment + + delegate :size, :to => :attachment, :prefix => :avatar + + +In the previous example the macro generates +User#avatar_size+ rather than +User#size+. + +NOTE: Defined in +active_support/core_ext/module/delegation.rb+ + h3. Extensions to +Class+ h4. Class Attributes -- cgit v1.2.3 From ae147b45bb4aa2168e104ffc989e42f5c9dbfd80 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 10 Aug 2010 03:46:21 +0200 Subject: AS guide: documents Module#(instance_)method_names --- .../guides/source/active_support_core_extensions.textile | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index 672bf750bf..e0c69516cf 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -1066,6 +1066,18 @@ In the previous example the macro generates +User#avatar_size+ rather than +User NOTE: Defined in +active_support/core_ext/module/delegation.rb+ +h4. Method Names + +The builtin methods +instance_methods+ and +methods+ return method names as strings or symbols depending on the Ruby version. Active Support defines +instance_method_names+ and +method_names+ to be equivalent to them, respectively, but always getting strings back. + +For example, +ActionView::Helpers::FormBuilder+ knows this array difference is going to work no matter the Ruby version: + + +self.field_helpers = (FormHelper.instance_method_names - ['form_for']) + + +NOTE: Defined in +active_support/core_ext/module/method_names.rb+ + h3. Extensions to +Class+ h4. Class Attributes -- cgit v1.2.3 From 5fc8af4712ea5b5f7a2ee8bedb3b431901de814d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 10 Aug 2010 03:59:26 +0200 Subject: AS guide: documents Module#redefine_method --- .../source/active_support_core_extensions.textile | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index e0c69516cf..31433d50eb 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -1078,6 +1078,27 @@ self.field_helpers = (FormHelper.instance_method_names - ['form_for']) NOTE: Defined in +active_support/core_ext/module/method_names.rb+ +h4. Redefining Methods + +There are cases where you need to define a method with +define_method+, but don't know whether a method with that name already exists. If it does, a warning is issued if they are enabled. No big deal, but not clean either. + +The method +redefine_method+ prevents such a potential warning, removing the existing method before if needed. Rails uses it in a few places, for instance when it generates an association's API: + + +redefine_method("#{reflection.name}=") do |new_value| + association = association_instance_get(reflection.name) + + if association.nil? || association.target != new_value + association = association_proxy_class.new(self, reflection) + end + + association.replace(new_value) + association_instance_set(reflection.name, new_value.nil? ? nil : association) +end + + +NOTE: Defined in +active_support/core_ext/module/remove_method.rb+ + h3. Extensions to +Class+ h4. Class Attributes -- cgit v1.2.3 From 347d604792fb70873b6c1a9eb13fce11e96005fb Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 10 Aug 2010 04:09:52 +0200 Subject: adds the AS guide to the guides index --- railties/guides/source/active_support_core_extensions.textile | 1 + railties/guides/source/index.html.erb | 4 ++++ railties/guides/source/layout.html.erb | 1 + 3 files changed, 6 insertions(+) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index 31433d50eb..c1e3b97f81 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -3543,4 +3543,5 @@ h3. Changelog "Lighthouse ticket":https://rails.lighthouseapp.com/projects/16213/tickets/67 +* August 10, 2010: Starts to take shape, added to the index. * April 18, 2009: Initial version by "Xavier Noria":credits.html#fxn diff --git a/railties/guides/source/index.html.erb b/railties/guides/source/index.html.erb index 254ee91ab4..a0db87c188 100644 --- a/railties/guides/source/index.html.erb +++ b/railties/guides/source/index.html.erb @@ -88,6 +88,10 @@ Ruby on Rails Guides
+<%= guide("Active Support Core Extensions", 'active_support_core_extensions.html') do %> +

This guide documents the Ruby core extensions defined in Active Support.

+<% end %> + <%= guide("Rails Internationalization API", 'i18n.html') do %>

This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on.

<% end %> diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb index c4758316ea..cc7d54c256 100644 --- a/railties/guides/source/layout.html.erb +++ b/railties/guides/source/layout.html.erb @@ -62,6 +62,7 @@
Digging Deeper
+
Active Support Core Extensions
Rails Internationalization API
Action Mailer Basics
Testing Rails Applications
-- cgit v1.2.3 From 2a984806ca89ac5fb64256d82fdfe318dd8c543d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 10 Aug 2010 11:52:29 +0200 Subject: AS guide: removes some duplication, and makes a second pass on method delegation --- .../source/active_support_core_extensions.textile | 165 +++------------------ 1 file changed, 21 insertions(+), 144 deletions(-) (limited to 'railties') diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index c1e3b97f81..54f766fffd 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -667,129 +667,6 @@ end NOTE: Defined in +active_support/core_ext/module/attribute_accessors.rb+. -h4. Method Delegation - -The class method +delegate+ offers an easy way to forward methods. - -For example, if +User+ has some details like the age factored out to +Profile+, it could be handy to still be able to access such attributes directly, user.age, instead of having to explicit the chain user.profile.age. - -That can be accomplished by hand: - - -class User - has_one :profile - - def age - profile.age - end -end - - -But with +delegate+ you can make that shorter and the intention even more obvious: - - -class User - has_one :profile - - delegate :age, to => :profile -end - - -The macro accepts more than one method: - - -class User - has_one :profile - - delegate :age, :avatar, :twitter_username, to => :profile -end - - -Methods can be delegated to objects returned by methods, as in the examples above, but also to instance variables, class variables, and constants. Just pass their names as symbols or strings, including the at signs in the last cases. - -For example, +ActionView::Base+ delegates +erb_trim_mode=+: - - -module ActionView - class Base - delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB' - end -end - - -In fact, you can delegate to any expression passed as a string. It will be evaluated in the context of the receiver. Controllers for example delegate alerts and notices to the current flash: - - -delegate :alert, :notice, :to => "request.flash" - - -If the target is +nil+ calling any delegated method will raise an exception even if +nil+ responds to such method. You can override this behavior setting the option +:allow_nil+ to true, in which case the forwarded call will simply return +nil+. - -If the target is a method, the name of delegated methods can also be prefixed. If the +:prefix+ option is set to (exactly) the +true+ object, the value of the +:to+ option is prefixed: - - -class Invoice - belongs_to :customer - - # defines a method called customer_name - delegate :name, :to => :customer, :prefix => true -end - - -And a custom prefix can be set as well, in that case it does not matter wheter the target is a method or not: - - -class Account - belongs_to :user - - # defines a method called admin_email - delegate :email, :to => :user, :prefix => 'admin' -end - - -NOTE: Defined in +active_support/core_ext/module/delegation.rb+. - -h4. Method Removal - -h5. +remove_possible_method+ - -The method +remove_possible_method+ is like the standard +remove_method+, except it silently returns on failure: - - -class A; end - -A.class_eval do - remove_method(:nonexistent) # raises NameError - remove_possible_method(:nonexistent) # no problem, continue -end - - -This may come in handy if you need to define a method that may already exist, since redefining a method issues a warning "method redefined; discarding old redefined_method_name". - -h5. +redefine_method(method_name, &block)+ - -The method first removes method with given name (using +remove_possible_method+) and then defines new one. - - -class A; end - -A.class_eval do - redefine_method(:foobar) do |foo| - #do something here - end - - #Code above does the same as this: - - method_name = :foobar - remove_possible_method(method_name) - define_method(method_name) do |foo| - #do something here - end -end - - -NOTE: Defined in +active_support/core_ext/module/remove_method.rb+. - h4. Parents h5. +parent+ @@ -984,9 +861,9 @@ though an anonymous module is unreachable by definition. NOTE: Defined in +active_support/core_ext/module/anonymous.rb+. -h4. Delegation +h4. Method Delegation -The +delegate+ macro declares that some instance method has to be forwarded to some object. +The macro +delegate+ offers an easy way to forward methods. Let's imagine that users in some application have login information in the +User+ model but name and other data in a separate +Profile+ model: @@ -996,7 +873,7 @@ class User < ActiveRecord::Base end -With that configuration you get a user's name via his profile, +user.profile.name+, but you could write a shortcut so that client code can read it directly: +With that configuration you get a user's name via his profile, +user.profile.name+, but it could be handy to still be able to access such attribute directly: class User < ActiveRecord::Base @@ -1018,10 +895,21 @@ class User < ActiveRecord::Base end -When interpolated into a string, the +:to+ option should become an expression that evaluates to the object the method is delegated to: +It is shorter, and the intention more obvious. + +The macro accepts several methods: + + +delegate :name, :age, :address, :twitter, :to => :profile + + +When interpolated into a string, the +:to+ option should become an expression that evaluates to the object the method is delegated to. Typically a string or symbol. Such a expression is evaluated in the context of the receiver: +# delegates to the Rails constant delegate :logger, :to => :Rails + +# delegates to the receiver's class delegate :table_name, :to => 'self.class' @@ -1030,39 +918,28 @@ WARNING: If the +:prefix+ option is +true+ this is less generic, see below. By default, if the delegation raises +NoMethodError+ and the target is +nil+ the exception is propagated. You can ask that +nil+ is returned instead with the +:allow_nil+ option: -class User < ActiveRecord::Base - has_one :profile - - delegate :name, :to => :profile, :allow_nil => true -end +delegate :name, :to => :profile, :allow_nil => true -With +:allow_nil+ the call +user.name+ returns +nil+ if the user has no profile instead of raising an exception. +With +:allow_nil+ the call +user.name+ returns +nil+ if the user has no profile. The option +:prefix+ adds a prefix to the name of the generated method. This may be handy for example to get a better name: -class Account < ActiveRecord::Base - has_one :address - - delegate :street, :to => :address, :prefix => true -end +delegate :street, :to => :address, :prefix => true -The previous example generates +Account#address_street+ rather than +Account#street+. +The previous example generates +address_street+ rather than +street+. WARNING: Since in this case the name of the generated method is composed of the target object and target method names, the +:to+ option must be a method name. A custom prefix may also be configured: -class User < ActiveRecord::Base - has_one :attachment - - delegate :size, :to => :attachment, :prefix => :avatar +delegate :size, :to => :attachment, :prefix => :avatar -In the previous example the macro generates +User#avatar_size+ rather than +User#size+. +In the previous example the macro generates +avatar_size+ rather than +size+. NOTE: Defined in +active_support/core_ext/module/delegation.rb+ -- cgit v1.2.3