From 9c80f5b3910ca0573f6e40aaccf3102c260986b6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 17 Jul 2010 13:14:38 -0700 Subject: use === to avoid regular expression creation, and speed up string comparison --- activesupport/lib/active_support/notifications/fanout.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 300ec842a9..b27713e4ad 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -39,13 +39,7 @@ module ActiveSupport class Binding #:nodoc: def initialize(queue, pattern) @queue = queue - @pattern = - case pattern - when Regexp, NilClass - pattern - else - /^#{Regexp.escape(pattern.to_s)}$/ - end + @pattern = pattern end def subscribe(&block) @@ -70,13 +64,13 @@ module ActiveSupport end def subscribed_to?(name) - !@pattern || @pattern =~ name.to_s + !@pattern || @pattern === name.to_s end def matches?(subscriber_or_name) case subscriber_or_name when String - @pattern && @pattern =~ subscriber_or_name + @pattern && @pattern === subscriber_or_name when self true end -- cgit v1.2.3 From 4226c93779dae53c6921f8ce93d6af000a24e2d1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 17 Jul 2010 14:35:44 -0700 Subject: removing Binding class --- .../lib/active_support/notifications/fanout.rb | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index b27713e4ad..fa469c8b79 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -6,13 +6,15 @@ module ActiveSupport def initialize @subscribers = [] @listeners_for = {} + @pattern = nil end def bind(pattern) - Binding.new(self, pattern) + @pattern = pattern + self end - def subscribe(pattern = nil, &block) + def subscribe(pattern = @pattern, &block) @listeners_for.clear @subscribers << Subscriber.new(pattern, &block) @subscribers.last @@ -35,18 +37,6 @@ module ActiveSupport def wait end - # Used for internal implementation only. - class Binding #:nodoc: - def initialize(queue, pattern) - @queue = queue - @pattern = pattern - end - - def subscribe(&block) - @queue.subscribe(@pattern, &block) - end - end - class Subscriber #:nodoc: def initialize(pattern, &block) @pattern = pattern -- cgit v1.2.3 From fa73e777a1dac2daaa14f781b8a17102ca47ea8b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 17 Jul 2010 14:44:29 -0700 Subject: private method is not needed --- activesupport/lib/active_support/notifications/fanout.rb | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index fa469c8b79..6dfc3c0a99 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -45,7 +45,7 @@ module ActiveSupport def publish(*args) return unless subscribed_to?(args.first) - push(*args) + @block.call(*args) true end @@ -58,19 +58,9 @@ module ActiveSupport end def matches?(subscriber_or_name) - case subscriber_or_name - when String + self === subscriber_or_name || @pattern && @pattern === subscriber_or_name - when self - true - end end - - private - - def push(*args) - @block.call(*args) - end end end end -- cgit v1.2.3 From 606d8fdfc8b86800fb738854c240c1c15bd272bb Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 17 Jul 2010 14:45:59 -0700 Subject: drained? is never called --- activesupport/lib/active_support/notifications/fanout.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 6dfc3c0a99..7eefb7f20a 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -49,10 +49,6 @@ module ActiveSupport true end - def drained? - true - end - def subscribed_to?(name) !@pattern || @pattern === name.to_s end -- cgit v1.2.3 From 7aec9f9c028dd7b53f2a389d2d8bd7d27a770461 Mon Sep 17 00:00:00 2001 From: Nick Quaranto Date: Sun, 18 Jul 2010 05:47:34 +0800 Subject: Removing ActionDispatch::Http::FilterParameters#fitered_parameters alias --- actionpack/lib/action_dispatch/http/filter_parameters.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index 152aaa2e67..47643ce130 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -36,7 +36,6 @@ module ActionDispatch parameters.dup end end - alias :fitered_params :filtered_parameters # Return a hash of request.env with all sensitive data replaced. def filtered_env @@ -110,4 +109,4 @@ module ActionDispatch end end -end \ No newline at end of file +end -- cgit v1.2.3 From cebe5c2fac708885c018550f7ef4610df613e5af Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 18 Jul 2010 04:55:59 +0800 Subject: It's not needed to initialize the attr when calling mattr_writer --- .../lib/active_support/core_ext/module/attribute_accessors.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 9c4d5fae26..2d88cb57e5 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -5,9 +5,7 @@ class Module options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) - unless defined? @@#{sym} - @@#{sym} = nil - end + @@#{sym} = nil unless defined? @@#{sym} def self.#{sym} @@#{sym} @@ -28,10 +26,6 @@ class Module options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) - unless defined? @@#{sym} - @@#{sym} = nil - end - def self.#{sym}=(obj) @@#{sym} = obj end -- cgit v1.2.3 From cfca55949f51bf3970bae7c506807db97dbcf05f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 17 Jul 2010 15:53:22 -0700 Subject: convert duration to an attr_reader --- activesupport/lib/active_support/notifications/instrumenter.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index 7e89402822..34bccb83d0 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -33,7 +33,7 @@ module ActiveSupport end class Event - attr_reader :name, :time, :end, :transaction_id, :payload + attr_reader :name, :time, :end, :transaction_id, :payload, :duration def initialize(name, start, ending, transaction_id, payload) @name = name @@ -41,14 +41,11 @@ module ActiveSupport @time = start @transaction_id = transaction_id @end = ending - end - - def duration - @duration ||= 1000.0 * (@end - @time) + @duration = 1000.0 * (@end - @time) end def parent_of?(event) - start = (self.time - event.time) * 1000 + start = (time - event.time) * 1000 start <= 0 && (start + duration >= event.duration) end end -- 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. --- activerecord/lib/active_record/railtie.rb | 6 ++++++ 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 ++++++++++++++++ 6 files changed, 64 insertions(+), 15 deletions(-) diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index eff51a7c87..78fdb77216 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -22,6 +22,12 @@ module ActiveRecord load "active_record/railties/databases.rake" end + # When loading console, force ActiveRecord to be loaded to avoid cross + # references when loading a constant for the first time. + console do + ActiveRecord::Base + end + initializer "active_record.initialize_timezone" do ActiveSupport.on_load(:active_record) do self.time_zone_aware_attributes = true 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 e210895ba95e498b9debbf43a3e5ae588bca81f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 18 Jul 2010 11:01:32 +0200 Subject: Avoid uneeded queries in session stores if sid is not given. --- .../lib/action_dispatch/middleware/session/abstract_store.rb | 7 +++++-- .../lib/action_dispatch/middleware/session/mem_cache_store.rb | 1 - activerecord/lib/active_record/session_store.rb | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 64f4d1d532..08c969c449 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -191,8 +191,11 @@ module ActionDispatch def load_session(env) stale_session_check! do - sid = current_session_id(env) - sid, session = get_session(env, sid) + if sid = current_session_id(env) + sid, session = get_session(env, sid) + else + sid, session = generate_sid, {} + end [sid, session] end end diff --git a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb index 28e3dbd732..5304440418 100644 --- a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb @@ -25,7 +25,6 @@ module ActionDispatch private def get_session(env, sid) - sid ||= generate_sid begin session = @pool.get(sid) || {} rescue MemCache::MemCacheError, Errno::ECONNREFUSED diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index df2f429c5d..7ea7fb5c51 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -294,7 +294,6 @@ module ActiveRecord private def get_session(env, sid) Base.silence do - sid ||= generate_sid session = find_session(sid) env[SESSION_RECORD_KEY] = session [sid, session.data] -- cgit v1.2.3 From 4a0d7c1a439c6ad8d35bf514761824e51fa07df2 Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Wed, 14 Jul 2010 04:39:54 -0400 Subject: save on parent should not cascade to child unless child changed [#3353 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/autosave_association.rb | 4 +++- activerecord/test/cases/autosave_association_test.rb | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 7517896235..6af384367f 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -372,7 +372,9 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy elsif autosave != false - saved = association.save(:validate => !autosave) if association.new_record? || autosave + if association.new_record? || ( autosave && association.changed? ) + saved = association.save(:validate => !autosave) + end if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 3b89c12a3f..48479bb429 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -632,6 +632,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent + #association save method only trigged when association is changed + @ship.pirate.catchphrase = "new catch phrase" # Stub the save method of the @ship.pirate instance to destroy and then raise an exception class << @ship.pirate def save(*args) @@ -880,6 +882,22 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?") end + def test_should_not_call_belongs_to_after_save_callbacks_if_no_changes + @ship.attributes = { :name => "Titanic", :pirate_attributes => {:id => @pirate.id} } + #here there are no changes to pirate so if save on ship causes save on pirate + #this callback will fail pirate save.(pirate save shouldn't happen) + @ship.pirate.cancel_save_from_callback = true + @ship.save + assert_equal 'Titanic', @ship.reload.name + end + + def test_should_call_belongs_to_save_if_belongs_to_has_changes + @ship.attributes = { :name => "Titanic", :pirate_attributes => { :catchphrase => 'Jack', :id => @pirate.id} } + @ship.save + assert_equal 'Titanic', @ship.reload.name + assert_equal 'Jack', @pirate.reload.catchphrase + end + def test_should_still_work_without_an_associated_model @pirate.destroy @ship.reload.name = "The Vile Insanity" -- cgit v1.2.3 From 1f499e6d4cb1055de952957e3c9bd770e0219cc1 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 17 Jul 2010 16:08:17 -0400 Subject: fixing the ternary operation where the logic is very confusing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5136 state:resolved] Signed-off-by: José Valim --- actionpack/lib/action_view/helpers/date_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index f097b9a5a3..8050669adb 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -800,7 +800,8 @@ module ActionView start = options.delete(:start) || 0 stop = options.delete(:end) || 59 step = options.delete(:step) || 1 - leading_zeros = options.delete(:leading_zeros).nil? ? true : false + options.reverse_merge!({:leading_zeros => true}) + leading_zeros = options.delete(:leading_zeros) select_options = [] start.step(stop, step) do |i| -- cgit v1.2.3 From cdfa11409c6196d35e890cf1766e1e2cc6f3d7d7 Mon Sep 17 00:00:00 2001 From: Ivan Torres Date: Wed, 14 Jul 2010 01:23:41 -0500 Subject: select :include_blank or :prompt should return escaped string [#5099 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_view/helpers/form_options_helper.rb | 6 +++--- .../test/template/form_options_helper_test.rb | 23 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 6f9d14de8b..ee34452769 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -447,7 +447,7 @@ module ActionView # wrap the output in an appropriate \n\n\n", + select("post", "category", %w( abe hest), :include_blank => '') + ) + end + def test_select_with_default_prompt @post = Post.new @post.category = "" @@ -394,6 +409,14 @@ class FormOptionsHelperTest < ActionView::TestCase ) end + def test_select_with_given_prompt_escaped + @post = Post.new + assert_dom_equal( + "", + select("post", "category", %w( abe hest), :prompt => '') + ) + end + def test_select_with_prompt_and_blank @post = Post.new @post.category = "" -- cgit v1.2.3 From 291adcd588e86746145e4ba9ab2ea4d0de26279f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 18 Jul 2010 12:51:03 +0200 Subject: Set session options id to nil is respected and cancels lazy loading. --- .../middleware/session/abstract_store.rb | 4 ++-- .../test/dispatch/session/cookie_store_test.rb | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 08c969c449..ad98249468 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -24,9 +24,9 @@ module ActionDispatch def [](key) if key == :id - load_session_id! unless super(:id) || has_session_id? + load_session_id! unless key?(:id) || has_session_id? end - super(key) + super end private diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index f0e01bfff0..3864821ef0 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -44,7 +44,12 @@ class CookieStoreTest < ActionController::IntegrationTest session[:foo] = 'bye!' * 1024 head :ok end - + + def change_session_id + request.session_options[:id] = nil + get_session_id + end + def rescue_action(e) raise end end @@ -212,6 +217,19 @@ class CookieStoreTest < ActionController::IntegrationTest end end + def test_setting_session_id_to_nil_is_respected + with_test_route_set do + cookies[SessionKey] = SignedBar + + get "/get_session_id" + sid = response.body + assert_equal sid.size, 36 + + get "/change_session_id" + assert_not_equal sid, response.body + end + end + def test_session_store_with_expire_after with_test_route_set(:expire_after => 5.hours) do # First request accesses the session -- cgit v1.2.3 From df6aa8e246bed1961ee042aa9ab8a5209e2ce7f3 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 16 Jul 2010 22:18:50 -0400 Subject: removing extra whitespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/aggregations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb index 74588b4f47..9e285e57dc 100644 --- a/activerecord/test/cases/aggregations_test.rb +++ b/activerecord/test/cases/aggregations_test.rb @@ -125,7 +125,7 @@ class OverridingAggregationsTest < ActiveRecord::TestCase class Name; end class DifferentName; end - class Person < ActiveRecord::Base + class Person < ActiveRecord::Base composed_of :composed_of, :mapping => %w(person_first_name first_name) end -- cgit v1.2.3 From b58e1c52f75130a237b53c0e488341832487dd54 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 16 Jul 2010 22:19:29 -0400 Subject: fixing typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/ar_schema_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 665c387d5d..588adc38e3 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -28,7 +28,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal 7, ActiveRecord::Migrator::current_version end - def test_schema_raises_an_error_for_invalid_column_ntype + def test_schema_raises_an_error_for_invalid_column_type assert_raise NoMethodError do ActiveRecord::Schema.define(:version => 8) do create_table :vegetables do |t| -- cgit v1.2.3 From 6caf943ace63c5d53ad95f4835ea58caea7eb22e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 16 Jul 2010 22:21:05 -0400 Subject: primary_keys_test reads better than pk_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/pk_test.rb | 139 --------------------------- activerecord/test/cases/primary_keys_test.rb | 139 +++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 139 deletions(-) delete mode 100644 activerecord/test/cases/pk_test.rb create mode 100644 activerecord/test/cases/primary_keys_test.rb diff --git a/activerecord/test/cases/pk_test.rb b/activerecord/test/cases/pk_test.rb deleted file mode 100644 index 73f4b3848c..0000000000 --- a/activerecord/test/cases/pk_test.rb +++ /dev/null @@ -1,139 +0,0 @@ -require "cases/helper" -require 'models/topic' -require 'models/reply' -require 'models/subscriber' -require 'models/movie' -require 'models/keyboard' -require 'models/mixed_case_monkey' - -class PrimaryKeysTest < ActiveRecord::TestCase - fixtures :topics, :subscribers, :movies, :mixed_case_monkeys - - def test_to_key_with_default_primary_key - topic = Topic.new - assert topic.to_key.nil? - topic = Topic.find(1) - assert_equal topic.to_key, [1] - end - - def test_to_key_with_customized_primary_key - keyboard = Keyboard.new - assert_nil keyboard.to_key - keyboard.save - assert_equal keyboard.to_key, [keyboard.id] - end - - def test_to_key_with_primary_key_after_destroy - topic = Topic.find(1) - topic.destroy - assert_equal topic.to_key, [1] - end - - def test_integer_key - topic = Topic.find(1) - assert_equal(topics(:first).author_name, topic.author_name) - topic = Topic.find(2) - assert_equal(topics(:second).author_name, topic.author_name) - - topic = Topic.new - topic.title = "New Topic" - assert_nil topic.id - assert_nothing_raised { topic.save! } - id = topic.id - - topicReloaded = Topic.find(id) - assert_equal("New Topic", topicReloaded.title) - end - - def test_customized_primary_key_auto_assigns_on_save - Keyboard.delete_all - keyboard = Keyboard.new(:name => 'HHKB') - assert_nothing_raised { keyboard.save! } - assert_equal keyboard.id, Keyboard.find_by_name('HHKB').id - end - - def test_customized_primary_key_can_be_get_before_saving - keyboard = Keyboard.new - assert_nil keyboard.id - assert_nothing_raised { assert_nil keyboard.key_number } - end - - def test_customized_string_primary_key_settable_before_save - subscriber = Subscriber.new - assert_nothing_raised { subscriber.id = 'webster123' } - assert_equal 'webster123', subscriber.id - assert_equal 'webster123', subscriber.nick - end - - def test_string_key - subscriber = Subscriber.find(subscribers(:first).nick) - assert_equal(subscribers(:first).name, subscriber.name) - subscriber = Subscriber.find(subscribers(:second).nick) - assert_equal(subscribers(:second).name, subscriber.name) - - subscriber = Subscriber.new - subscriber.id = "jdoe" - assert_equal("jdoe", subscriber.id) - subscriber.name = "John Doe" - assert_nothing_raised { subscriber.save! } - assert_equal("jdoe", subscriber.id) - - subscriberReloaded = Subscriber.find("jdoe") - assert_equal("John Doe", subscriberReloaded.name) - end - - def test_find_with_more_than_one_string_key - assert_equal 2, Subscriber.find(subscribers(:first).nick, subscribers(:second).nick).length - end - - def test_primary_key_prefix - ActiveRecord::Base.primary_key_prefix_type = :table_name - Topic.reset_primary_key - assert_equal "topicid", Topic.primary_key - - ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore - Topic.reset_primary_key - assert_equal "topic_id", Topic.primary_key - - ActiveRecord::Base.primary_key_prefix_type = nil - Topic.reset_primary_key - assert_equal "id", Topic.primary_key - end - - def test_delete_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.delete(1) } - end - def test_update_counters_should_quote_pkey_and_quote_counter_columns - assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) } - end - def test_find_with_one_id_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.find(1) } - end - def test_find_with_multiple_ids_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.find([1,2]) } - end - def test_instance_update_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.find(1).save } - end - def test_instance_destroy_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.find(1).destroy } - end - - def test_supports_primary_key - assert_nothing_raised NoMethodError do - ActiveRecord::Base.connection.supports_primary_key? - end - end - - def test_primary_key_returns_value_if_it_exists - if ActiveRecord::Base.connection.supports_primary_key? - assert_equal 'id', ActiveRecord::Base.connection.primary_key('developers') - end - end - - def test_primary_key_returns_nil_if_it_does_not_exist - if ActiveRecord::Base.connection.supports_primary_key? - assert_nil ActiveRecord::Base.connection.primary_key('developers_projects') - end - end -end diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb new file mode 100644 index 0000000000..73f4b3848c --- /dev/null +++ b/activerecord/test/cases/primary_keys_test.rb @@ -0,0 +1,139 @@ +require "cases/helper" +require 'models/topic' +require 'models/reply' +require 'models/subscriber' +require 'models/movie' +require 'models/keyboard' +require 'models/mixed_case_monkey' + +class PrimaryKeysTest < ActiveRecord::TestCase + fixtures :topics, :subscribers, :movies, :mixed_case_monkeys + + def test_to_key_with_default_primary_key + topic = Topic.new + assert topic.to_key.nil? + topic = Topic.find(1) + assert_equal topic.to_key, [1] + end + + def test_to_key_with_customized_primary_key + keyboard = Keyboard.new + assert_nil keyboard.to_key + keyboard.save + assert_equal keyboard.to_key, [keyboard.id] + end + + def test_to_key_with_primary_key_after_destroy + topic = Topic.find(1) + topic.destroy + assert_equal topic.to_key, [1] + end + + def test_integer_key + topic = Topic.find(1) + assert_equal(topics(:first).author_name, topic.author_name) + topic = Topic.find(2) + assert_equal(topics(:second).author_name, topic.author_name) + + topic = Topic.new + topic.title = "New Topic" + assert_nil topic.id + assert_nothing_raised { topic.save! } + id = topic.id + + topicReloaded = Topic.find(id) + assert_equal("New Topic", topicReloaded.title) + end + + def test_customized_primary_key_auto_assigns_on_save + Keyboard.delete_all + keyboard = Keyboard.new(:name => 'HHKB') + assert_nothing_raised { keyboard.save! } + assert_equal keyboard.id, Keyboard.find_by_name('HHKB').id + end + + def test_customized_primary_key_can_be_get_before_saving + keyboard = Keyboard.new + assert_nil keyboard.id + assert_nothing_raised { assert_nil keyboard.key_number } + end + + def test_customized_string_primary_key_settable_before_save + subscriber = Subscriber.new + assert_nothing_raised { subscriber.id = 'webster123' } + assert_equal 'webster123', subscriber.id + assert_equal 'webster123', subscriber.nick + end + + def test_string_key + subscriber = Subscriber.find(subscribers(:first).nick) + assert_equal(subscribers(:first).name, subscriber.name) + subscriber = Subscriber.find(subscribers(:second).nick) + assert_equal(subscribers(:second).name, subscriber.name) + + subscriber = Subscriber.new + subscriber.id = "jdoe" + assert_equal("jdoe", subscriber.id) + subscriber.name = "John Doe" + assert_nothing_raised { subscriber.save! } + assert_equal("jdoe", subscriber.id) + + subscriberReloaded = Subscriber.find("jdoe") + assert_equal("John Doe", subscriberReloaded.name) + end + + def test_find_with_more_than_one_string_key + assert_equal 2, Subscriber.find(subscribers(:first).nick, subscribers(:second).nick).length + end + + def test_primary_key_prefix + ActiveRecord::Base.primary_key_prefix_type = :table_name + Topic.reset_primary_key + assert_equal "topicid", Topic.primary_key + + ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore + Topic.reset_primary_key + assert_equal "topic_id", Topic.primary_key + + ActiveRecord::Base.primary_key_prefix_type = nil + Topic.reset_primary_key + assert_equal "id", Topic.primary_key + end + + def test_delete_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.delete(1) } + end + def test_update_counters_should_quote_pkey_and_quote_counter_columns + assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) } + end + def test_find_with_one_id_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.find(1) } + end + def test_find_with_multiple_ids_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.find([1,2]) } + end + def test_instance_update_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.find(1).save } + end + def test_instance_destroy_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.find(1).destroy } + end + + def test_supports_primary_key + assert_nothing_raised NoMethodError do + ActiveRecord::Base.connection.supports_primary_key? + end + end + + def test_primary_key_returns_value_if_it_exists + if ActiveRecord::Base.connection.supports_primary_key? + assert_equal 'id', ActiveRecord::Base.connection.primary_key('developers') + end + end + + def test_primary_key_returns_nil_if_it_does_not_exist + if ActiveRecord::Base.connection.supports_primary_key? + assert_nil ActiveRecord::Base.connection.primary_key('developers_projects') + end + end +end -- cgit v1.2.3 From 0fce4ae57fa356e9eca7059a4ff6a67e68c37961 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 16 Jul 2010 22:24:18 -0400 Subject: expected value should come first in assert_equal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/primary_keys_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 73f4b3848c..1e44237e0a 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -13,7 +13,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase topic = Topic.new assert topic.to_key.nil? topic = Topic.find(1) - assert_equal topic.to_key, [1] + assert_equal [1], topic.to_key end def test_to_key_with_customized_primary_key @@ -26,7 +26,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase def test_to_key_with_primary_key_after_destroy topic = Topic.find(1) topic.destroy - assert_equal topic.to_key, [1] + assert_equal [1], topic.to_key end def test_integer_key -- cgit v1.2.3 From 387036609268c4cf68401a1333715f2ee4aecffc Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 18 Jul 2010 14:19:34 -0300 Subject: Float comparison adjustment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/test/notifications_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 3e16e01d89..73c85be87c 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -200,7 +200,7 @@ module Notifications assert_equal :foo, event.name assert_equal time, event.time - assert_in_delta 10.0, event.duration, 0.00000000000001 + assert_in_delta 10.0, event.duration, 0.00001 end def test_events_consumes_information_given_as_payload -- cgit v1.2.3 From 2cbef6996c41c785a626291ce29b934797359fd2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 18 Jul 2010 15:37:23 -0700 Subject: bind method is not needed, so goodbye! <3 <3 <3 --- activesupport/lib/active_support/notifications.rb | 2 +- activesupport/lib/active_support/notifications/fanout.rb | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 1444fc1609..bb5f497c83 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -65,7 +65,7 @@ module ActiveSupport end def subscribe(pattern = nil, &block) - @queue.bind(pattern).subscribe(&block) + @queue.subscribe(pattern, &block) end def unsubscribe(subscriber) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 7eefb7f20a..48ccb156ae 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -6,15 +6,9 @@ module ActiveSupport def initialize @subscribers = [] @listeners_for = {} - @pattern = nil end - def bind(pattern) - @pattern = pattern - self - end - - def subscribe(pattern = @pattern, &block) + def subscribe(pattern = nil, &block) @listeners_for.clear @subscribers << Subscriber.new(pattern, &block) @subscribers.last -- cgit v1.2.3 From 234b9699463ba435086aa253ee143014a835bbe6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 18 Jul 2010 15:39:32 -0700 Subject: tap the subscriber for easier return value --- activesupport/lib/active_support/notifications/fanout.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 48ccb156ae..6e4ff40dc3 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -10,8 +10,9 @@ module ActiveSupport def subscribe(pattern = nil, &block) @listeners_for.clear - @subscribers << Subscriber.new(pattern, &block) - @subscribers.last + Subscriber.new(pattern, &block).tap do |s| + @subscribers << s + end end def unsubscribe(subscriber) -- cgit v1.2.3 From b2c8a5fd3ea2268022915bb8a7ab449a1502b90d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 18 Jul 2010 16:49:29 -0700 Subject: Notifier API == Fanout API, so replace Notifier with Fanout as they quack the same --- .../active_support/log_subscriber/test_helper.rb | 8 ++------ activesupport/lib/active_support/notifications.rb | 24 +--------------------- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index 96506a4b2b..a3fb92778b 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -33,7 +33,7 @@ module ActiveSupport module TestHelper def setup @logger = MockLogger.new - @notifier = ActiveSupport::Notifications::Notifier.new(queue) + @notifier = ActiveSupport::Notifications::Fanout.new ActiveSupport::LogSubscriber.colorize_logging = false @@ -81,10 +81,6 @@ module ActiveSupport def set_logger(logger) ActiveSupport::LogSubscriber.logger = logger end - - def queue - ActiveSupport::Notifications::Fanout.new - end end end -end \ No newline at end of file +end diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index bb5f497c83..93d1907edc 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -47,34 +47,12 @@ module ActiveSupport delegate :instrument, :to => :instrumenter def notifier - @notifier ||= Notifier.new + @notifier ||= Fanout.new end def instrumenter Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier) end end - - class Notifier - def initialize(queue = Fanout.new) - @queue = queue - end - - def publish(*args) - @queue.publish(*args) - end - - def subscribe(pattern = nil, &block) - @queue.subscribe(pattern, &block) - end - - def unsubscribe(subscriber) - @queue.unsubscribe(subscriber) - end - - def wait - @queue.wait - end - end end end -- cgit v1.2.3 From 8cbb89c0bf33e6daad3f91c6debd283b979d800c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 18 Jul 2010 17:20:20 -0700 Subject: subscriber does not need to be a block, but an object that responds to #call --- activesupport/lib/active_support/notifications/fanout.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 6e4ff40dc3..3bbb9cca44 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -8,9 +8,9 @@ module ActiveSupport @listeners_for = {} end - def subscribe(pattern = nil, &block) + def subscribe(pattern = nil, block = Proc.new) @listeners_for.clear - Subscriber.new(pattern, &block).tap do |s| + Subscriber.new(pattern, block).tap do |s| @subscribers << s end end @@ -33,14 +33,14 @@ module ActiveSupport end class Subscriber #:nodoc: - def initialize(pattern, &block) + def initialize(pattern, delegate) @pattern = pattern - @block = block + @delegate = delegate end def publish(*args) return unless subscribed_to?(args.first) - @block.call(*args) + @delegate.call(*args) true end -- cgit v1.2.3 From ad8f4dfc50fe3858b80aeceb6d9240d2af4a2fea Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 18 Jul 2010 17:37:39 -0700 Subject: avoid proc activation every time a log message is made --- activesupport/lib/active_support/log_subscriber.rb | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 891d718af3..7611aff964 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -63,15 +63,9 @@ module ActiveSupport @@flushable_loggers = nil log_subscriber.public_methods(false).each do |event| - notifier.subscribe("#{event}.#{namespace}") do |*args| - next if log_subscriber.logger.nil? - - begin - log_subscriber.send(event, ActiveSupport::Notifications::Event.new(*args)) - rescue Exception => e - log_subscriber.logger.error "Could not log #{args[0].inspect} event. #{e.class}: #{e.message}" - end - end + next if 'call' == event.to_s + + notifier.subscribe("#{event}.#{namespace}", log_subscriber) end end @@ -92,6 +86,17 @@ module ActiveSupport flushable_loggers.each(&:flush) end + def call(message, *args) + return unless logger + + method = message.split('.').first + begin + send(method, ActiveSupport::Notifications::Event.new(message, *args)) + rescue Exception => e + logger.error "Could not log #{message.inspect} event. #{e.class}: #{e.message}" + end + end + protected %w(info debug warn error fatal unknown).each do |level| -- cgit v1.2.3 From 33c5689e2d04aa08759903bc5d1e4de3bf6c35dd Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 8 Jun 2010 11:59:09 -0400 Subject: Exceptions from views should be rescued based on the original exception. If a handler for original exception is missing then apply ActiveView::TemplateError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#2034 state:resolved] Signed-off-by: José Valim --- actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/base.rb | 6 +++-- .../action_controller/metal/rescue_with_helper.rb | 14 +++++++++++ actionpack/test/controller/rescue_test.rb | 27 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 actionpack/lib/action_controller/metal/rescue_with_helper.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 1bd4572a47..1b23a8e916 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -28,6 +28,7 @@ module ActionController autoload :Rendering autoload :RequestForgeryProtection autoload :Rescue + autoload :RescueWithHelper autoload :Responder autoload :SessionManagement autoload :Streaming diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 1a2cbaab65..9616aa1639 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -49,7 +49,9 @@ module ActionController AbstractController::Callbacks, # The same with rescue, append it at the end to wrap as much as possible. - Rescue + Rescue, + + RescueWithHelper ] MODULES.each do |mod| @@ -69,4 +71,4 @@ module ActionController end end -require "action_controller/deprecated/base" \ No newline at end of file +require "action_controller/deprecated/base" diff --git a/actionpack/lib/action_controller/metal/rescue_with_helper.rb b/actionpack/lib/action_controller/metal/rescue_with_helper.rb new file mode 100644 index 0000000000..7894deaeef --- /dev/null +++ b/actionpack/lib/action_controller/metal/rescue_with_helper.rb @@ -0,0 +1,14 @@ +module ActionController #:nodoc: + module RescueWithHelper + + def rescue_with_handler(exception) + if ((exception.class == ActionView::TemplateError) && + (orig_exception = exception.original_exception) && + (orig_handler = handler_for_rescue(orig_exception))) + exception = orig_exception + end + super(exception) + end + + end +end diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index 0f64b77647..a24de62b19 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -79,6 +79,14 @@ class RescueController < ActionController::Base render :text => 'no way' end + rescue_from ActionView::TemplateError do + render :text => 'action_view templater error' + end + + rescue_from IOError do + render :text => 'io error' + end + before_filter(:only => :before_filter_raises) { raise 'umm nice' } def before_filter_raises @@ -141,6 +149,14 @@ class RescueController < ActionController::Base def missing_template end + + def io_error_in_view + raise ActionView::TemplateError.new(nil, {}, IOError.new('this is io error')) + end + + def zero_division_error_in_view + raise ActionView::TemplateError.new(nil, {}, ZeroDivisionError.new('this is zero division error')) + end protected def deny_access @@ -228,6 +244,17 @@ class ControllerInheritanceRescueControllerTest < ActionController::TestCase end class RescueControllerTest < ActionController::TestCase + + def test_io_error_in_view + get :io_error_in_view + assert_equal 'io error', @response.body + end + + def test_zero_division_error_in_view + get :zero_division_error_in_view + assert_equal 'action_view templater error', @response.body + end + def test_rescue_handler get :not_authorized assert_response :forbidden -- cgit v1.2.3 From ab6ff859067b35bda62e9a92fcd710cf925ba2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 19 Jul 2010 14:49:20 +0200 Subject: No need to create a new module in the previous commit. --- actionpack/lib/action_controller.rb | 1 - actionpack/lib/action_controller/base.rb | 4 +--- actionpack/lib/action_controller/metal/rescue.rb | 9 +++++++++ .../lib/action_controller/metal/rescue_with_helper.rb | 14 -------------- 4 files changed, 10 insertions(+), 18 deletions(-) delete mode 100644 actionpack/lib/action_controller/metal/rescue_with_helper.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 1b23a8e916..1bd4572a47 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -28,7 +28,6 @@ module ActionController autoload :Rendering autoload :RequestForgeryProtection autoload :Rescue - autoload :RescueWithHelper autoload :Responder autoload :SessionManagement autoload :Streaming diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 9616aa1639..cfe6b30add 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -49,9 +49,7 @@ module ActionController AbstractController::Callbacks, # The same with rescue, append it at the end to wrap as much as possible. - Rescue, - - RescueWithHelper + Rescue ] MODULES.each do |mod| diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb index bbca1b2179..cc2b020e03 100644 --- a/actionpack/lib/action_controller/metal/rescue.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -3,6 +3,15 @@ module ActionController #:nodoc: extend ActiveSupport::Concern include ActiveSupport::Rescuable + def rescue_with_handler(exception) + if ((exception.respond_to?(:original_exception)) && + (orig_exception = exception.original_exception) && + (orig_handler = handler_for_rescue(orig_exception))) + exception = orig_exception + end + super(exception) + end + private def process_action(*args) super diff --git a/actionpack/lib/action_controller/metal/rescue_with_helper.rb b/actionpack/lib/action_controller/metal/rescue_with_helper.rb deleted file mode 100644 index 7894deaeef..0000000000 --- a/actionpack/lib/action_controller/metal/rescue_with_helper.rb +++ /dev/null @@ -1,14 +0,0 @@ -module ActionController #:nodoc: - module RescueWithHelper - - def rescue_with_handler(exception) - if ((exception.class == ActionView::TemplateError) && - (orig_exception = exception.original_exception) && - (orig_handler = handler_for_rescue(orig_exception))) - exception = orig_exception - end - super(exception) - end - - end -end -- cgit v1.2.3 From 70f7ba3e3bf10e1a646ef6ace09da7b2918ba75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 19 Jul 2010 14:56:09 +0200 Subject: There is absolutely no need to use __send__ here. --- actionpack/lib/action_controller/polymorphic_routes.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index bee50a7a3b..b4837c610a 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -110,7 +110,7 @@ module ActionController args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options end - __send__(named_route, *args) + send(named_route, *args) end # Returns the path component of a URL for the given record. It uses @@ -154,7 +154,7 @@ module ActionController if parent.is_a?(Symbol) || parent.is_a?(String) string << "#{parent}_" else - string << RecordIdentifier.__send__("plural_class_name", parent).singularize + string << RecordIdentifier.plural_class_name(parent).singularize string << "_" end end @@ -163,7 +163,7 @@ module ActionController if record.is_a?(Symbol) || record.is_a?(String) route << "#{record}_" else - route << RecordIdentifier.__send__("plural_class_name", record) + route << RecordIdentifier.plural_class_name(record) route = route.singularize if inflection == :singular route << "_" route << "index_" if RecordIdentifier.uncountable?(record) && inflection == :plural -- 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. --- actionpack/CHANGELOG | 2 ++ activemodel/CHANGELOG | 5 +++++ activerecord/CHANGELOG | 2 ++ railties/CHANGELOG | 14 ++++++++------ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 32aba2091a..6e07516a58 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* +* Allow stylesheet/javascript extensions to be changed through railties. [Josh Kalderimis] + * link_to, button_to, and tag/tag_options now rely on html_escape instead of escape_once. [fxn] * url_for returns always unescaped strings, and the :escape option is gone. [fxn] diff --git a/activemodel/CHANGELOG b/activemodel/CHANGELOG index a5e7f300d9..33602657f5 100644 --- a/activemodel/CHANGELOG +++ b/activemodel/CHANGELOG @@ -1,3 +1,8 @@ +*Rails 3.0.0 [Release Candidate] (unreleased)* + +* Added ActiveModel::MassAssignmentSecurity [Eric Chapweske, Josh Kalderimis] + + *Rails 3.0.0 [beta 4] (June 8th, 2010)* * JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh] diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index a1a82fdff5..679fdafae8 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [RC1] (unreleased)* +* Changed update_attribute to not run callbacks and update the record directly in the database [Neeraj Singh] + * Add scoping and unscoped as the syntax to replace the old with_scope and with_exclusive_scope [José Valim] * New rake task, db:migrate:status, displays status of migrations #4947 [Kevin Skoglund] 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 247886e1b4256aeebc6b5fde0549400240b04e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Thu, 15 Jul 2010 03:43:59 +0800 Subject: Removed warnings when a variable is shadowed --- activerecord/lib/active_record/association_preload.rb | 4 ++-- .../lib/active_record/associations/association_collection.rb | 6 +++--- activerecord/test/cases/associations/inverse_associations_test.rb | 4 ++-- activerecord/test/cases/base_test.rb | 4 ++-- activerecord/test/cases/fixtures_test.rb | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index cbec5789fd..9172ab2a20 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -112,13 +112,13 @@ module ActiveRecord # Not all records have the same class, so group then preload # group on the reflection itself so that if various subclass share the same association then we do not split them # unnecessarily - records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records| + records.group_by { |record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, _records| raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection # 'reflection.macro' can return 'belongs_to', 'has_many', etc. Thus, # the following could call 'preload_belongs_to_association', # 'preload_has_many_association', etc. - send("preload_#{reflection.macro}_association", records, reflection, preload_options) + send("preload_#{reflection.macro}_association", _records, reflection, preload_options) end end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index f4e34657a5..7abb738a74 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -218,9 +218,9 @@ module ActiveRecord # are actually removed from the database, that depends precisely on # +delete_records+. They are in any case removed from the collection. def delete(*records) - remove_records(records) do |records, old_records| + remove_records(records) do |_records, old_records| delete_records(old_records) if old_records.any? - records.each { |record| @target.delete(record) } + _records.each { |record| @target.delete(record) } end end @@ -231,7 +231,7 @@ module ActiveRecord # ignoring the +:dependent+ option. def destroy(*records) records = find(records) if records.any? {|record| record.kind_of?(Fixnum) || record.kind_of?(String)} - remove_records(records) do |records, old_records| + remove_records(records) do |_records, old_records| old_records.each { |record| record.destroy } end diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index 34d24a2948..fa5c2e49df 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -412,7 +412,7 @@ class InverseBelongsToTests < ActiveRecord::TestCase i = interests(:trainspotting) m = i.man assert_not_nil m.interests - iz = m.interests.detect {|iz| iz.id == i.id} + iz = m.interests.detect { |_iz| _iz.id == i.id} assert_not_nil iz assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child" i.topic = 'Eating cheese with a spoon' @@ -516,7 +516,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase i = interests(:llama_wrangling) m = i.polymorphic_man assert_not_nil m.polymorphic_interests - iz = m.polymorphic_interests.detect {|iz| iz.id == i.id} + iz = m.polymorphic_interests.detect { |_iz| _iz.id == i.id} assert_not_nil iz assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child" i.topic = 'Eating cheese with a spoon' diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 831dd446ad..709e22a648 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1816,8 +1816,8 @@ class BasicsTest < ActiveRecord::TestCase def test_to_xml_with_block value = "Rockin' the block" - xml = Company.new.to_xml(:skip_instruct => true) do |xml| - xml.tag! "arbitrary-element", value + xml = Company.new.to_xml(:skip_instruct => true) do |_xml| + _xml.tag! "arbitrary-element", value end assert_equal "", xml.first(9) assert xml.include?(%(#{value})) diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 8008b86f81..93f8749255 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -36,7 +36,7 @@ class FixturesTest < ActiveRecord::TestCase fixtures = nil assert_nothing_raised { fixtures = create_fixtures(name) } assert_kind_of(Fixtures, fixtures) - fixtures.each { |name, fixture| + fixtures.each { |_name, fixture| fixture.each { |key, value| assert_match(MATCH_ATTRIBUTE_NAME, key) } @@ -229,9 +229,9 @@ if Account.connection.respond_to?(:reset_pk_sequence!) def test_create_fixtures_resets_sequences_when_not_cached @instances.each do |instance| - max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)| + max_id = create_fixtures(instance.class.table_name).inject(0) do |_max_id, (name, fixture)| fixture_id = fixture['id'].to_i - fixture_id > max_id ? fixture_id : max_id + fixture_id > _max_id ? fixture_id : _max_id end # Clone the last fixture to check that it gets the next greatest id. -- 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 --- .../core_ext/module/remove_method.rb | 5 +++++ .../source/active_support_core_extensions.textile | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/activesupport/lib/active_support/core_ext/module/remove_method.rb b/activesupport/lib/active_support/core_ext/module/remove_method.rb index 2714a46b28..b8c01aca0e 100644 --- a/activesupport/lib/active_support/core_ext/module/remove_method.rb +++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb @@ -3,4 +3,9 @@ class Module remove_method(method) rescue NameError end + + def redefine_method(method, &block) + remove_possible_method(method) + define_method(method, &block) + end end \ No newline at end of file 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 010eda2eb516d237a7b5cf022b7dd2213ff77bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Sat, 17 Jul 2010 03:21:14 +0800 Subject: Removed warnings about method redefining --- activerecord/lib/active_record/associations.rb | 33 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d67df64f59..a9d256a771 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1354,6 +1354,7 @@ module ActiveRecord end def association_accessor_methods(reflection, association_proxy_class) + remove_possible_method(reflection.name) define_method(reflection.name) do |*params| force_reload = params.first unless params.empty? association = association_instance_get(reflection.name) @@ -1371,12 +1372,16 @@ module ActiveRecord association.target.nil? ? nil : association end - define_method("loaded_#{reflection.name}?") do + method = "loaded_#{reflection.name}?" + remove_possible_method(method) + define_method(method) do association = association_instance_get(reflection.name) association && association.loaded? end - define_method("#{reflection.name}=") do |new_value| + method = "#{reflection.name}=" + remove_possible_method(method) + define_method(method) do |new_value| association = association_instance_get(reflection.name) if association.nil? || association.target != new_value @@ -1386,8 +1391,10 @@ module ActiveRecord association.replace(new_value) association_instance_set(reflection.name, new_value.nil? ? nil : association) end - - define_method("set_#{reflection.name}_target") do |target| + + method = "set_#{reflection.name}_target" + remove_possible_method(method) + define_method(method) do |target| return if target.nil? and association_proxy_class == BelongsToAssociation association = association_proxy_class.new(self, reflection) association.target = target @@ -1396,6 +1403,7 @@ module ActiveRecord end def collection_reader_method(reflection, association_proxy_class) + remove_possible_method(reflection.name) define_method(reflection.name) do |*params| force_reload = params.first unless params.empty? association = association_instance_get(reflection.name) @@ -1409,8 +1417,10 @@ module ActiveRecord association end - - define_method("#{reflection.name.to_s.singularize}_ids") do + + method = "#{reflection.name.to_s.singularize}_ids" + remove_possible_method(method) + define_method(method) do if send(reflection.name).loaded? || reflection.options[:finder_sql] send(reflection.name).map(&:id) else @@ -1436,8 +1446,10 @@ module ActiveRecord association.replace(new_value) association end - - define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| + + method = "#{reflection.name.to_s.singularize}_ids=" + remove_possible_method(method) + define_method(method) do |new_value| ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i) send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids)) end @@ -1445,7 +1457,9 @@ module ActiveRecord end def association_constructor_method(constructor, reflection, association_proxy_class) - define_method("#{constructor}_#{reflection.name}") do |*params| + method = "#{constructor}_#{reflection.name}" + remove_possible_method(method) + define_method(method) do |*params| attributees = params.first unless params.empty? replace_existing = params[1].nil? ? true : params[1] association = association_instance_get(reflection.name) @@ -1487,6 +1501,7 @@ module ActiveRecord def add_touch_callbacks(reflection, touch_attribute) method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym + remove_possible_method(method_name) define_method(method_name) do association = send(reflection.name) -- cgit v1.2.3 From 7637b7184a990ae51f3ab10e2af99ff88cb633ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Thu, 15 Jul 2010 04:40:17 +0800 Subject: Test for behaviour of befeore_type_cast when operating on datetime colmun --- activerecord/test/cases/base_test.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 709e22a648..df6895f0d0 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -123,6 +123,14 @@ class BasicsTest < ActiveRecord::TestCase assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] else assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) + + developer.created_at = "345643456" + assert_equal developer.created_at_before_type_cast, "345643456" + assert_equal developer.created_at, nil + + developer.created_at = "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") end end -- cgit v1.2.3 From 661fd98aad027b6171253828ed89115f30fac46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Sat, 17 Jul 2010 17:42:17 +0800 Subject: Make use of redefine_method, removed some more redefining warnings --- activerecord/lib/active_record/associations.rb | 39 +++++++--------------- activerecord/lib/active_record/fixtures.rb | 2 +- activerecord/lib/active_record/named_scope.rb | 2 +- .../cases/adapters/mysql/active_schema_test.rb | 1 + 4 files changed, 15 insertions(+), 29 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a9d256a771..7cddb9c601 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1354,8 +1354,7 @@ module ActiveRecord end def association_accessor_methods(reflection, association_proxy_class) - remove_possible_method(reflection.name) - define_method(reflection.name) do |*params| + redefine_method(reflection.name) do |*params| force_reload = params.first unless params.empty? association = association_instance_get(reflection.name) @@ -1372,16 +1371,12 @@ module ActiveRecord association.target.nil? ? nil : association end - method = "loaded_#{reflection.name}?" - remove_possible_method(method) - define_method(method) do + redefine_method("loaded_#{reflection.name}?") do association = association_instance_get(reflection.name) association && association.loaded? end - - method = "#{reflection.name}=" - remove_possible_method(method) - define_method(method) do |new_value| + + redefine_method("#{reflection.name}=") do |new_value| association = association_instance_get(reflection.name) if association.nil? || association.target != new_value @@ -1392,9 +1387,7 @@ module ActiveRecord association_instance_set(reflection.name, new_value.nil? ? nil : association) end - method = "set_#{reflection.name}_target" - remove_possible_method(method) - define_method(method) do |target| + redefine_method("set_#{reflection.name}_target") do |target| return if target.nil? and association_proxy_class == BelongsToAssociation association = association_proxy_class.new(self, reflection) association.target = target @@ -1403,8 +1396,7 @@ module ActiveRecord end def collection_reader_method(reflection, association_proxy_class) - remove_possible_method(reflection.name) - define_method(reflection.name) do |*params| + redefine_method(reflection.name) do |*params| force_reload = params.first unless params.empty? association = association_instance_get(reflection.name) @@ -1418,9 +1410,7 @@ module ActiveRecord association end - method = "#{reflection.name.to_s.singularize}_ids" - remove_possible_method(method) - define_method(method) do + redefine_method("#{reflection.name.to_s.singularize}_ids") do if send(reflection.name).loaded? || reflection.options[:finder_sql] send(reflection.name).map(&:id) else @@ -1440,16 +1430,14 @@ module ActiveRecord collection_reader_method(reflection, association_proxy_class) if writer - define_method("#{reflection.name}=") do |new_value| + redefine_method("#{reflection.name}=") do |new_value| # Loads proxy class instance (defined in collection_reader_method) if not already loaded association = send(reflection.name) association.replace(new_value) association end - method = "#{reflection.name.to_s.singularize}_ids=" - remove_possible_method(method) - define_method(method) do |new_value| + redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i) send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids)) end @@ -1457,9 +1445,7 @@ module ActiveRecord end def association_constructor_method(constructor, reflection, association_proxy_class) - method = "#{constructor}_#{reflection.name}" - remove_possible_method(method) - define_method(method) do |*params| + redefine_method("#{constructor}_#{reflection.name}") do |*params| attributees = params.first unless params.empty? replace_existing = params[1].nil? ? true : params[1] association = association_instance_get(reflection.name) @@ -1500,9 +1486,8 @@ module ActiveRecord end def add_touch_callbacks(reflection, touch_attribute) - method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym - remove_possible_method(method_name) - define_method(method_name) do + method_name = :"belongs_to_touch_after_save_or_destroy_for_#{reflection.name}" + redefine_method(method_name) do association = send(reflection.name) if touch_attribute == true diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 5bf43b3a72..657303fd14 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -871,7 +871,7 @@ module ActiveRecord table_names.each do |table_name| table_name = table_name.to_s.tr('./', '_') - define_method(table_name) do |*fixtures| + redefine_method(table_name) do |*fixtures| force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload @fixture_cache[table_name] ||= {} diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 849ec9c884..6596c695e2 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -105,7 +105,7 @@ module ActiveRecord extension ? relation.extending(extension) : relation end - singleton_class.send :define_method, name, &scopes[name] + singleton_class.send(:redefine_method, name, &scopes[name]) end def named_scope(*args, &block) diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb index 6e6645511c..ed4efdc1c0 100644 --- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -101,6 +101,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase #we need to actually modify some data, so we make execute point to the original method ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do alias_method :execute_with_stub, :execute + remove_method :execute alias_method :execute, :execute_without_stub end yield -- cgit v1.2.3 From bd4b3d8b2fc6b657ca2981582f8736842ac500d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Sat, 17 Jul 2010 23:53:05 +0800 Subject: Added missing require of remove_method --- activerecord/lib/active_record/associations.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7cddb9c601..1b9b725dd4 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/enumerable' require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/conversions' +require 'active_support/core_ext/module/remove_method' module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: -- cgit v1.2.3 From dd4e81df86a119a32d52396c4ef856c856b7965a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 10:21:45 -0700 Subject: avoid call to Array#first --- activesupport/lib/active_support/notifications/fanout.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 3bbb9cca44..526ca26764 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -38,9 +38,9 @@ module ActiveSupport @delegate = delegate end - def publish(*args) - return unless subscribed_to?(args.first) - @delegate.call(*args) + def publish(message, *args) + return unless subscribed_to?(message) + @delegate.call(message, *args) true end -- cgit v1.2.3 From ad4ef4226fd5d47252d30effa41a3ab2f55dbc8d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 10:31:24 -0700 Subject: avoid const lookup. we know what these constants are in advance --- activerecord/lib/active_record/log_subscriber.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 71065f9908..89c79055fe 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -10,10 +10,10 @@ module ActiveRecord sql = event.payload[:sql].squeeze(' ') if odd? - name = color(name, :cyan, true) + name = color(name, CYAN, true) sql = color(sql, nil, true) else - name = color(name, :magenta, true) + name = color(name, MAGENTA, true) end debug " #{name} #{sql}" @@ -29,4 +29,4 @@ module ActiveRecord end end -ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file +ActiveRecord::LogSubscriber.attach_to :active_record -- cgit v1.2.3 From 38f0161aabb302550e1522cb62d19e54d448be9b Mon Sep 17 00:00:00 2001 From: Daniel Guettler Date: Sun, 18 Jul 2010 07:30:48 -0400 Subject: Minor performance improvment in notifications/fanout and active_record/log_subscriber [#5098 state:open] --- activerecord/lib/active_record/log_subscriber.rb | 2 ++ activesupport/lib/active_support/notifications/fanout.rb | 12 +++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 89c79055fe..278e192e59 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -6,6 +6,8 @@ module ActiveRecord end def sql(event) + return unless logger.debug? + name = '%s (%.1fms)' % [event.payload[:name], event.duration] sql = event.payload[:sql].squeeze(' ') diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 526ca26764..64f315cb6a 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -21,11 +21,11 @@ module ActiveSupport end def publish(name, *args) - if listeners = @listeners_for[name] - listeners.each { |s| s.publish(name, *args) } - else - @listeners_for[name] = @subscribers.select { |s| s.publish(name, *args) } - end + listeners_for(name).each { |s| s.publish(name, *args) } + end + + def listeners_for(name) + @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) } end # This is a sync queue, so there is not waiting. @@ -39,9 +39,7 @@ module ActiveSupport end def publish(message, *args) - return unless subscribed_to?(message) @delegate.call(message, *args) - true end def subscribed_to?(name) -- cgit v1.2.3 From 1b26c66ce470ce68674bbdce738c6f68467cff7d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 13:19:28 -0700 Subject: mocking out debing? call in the MockLogger --- activerecord/test/cases/log_subscriber_test.rb | 2 ++ activesupport/lib/active_support/log_subscriber/test_helper.rb | 3 +++ 2 files changed, 5 insertions(+) diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index cde383783b..fa53fc7477 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -22,6 +22,7 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_basic_query_logging + @logger.debugging = true Developer.all wait assert_equal 1, @logger.logged(:debug).size @@ -30,6 +31,7 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_cached_queries + @logger.debugging = true ActiveRecord::Base.cache do Developer.all Developer.all diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index a3fb92778b..0f5fc3554b 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -48,9 +48,12 @@ module ActiveSupport class MockLogger attr_reader :flush_count + attr_accessor :debugging + alias :debug? :debugging def initialize @flush_count = 0 + @debugging = false @logged = Hash.new { |h,k| h[k] = [] } end -- cgit v1.2.3 From d39c3b179c2c0d31099033b3de4a866e19ce144b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 20 Jul 2010 04:28:51 +0800 Subject: Test added, we shouldn't log sql calls when logger is not on debug? mode --- activerecord/test/cases/log_subscriber_test.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index fa53fc7477..2a207bed8a 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -41,4 +41,21 @@ class LogSubscriberTest < ActiveRecord::TestCase assert_match(/CACHE/, @logger.logged(:debug).last) assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last) end + + def test_basic_query_doesnt_log_when_level_is_not_debug + @logger.debugging = false + Developer.all + wait + assert_equal 0, @logger.logged(:debug).size + end + + def test_cached_queries_doesnt_log_when_level_is_not_debug + @logger.debugging = false + ActiveRecord::Base.cache do + Developer.all + Developer.all + end + wait + assert_equal 0, @logger.logged(:debug).size + end end -- cgit v1.2.3 From 17600eb435693f333af46912fd796fcdff5657b2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 20 Jul 2010 00:03:20 +0800 Subject: Remove unused orig_handler and unneeded parentheses --- actionpack/lib/action_controller/metal/rescue.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb index cc2b020e03..eb037aa1b0 100644 --- a/actionpack/lib/action_controller/metal/rescue.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -4,9 +4,9 @@ module ActionController #:nodoc: include ActiveSupport::Rescuable def rescue_with_handler(exception) - if ((exception.respond_to?(:original_exception)) && - (orig_exception = exception.original_exception) && - (orig_handler = handler_for_rescue(orig_exception))) + if (exception.respond_to?(:original_exception) && + (orig_exception = exception.original_exception) && + handler_for_rescue(orig_exception)) exception = orig_exception end super(exception) -- cgit v1.2.3 From c3c349ec3e9a3990cac4d256c308b18fd35d9606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 19 Jul 2010 22:33:51 +0200 Subject: Remove assert_valid. It was already deprecated on Rails 2.3. --- actionpack/lib/action_dispatch/testing/assertions.rb | 2 -- .../lib/action_dispatch/testing/assertions/model.rb | 19 ------------------- .../test/controller/action_pack_assertions_test.rb | 15 --------------- 3 files changed, 36 deletions(-) delete mode 100644 actionpack/lib/action_dispatch/testing/assertions/model.rb diff --git a/actionpack/lib/action_dispatch/testing/assertions.rb b/actionpack/lib/action_dispatch/testing/assertions.rb index 0e4a92048f..822150b768 100644 --- a/actionpack/lib/action_dispatch/testing/assertions.rb +++ b/actionpack/lib/action_dispatch/testing/assertions.rb @@ -1,7 +1,6 @@ module ActionDispatch module Assertions autoload :DomAssertions, 'action_dispatch/testing/assertions/dom' - autoload :ModelAssertions, 'action_dispatch/testing/assertions/model' autoload :ResponseAssertions, 'action_dispatch/testing/assertions/response' autoload :RoutingAssertions, 'action_dispatch/testing/assertions/routing' autoload :SelectorAssertions, 'action_dispatch/testing/assertions/selector' @@ -11,7 +10,6 @@ module ActionDispatch included do include DomAssertions - include ModelAssertions include ResponseAssertions include RoutingAssertions include SelectorAssertions diff --git a/actionpack/lib/action_dispatch/testing/assertions/model.rb b/actionpack/lib/action_dispatch/testing/assertions/model.rb deleted file mode 100644 index 46714418c6..0000000000 --- a/actionpack/lib/action_dispatch/testing/assertions/model.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ActionDispatch - module Assertions - module ModelAssertions - # Ensures that the passed record is valid by Active Record standards and - # returns any error messages if it is not. - # - # ==== Examples - # - # # assert that a newly created record is valid - # model = Model.new - # assert_valid(model) - # - def assert_valid(record) - ::ActiveSupport::Deprecation.warn("assert_valid is deprecated. Use assert record.valid? instead", caller) - assert record.valid?, record.errors.full_messages.join("\n") - end - end - end -end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 765e111226..53cdd358b4 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -482,21 +482,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase assert_redirected_to :controller => 'admin/user' end - def test_assert_valid - get :get_valid_record - assert_deprecated { assert_valid assigns('record') } - end - - def test_assert_valid_failing - get :get_invalid_record - - begin - assert_deprecated { assert_valid assigns('record') } - assert false - rescue ActiveSupport::TestCase::Assertion => e - end - end - def test_assert_response_uses_exception_message @controller = AssertResponseWithUnexpectedErrorController.new get :index -- cgit v1.2.3 From 202fb79e8686ee127fe49497c979cfc9c9d985d5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 13:44:11 -0700 Subject: reusing the time instrumentation from the instrumenter rather than Benchmark. [#5098 state:open] --- .../lib/active_record/connection_adapters/abstract_adapter.rb | 11 +++++++---- .../lib/active_support/notifications/instrumenter.rb | 11 +++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index be8d1bd76b..6072481411 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -199,11 +199,14 @@ module ActiveRecord def log(sql, name) name ||= "SQL" - result = nil - ActiveSupport::Notifications.instrument("sql.active_record", - :sql => sql, :name => name, :connection_id => self.object_id) do - @runtime += Benchmark.ms { result = yield } + instrumenter = ActiveSupport::Notifications.instrumenter + + result = instrumenter.instrument("sql.active_record", + :sql => sql, :name => name, :connection_id => object_id) do + yield end + @runtime += instrumenter.elapsed + result rescue Exception => e message = "#{e.class.name}: #{e.message}: #{sql}" diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index 34bccb83d0..ff2b19bc65 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -9,23 +9,30 @@ module ActiveSupport def initialize(notifier) @id = unique_id @notifier = notifier + @started = nil + @finished = nil end # Instrument the given block by measuring the time taken to execute it # and publish it. Notice that events get sent even if an error occurs # in the passed-in block def instrument(name, payload={}) - time = Time.now + @started = Time.now begin yield(payload) if block_given? rescue Exception => e payload[:exception] = [e.class.name, e.message] raise e ensure - @notifier.publish(name, time, Time.now, @id, payload) + @finished = Time.now + @notifier.publish(name, @started, @finished, @id, payload) end end + def elapsed + 1000.0 * @finished.to_f - @started.to_f + end + private def unique_id SecureRandom.hex(10) -- cgit v1.2.3 From e466354edb31f243899051e2119f4ce72bafd5f3 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 19 Jul 2010 13:33:26 -0700 Subject: Extract ParameterFilter class from FilterParameters mixin Signed-off-by: wycats --- actionpack/lib/action_dispatch.rb | 1 + .../lib/action_dispatch/http/filter_parameters.rb | 73 +++------------------- .../lib/action_dispatch/http/parameter_filter.rb | 72 +++++++++++++++++++++ actionpack/test/dispatch/request_test.rb | 8 +-- 4 files changed, 86 insertions(+), 68 deletions(-) create mode 100644 actionpack/lib/action_dispatch/http/parameter_filter.rb diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index cdf81c6648..1da4a0d4be 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -63,6 +63,7 @@ module ActionDispatch autoload :Headers autoload :MimeNegotiation autoload :Parameters + autoload :ParameterFilter autoload :FilterParameters autoload :Upload autoload :UploadedFile, 'action_dispatch/http/upload' diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index 47643ce130..1ab48ae04d 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -26,85 +26,30 @@ module ActionDispatch module FilterParameters extend ActiveSupport::Concern - @@compiled_parameter_filter_for = {} + @@parameter_filter_for = {} # Return a hash of parameters with all sensitive data replaced. def filtered_parameters - @filtered_parameters ||= if filtering_parameters? - process_parameter_filter(parameters) - else - parameters.dup - end + @filtered_parameters ||= parameter_filter.filter(parameters) end # Return a hash of request.env with all sensitive data replaced. def filtered_env - filtered_env = @env.dup - filtered_env.each do |key, value| - if (key =~ /RAW_POST_DATA/i) - filtered_env[key] = '[FILTERED]' - elsif value.is_a?(Hash) - filtered_env[key] = process_parameter_filter(value) - end - end - filtered_env + @filtered_env ||= env_filter.filter(@env) end protected - def filtering_parameters? #:nodoc: - @env["action_dispatch.parameter_filter"].present? + def parameter_filter + parameter_filter_for(@env["action_dispatch.parameter_filter"]) end - def process_parameter_filter(params) #:nodoc: - compiled_parameter_filter_for(@env["action_dispatch.parameter_filter"]).call(params) + def env_filter + parameter_filter_for(Array.wrap(@env["action_dispatch.parameter_filter"]) << /RAW_POST_DATA/) end - def compile_parameter_filter(filters) #:nodoc: - strings, regexps, blocks = [], [], [] - - filters.each do |item| - case item - when NilClass - when Proc - blocks << item - when Regexp - regexps << item - else - strings << item.to_s - end - end - - regexps << Regexp.new(strings.join('|'), true) unless strings.empty? - [regexps, blocks] - end - - def compiled_parameter_filter_for(filters) #:nodoc: - @@compiled_parameter_filter_for[filters] ||= begin - regexps, blocks = compile_parameter_filter(filters) - - lambda do |original_params| - filtered_params = {} - - original_params.each do |key, value| - if regexps.find { |r| key =~ r } - value = '[FILTERED]' - elsif value.is_a?(Hash) - value = process_parameter_filter(value) - elsif value.is_a?(Array) - value = value.map { |v| v.is_a?(Hash) ? process_parameter_filter(v) : v } - elsif blocks.present? - key = key.dup - value = value.dup if value.duplicable? - blocks.each { |b| b.call(key, value) } - end - - filtered_params[key] = value - end - - filtered_params - end - end + def parameter_filter_for(filters) + @@parameter_filter_for[filters] ||= ParameterFilter.new(filters) end end diff --git a/actionpack/lib/action_dispatch/http/parameter_filter.rb b/actionpack/lib/action_dispatch/http/parameter_filter.rb new file mode 100644 index 0000000000..1480e8f77c --- /dev/null +++ b/actionpack/lib/action_dispatch/http/parameter_filter.rb @@ -0,0 +1,72 @@ +module ActionDispatch + module Http + class ParameterFilter + + def initialize(filters) + @filters = filters + end + + def filter(params) + if enabled? + compiled_filter.call(params) + else + params.dup + end + end + + private + + def enabled? + @filters.present? + end + + def compiled_filter + @compiled_filter ||= begin + regexps, blocks = compile_filter + + lambda do |original_params| + filtered_params = {} + + original_params.each do |key, value| + if regexps.find { |r| key =~ r } + value = '[FILTERED]' + elsif value.is_a?(Hash) + value = filter(value) + elsif value.is_a?(Array) + value = value.map { |v| v.is_a?(Hash) ? filter(v) : v } + elsif blocks.present? + key = key.dup + value = value.dup if value.duplicable? + blocks.each { |b| b.call(key, value) } + end + + filtered_params[key] = value + end + + filtered_params + end + end + end + + def compile_filter + strings, regexps, blocks = [], [], [] + + @filters.each do |item| + case item + when NilClass + when Proc + blocks << item + when Regexp + regexps << item + else + strings << item.to_s + end + end + + regexps << Regexp.new(strings.join('|'), true) unless strings.empty? + [regexps, blocks] + end + + end + end +end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index e5ee412021..c8947aac80 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -392,19 +392,19 @@ class RequestTest < ActiveSupport::TestCase [{'baz'=>[{'foo'=>'baz'}, "1"]}, {'baz'=>[{'foo'=>'[FILTERED]'}, "1"]}, [/foo/]]] test_hashes.each do |before_filter, after_filter, filter_words| - request = stub_request('action_dispatch.parameter_filter' => filter_words) - assert_equal after_filter, request.send(:process_parameter_filter, before_filter) + parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words) + assert_equal after_filter, parameter_filter.filter(before_filter) filter_words << 'blah' filter_words << lambda { |key, value| value.reverse! if key =~ /bargain/ } - request = stub_request('action_dispatch.parameter_filter' => filter_words) + parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words) before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}} after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}} - assert_equal after_filter, request.send(:process_parameter_filter, before_filter) + assert_equal after_filter, parameter_filter.filter(before_filter) end end -- cgit v1.2.3 From a6e95ba55401ddcaf9ef867a080b30c2d07c56ac Mon Sep 17 00:00:00 2001 From: Kouhei Sutou Date: Sat, 17 Jul 2010 15:36:40 +0900 Subject: fix mixed encoding logs can't be logged. [#4807 state:committed] Signed-off-by: Kouhei Sutou Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/buffered_logger.rb | 6 +++++- activesupport/test/buffered_logger_test.rb | 15 +++++++++++++++ activesupport/test/multibyte_test_helpers.rb | 5 ++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index 29c3843d16..b861a6f62a 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -101,7 +101,11 @@ module ActiveSupport @guard.synchronize do unless buffer.empty? old_buffer = buffer - @log.write(old_buffer.join) + all_content = StringIO.new + old_buffer.each do |content| + all_content << content + end + @log.write(all_content.string) end # Important to do this even if buffer was empty or else @buffer will diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb index 850febb959..97c0ef14db 100644 --- a/activesupport/test/buffered_logger_test.rb +++ b/activesupport/test/buffered_logger_test.rb @@ -1,9 +1,12 @@ require 'abstract_unit' +require 'multibyte_test_helpers' require 'stringio' require 'fileutils' require 'active_support/buffered_logger' class BufferedLoggerTest < Test::Unit::TestCase + include MultibyteTestHelpers + Logger = ActiveSupport::BufferedLogger def setup @@ -146,4 +149,16 @@ class BufferedLoggerTest < Test::Unit::TestCase @logger.expects :clear_buffer @logger.flush end + + def test_buffer_multibyte + @logger.auto_flushing = 2 + @logger.info(UNICODE_STRING) + @logger.info(BYTE_STRING) + assert @output.string.include?(UNICODE_STRING) + byte_string = @output.string.dup + if byte_string.respond_to?(:force_encoding) + byte_string.force_encoding("ASCII-8BIT") + end + assert byte_string.include?(BYTE_STRING) + end end diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb index 597f949059..8839b75601 100644 --- a/activesupport/test/multibyte_test_helpers.rb +++ b/activesupport/test/multibyte_test_helpers.rb @@ -4,6 +4,9 @@ module MultibyteTestHelpers UNICODE_STRING = 'こにちわ' ASCII_STRING = 'ohayo' BYTE_STRING = "\270\236\010\210\245" + if BYTE_STRING.respond_to?(:force_encoding) + BYTE_STRING.force_encoding("ASCII-8BIT") + end def chars(str) ActiveSupport::Multibyte::Chars.new(str) @@ -16,4 +19,4 @@ module MultibyteTestHelpers def assert_equal_codepoints(expected, actual, message=nil) assert_equal(inspect_codepoints(expected), inspect_codepoints(actual), message) end -end \ No newline at end of file +end -- 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(-) 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 51d2db0a63529cfe6e7d7d0c620f10235c63ffe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 20 Jul 2010 09:55:24 +0200 Subject: Add missing require to metal/streaming.rb --- actionpack/lib/action_controller/metal/streaming.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 753af3dc58..d75b46dace 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/file/path' + module ActionController #:nodoc: # Methods for sending arbitrary data and for streaming files to the browser, # instead of rendering. -- cgit v1.2.3 From 9df9c4bac008ba94c37faff411368c33408faff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 20 Jul 2010 17:07:18 +0200 Subject: Add a test for elapsed and require missing benchmark file. --- .../lib/action_controller/metal/instrumentation.rb | 1 + .../active_support/notifications/instrumenter.rb | 4 +-- activesupport/test/notifications_test.rb | 30 ++++++++++------------ 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index b2c119d7e4..b08d9a8434 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -1,3 +1,4 @@ +require 'benchmark' require 'abstract_controller/logger' module ActionController diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index ff2b19bc65..e98189f899 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -17,8 +17,8 @@ module ActiveSupport # and publish it. Notice that events get sent even if an error occurs # in the passed-in block def instrument(name, payload={}) - @started = Time.now begin + @started = Time.now yield(payload) if block_given? rescue Exception => e payload[:exception] = [e.class.name, e.message] @@ -30,7 +30,7 @@ module ActiveSupport end def elapsed - 1000.0 * @finished.to_f - @started.to_f + 1000.0 * (@finished.to_f - @started.to_f) end private diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 73c85be87c..41e8ca4ae7 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -11,14 +11,11 @@ module Notifications @named_subscription = @notifier.subscribe("named.subscription") { |*args| @named_events << event(*args) } end - private - def event(*args) - ActiveSupport::Notifications::Event.new(*args) - end + private - def drain - @notifier.wait - end + def event(*args) + ActiveSupport::Notifications::Event.new(*args) + end end class UnsubscribeTest < TestCase @@ -132,13 +129,10 @@ module Notifications def test_instrument_returns_block_result assert_equal 2, instrument(:awesome) { 1 + 1 } - drain end def test_instrument_yields_the_paylod_for_further_modification assert_equal 2, instrument(:awesome) { |p| p[:result] = 1 + 1 } - drain - assert_equal 1, @events.size assert_equal :awesome, @events.first.name assert_equal Hash[:result => 2], @events.first.payload @@ -154,15 +148,11 @@ module Notifications 1 + 1 end - drain - assert_equal 1, @events.size assert_equal :wot, @events.first.name assert_equal Hash[:payload => "child"], @events.first.payload end - drain - assert_equal 2, @events.size assert_equal :awesome, @events.last.name assert_equal Hash[:payload => "notifications"], @events.last.payload @@ -177,16 +167,22 @@ module Notifications assert_equal "FAIL", e.message end - drain assert_equal 1, @events.size assert_equal Hash[:payload => "notifications", :exception => ["RuntimeError", "FAIL"]], @events.last.payload end + def test_elapsed + instrument(:something) do + sleep(0.001) + end + + # Elapsed returns duration in ms + assert_in_delta 1, ActiveSupport::Notifications.instrumenter.elapsed, 100 + end + def test_event_is_pushed_even_without_block instrument(:awesome, :payload => "notifications") - drain - assert_equal 1, @events.size assert_equal :awesome, @events.last.name assert_equal Hash[:payload => "notifications"], @events.last.payload -- cgit v1.2.3 From e1df4b956882f0c10a310088c1c13dcaa655a3b1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 21:50:11 -0700 Subject: adding a reader for loaded, initializing @loaded to false --- activerecord/lib/active_record/named_scope.rb | 2 +- activerecord/lib/active_record/relation.rb | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 6596c695e2..417ff4b5eb 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -26,7 +26,7 @@ module ActiveRecord # You can define a \scope that applies to all finders using # ActiveRecord::Base.default_scope. def scoped(options = nil) - if options.present? + if options scoped.apply_finder_options(options) else current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7499100f55..86a210d2be 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -13,14 +13,15 @@ module ActiveRecord delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a delegate :insert, :to => :arel - attr_reader :table, :klass + attr_reader :table, :klass, :loaded attr_accessor :extensions + alias :loaded? :loaded def initialize(klass, table) @klass, @table = klass, table @implicit_readonly = nil - @loaded = nil + @loaded = false SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)} (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])} @@ -292,10 +293,6 @@ module ActiveRecord where(@klass.primary_key => id_or_array).delete_all end - def loaded? - @loaded - end - def reload reset to_a # force reload -- 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. --- activerecord/test/cases/log_subscriber_test.rb | 6 ++---- .../lib/active_support/log_subscriber/test_helper.rb | 18 ++++++++++++++---- .../application/initializers/notifications_test.rb | 19 +++++-------------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 2a207bed8a..91ba852ecd 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -22,7 +22,6 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_basic_query_logging - @logger.debugging = true Developer.all wait assert_equal 1, @logger.logged(:debug).size @@ -31,7 +30,6 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_cached_queries - @logger.debugging = true ActiveRecord::Base.cache do Developer.all Developer.all @@ -43,14 +41,14 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_basic_query_doesnt_log_when_level_is_not_debug - @logger.debugging = false + @logger.level = 1 Developer.all wait assert_equal 0, @logger.logged(:debug).size end def test_cached_queries_doesnt_log_when_level_is_not_debug - @logger.debugging = false + @logger.level = 1 ActiveRecord::Base.cache do Developer.all Developer.all diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb index 0f5fc3554b..9e52cb97a9 100644 --- a/activesupport/lib/active_support/log_subscriber/test_helper.rb +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -1,4 +1,5 @@ require 'active_support/log_subscriber' +require 'active_support/buffered_logger' module ActiveSupport class LogSubscriber @@ -47,13 +48,14 @@ module ActiveSupport end class MockLogger + include ActiveSupport::BufferedLogger::Severity + attr_reader :flush_count - attr_accessor :debugging - alias :debug? :debugging + attr_accessor :level - def initialize + def initialize(level = DEBUG) @flush_count = 0 - @debugging = false + @level = level @logged = Hash.new { |h,k| h[k] = [] } end @@ -68,6 +70,14 @@ module ActiveSupport def flush @flush_count += 1 end + + ActiveSupport::BufferedLogger::Severity.constants.each do |severity| + class_eval <<-EOT, __FILE__, __LINE__ + 1 + def #{severity.downcase}? + #{severity} >= @level + end + EOT + end end # Wait notifications to be published. 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(-) 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 978c49ea6a969fe040ad15dbda78e43ca5afc069 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 20 Jul 2010 17:00:12 -0300 Subject: Make use of severity levels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/log_subscriber_test.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 91ba852ecd..342daa19df 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -4,6 +4,7 @@ require "active_support/log_subscriber/test_helper" class LogSubscriberTest < ActiveRecord::TestCase include ActiveSupport::LogSubscriber::TestHelper + include ActiveSupport::BufferedLogger::Severity def setup @old_logger = ActiveRecord::Base.logger @@ -41,14 +42,14 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_basic_query_doesnt_log_when_level_is_not_debug - @logger.level = 1 + @logger.level = INFO Developer.all wait assert_equal 0, @logger.logged(:debug).size end def test_cached_queries_doesnt_log_when_level_is_not_debug - @logger.level = 1 + @logger.level = INFO ActiveRecord::Base.cache do Developer.all Developer.all -- cgit v1.2.3 From a63566dda8246bd57e80032a1213532d0dc2ae0b Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Tue, 20 Jul 2010 21:07:59 +0200 Subject: Moved PolymorphicRoutes to ActionDispatch::Routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_controller.rb | 1 - actionpack/lib/action_controller/base.rb | 1 - .../lib/action_controller/polymorphic_routes.rb | 183 -------------------- actionpack/lib/action_dispatch/routing.rb | 4 +- .../action_dispatch/routing/polymorphic_routes.rb | 186 +++++++++++++++++++++ actionpack/lib/action_dispatch/routing/url_for.rb | 1 + .../helpers/record_identification_helper.rb | 2 +- actionpack/lib/action_view/test_case.rb | 2 +- 8 files changed, 191 insertions(+), 189 deletions(-) delete mode 100644 actionpack/lib/action_controller/polymorphic_routes.rb create mode 100644 actionpack/lib/action_dispatch/routing/polymorphic_routes.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 1bd4572a47..ca0e5d6ff6 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -6,7 +6,6 @@ module ActionController autoload :Base autoload :Caching - autoload :PolymorphicRoutes autoload :Metal autoload :Middleware diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index cfe6b30add..1c6896d362 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -28,7 +28,6 @@ module ActionController SessionManagement, Caching, MimeResponds, - PolymorphicRoutes, ImplicitRender, Cookies, diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb deleted file mode 100644 index b4837c610a..0000000000 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ /dev/null @@ -1,183 +0,0 @@ -module ActionController - # Polymorphic URL helpers are methods for smart resolution to a named route call when - # given an Active Record model instance. They are to be used in combination with - # ActionController::Resources. - # - # These methods are useful when you want to generate correct URL or path to a RESTful - # resource without having to know the exact type of the record in question. - # - # Nested resources and/or namespaces are also supported, as illustrated in the example: - # - # polymorphic_url([:admin, @article, @comment]) - # - # results in: - # - # admin_article_comment_url(@article, @comment) - # - # == Usage within the framework - # - # Polymorphic URL helpers are used in a number of places throughout the Rails framework: - # - # * url_for, so you can use it with a record as the argument, e.g. - # url_for(@article); - # * ActionView::Helpers::FormHelper uses polymorphic_path, so you can write - # form_for(@article) without having to specify :url parameter for the form - # action; - # * redirect_to (which, in fact, uses url_for) so you can write - # redirect_to(post) in your controllers; - # * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs - # for feed entries. - # - # == Prefixed polymorphic helpers - # - # In addition to polymorphic_url and polymorphic_path methods, a - # number of prefixed helpers are available as a shorthand to :action => "..." - # in options. Those are: - # - # * edit_polymorphic_url, edit_polymorphic_path - # * new_polymorphic_url, new_polymorphic_path - # - # Example usage: - # - # edit_polymorphic_path(@post) # => "/posts/1/edit" - # polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf" - module PolymorphicRoutes - # Constructs a call to a named RESTful route for the given record and returns the - # resulting URL string. For example: - # - # # calls post_url(post) - # polymorphic_url(post) # => "http://example.com/posts/1" - # polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1" - # polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1" - # polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1" - # polymorphic_url(Comment) # => "http://example.com/comments" - # - # ==== Options - # - # * :action - Specifies the action prefix for the named route: - # :new or :edit. Default is no prefix. - # * :routing_type - Allowed values are :path or :url. - # Default is :url. - # - # ==== Examples - # - # # an Article record - # polymorphic_url(record) # same as article_url(record) - # - # # a Comment record - # polymorphic_url(record) # same as comment_url(record) - # - # # it recognizes new records and maps to the collection - # record = Comment.new - # polymorphic_url(record) # same as comments_url() - # - # # the class of a record will also map to the collection - # polymorphic_url(Comment) # same as comments_url() - # - def polymorphic_url(record_or_hash_or_array, options = {}) - if record_or_hash_or_array.kind_of?(Array) - record_or_hash_or_array = record_or_hash_or_array.compact - record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1 - end - - record = extract_record(record_or_hash_or_array) - record = record.to_model if record.respond_to?(:to_model) - - args = case record_or_hash_or_array - when Hash; [ record_or_hash_or_array ] - when Array; record_or_hash_or_array.dup - else [ record_or_hash_or_array ] - end - - inflection = if options[:action].to_s == "new" - args.pop - :singular - elsif (record.respond_to?(:persisted?) && !record.persisted?) - args.pop - :plural - elsif record.is_a?(Class) - args.pop - :plural - else - :singular - end - - args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)} - named_route = build_named_route_call(record_or_hash_or_array, inflection, options) - - url_options = options.except(:action, :routing_type) - unless url_options.empty? - args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options - end - - send(named_route, *args) - end - - # Returns the path component of a URL for the given record. It uses - # polymorphic_url with :routing_type => :path. - def polymorphic_path(record_or_hash_or_array, options = {}) - polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path)) - end - - %w(edit new).each do |action| - module_eval <<-EOT, __FILE__, __LINE__ + 1 - def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {}) - polymorphic_url( # polymorphic_url( - record_or_hash, # record_or_hash, - options.merge(:action => "#{action}")) # options.merge(:action => "edit")) - end # end - # - def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {}) - polymorphic_url( # polymorphic_url( - record_or_hash, # record_or_hash, - options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path)) - end # end - EOT - end - - private - def action_prefix(options) - options[:action] ? "#{options[:action]}_" : '' - end - - def routing_type(options) - options[:routing_type] || :url - end - - def build_named_route_call(records, inflection, options = {}) - unless records.is_a?(Array) - record = extract_record(records) - route = '' - else - record = records.pop - route = records.inject("") do |string, parent| - if parent.is_a?(Symbol) || parent.is_a?(String) - string << "#{parent}_" - else - string << RecordIdentifier.plural_class_name(parent).singularize - string << "_" - end - end - end - - if record.is_a?(Symbol) || record.is_a?(String) - route << "#{record}_" - else - route << RecordIdentifier.plural_class_name(record) - route = route.singularize if inflection == :singular - route << "_" - route << "index_" if RecordIdentifier.uncountable?(record) && inflection == :plural - end - - action_prefix(options) + route + routing_type(options).to_s - end - - def extract_record(record_or_hash_or_array) - case record_or_hash_or_array - when Array; record_or_hash_or_array.last - when Hash; record_or_hash_or_array[:id] - else record_or_hash_or_array - end - end - end -end diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index da62b14f9b..b7e09b99d1 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -1,6 +1,5 @@ require 'active_support/core_ext/object/to_param' require 'active_support/core_ext/regexp' -require 'action_controller/polymorphic_routes' module ActionDispatch # = Routing @@ -217,13 +216,14 @@ module ActionDispatch autoload :Route, 'action_dispatch/routing/route' autoload :RouteSet, 'action_dispatch/routing/route_set' autoload :UrlFor, 'action_dispatch/routing/url_for' + autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes' SEPARATORS = %w( / . ? ) #:nodoc: HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] #:nodoc: # A helper module to hold URL related helpers. module Helpers #:nodoc: - include ActionController::PolymorphicRoutes + include PolymorphicRoutes end end end diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb new file mode 100644 index 0000000000..18ea82c478 --- /dev/null +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -0,0 +1,186 @@ +module ActionDispatch + module Routing + # Polymorphic URL helpers are methods for smart resolution to a named route call when + # given an Active Record model instance. They are to be used in combination with + # ActionController::Resources. + # + # These methods are useful when you want to generate correct URL or path to a RESTful + # resource without having to know the exact type of the record in question. + # + # Nested resources and/or namespaces are also supported, as illustrated in the example: + # + # polymorphic_url([:admin, @article, @comment]) + # + # results in: + # + # admin_article_comment_url(@article, @comment) + # + # == Usage within the framework + # + # Polymorphic URL helpers are used in a number of places throughout the Rails framework: + # + # * url_for, so you can use it with a record as the argument, e.g. + # url_for(@article); + # * ActionView::Helpers::FormHelper uses polymorphic_path, so you can write + # form_for(@article) without having to specify :url parameter for the form + # action; + # * redirect_to (which, in fact, uses url_for) so you can write + # redirect_to(post) in your controllers; + # * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs + # for feed entries. + # + # == Prefixed polymorphic helpers + # + # In addition to polymorphic_url and polymorphic_path methods, a + # number of prefixed helpers are available as a shorthand to :action => "..." + # in options. Those are: + # + # * edit_polymorphic_url, edit_polymorphic_path + # * new_polymorphic_url, new_polymorphic_path + # + # Example usage: + # + # edit_polymorphic_path(@post) # => "/posts/1/edit" + # polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf" + module PolymorphicRoutes + # Constructs a call to a named RESTful route for the given record and returns the + # resulting URL string. For example: + # + # # calls post_url(post) + # polymorphic_url(post) # => "http://example.com/posts/1" + # polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1" + # polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1" + # polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1" + # polymorphic_url(Comment) # => "http://example.com/comments" + # + # ==== Options + # + # * :action - Specifies the action prefix for the named route: + # :new or :edit. Default is no prefix. + # * :routing_type - Allowed values are :path or :url. + # Default is :url. + # + # ==== Examples + # + # # an Article record + # polymorphic_url(record) # same as article_url(record) + # + # # a Comment record + # polymorphic_url(record) # same as comment_url(record) + # + # # it recognizes new records and maps to the collection + # record = Comment.new + # polymorphic_url(record) # same as comments_url() + # + # # the class of a record will also map to the collection + # polymorphic_url(Comment) # same as comments_url() + # + def polymorphic_url(record_or_hash_or_array, options = {}) + if record_or_hash_or_array.kind_of?(Array) + record_or_hash_or_array = record_or_hash_or_array.compact + record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1 + end + + record = extract_record(record_or_hash_or_array) + record = record.to_model if record.respond_to?(:to_model) + + args = case record_or_hash_or_array + when Hash; [ record_or_hash_or_array ] + when Array; record_or_hash_or_array.dup + else [ record_or_hash_or_array ] + end + + inflection = if options[:action].to_s == "new" + args.pop + :singular + elsif (record.respond_to?(:persisted?) && !record.persisted?) + args.pop + :plural + elsif record.is_a?(Class) + args.pop + :plural + else + :singular + end + + args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)} + named_route = build_named_route_call(record_or_hash_or_array, inflection, options) + + url_options = options.except(:action, :routing_type) + unless url_options.empty? + args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options + end + + send(named_route, *args) + end + + # Returns the path component of a URL for the given record. It uses + # polymorphic_url with :routing_type => :path. + def polymorphic_path(record_or_hash_or_array, options = {}) + polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path)) + end + + %w(edit new).each do |action| + module_eval <<-EOT, __FILE__, __LINE__ + 1 + def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {}) + polymorphic_url( # polymorphic_url( + record_or_hash, # record_or_hash, + options.merge(:action => "#{action}")) # options.merge(:action => "edit")) + end # end + # + def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {}) + polymorphic_url( # polymorphic_url( + record_or_hash, # record_or_hash, + options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path)) + end # end + EOT + end + + private + def action_prefix(options) + options[:action] ? "#{options[:action]}_" : '' + end + + def routing_type(options) + options[:routing_type] || :url + end + + def build_named_route_call(records, inflection, options = {}) + unless records.is_a?(Array) + record = extract_record(records) + route = '' + else + record = records.pop + route = records.inject("") do |string, parent| + if parent.is_a?(Symbol) || parent.is_a?(String) + string << "#{parent}_" + else + string << ActionController::RecordIdentifier.plural_class_name(parent).singularize + string << "_" + end + end + end + + if record.is_a?(Symbol) || record.is_a?(String) + route << "#{record}_" + else + route << ActionController::RecordIdentifier.plural_class_name(record) + route = route.singularize if inflection == :singular + route << "_" + route << "index_" if ActionController::RecordIdentifier.uncountable?(record) && inflection == :plural + end + + action_prefix(options) + route + routing_type(options).to_s + end + + def extract_record(record_or_hash_or_array) + case record_or_hash_or_array + when Array; record_or_hash_or_array.last + when Hash; record_or_hash_or_array[:id] + else record_or_hash_or_array + end + end + end + end +end + diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index 980abd44df..662eb05c26 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -82,6 +82,7 @@ module ActionDispatch # module UrlFor extend ActiveSupport::Concern + include PolymorphicRoutes included do # TODO: with_routing extends @controller with url_helpers, trickling down to including this module which overrides its default_url_options diff --git a/actionpack/lib/action_view/helpers/record_identification_helper.rb b/actionpack/lib/action_view/helpers/record_identification_helper.rb index 372f1cb8aa..eea5de9bc6 100644 --- a/actionpack/lib/action_view/helpers/record_identification_helper.rb +++ b/actionpack/lib/action_view/helpers/record_identification_helper.rb @@ -20,4 +20,4 @@ module ActionView end end end -end \ No newline at end of file +end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 56aebca957..137281e5e9 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -37,7 +37,7 @@ module ActionView include ActionController::TemplateAssertions include ActionView::Context - include ActionController::PolymorphicRoutes + include ActionDispatch::Routing::PolymorphicRoutes include ActionController::RecordIdentifier include AbstractController::Helpers -- cgit v1.2.3 From f576d7cf848717384799a9e9669b253ccc94deb5 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 19 Jul 2010 15:32:00 -0400 Subject: Ensure that primary_keys of HABTM records is not double quoted [#5152 state:reslved] --- .../has_and_belongs_to_many_association.rb | 4 ++-- .../has_and_belongs_to_many_associations_test.rb | 18 ++++++++++++++++++ activerecord/test/models/country.rb | 7 +++++++ activerecord/test/models/treaty.rb | 7 +++++++ activerecord/test/schema/schema.rb | 13 +++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 activerecord/test/models/country.rb create mode 100644 activerecord/test/models/treaty.rb diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index c989c3536d..aba66d5a96 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -49,9 +49,9 @@ module ActiveRecord attributes = columns.inject({}) do |attrs, column| case column.name.to_s when @reflection.primary_key_name.to_s - attrs[relation[column.name]] = owner_quoted_id + attrs[relation[column.name]] = @owner.send(:id) when @reflection.association_foreign_key.to_s - attrs[relation[column.name]] = record.quoted_id + attrs[relation[column.name]] = record.send(:id) else if record.has_attribute?(column.name) value = @owner.send(:quote_value, record[column.name], column) diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index b11969a841..d4d3d8e43e 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -24,6 +24,8 @@ require 'models/club' require 'models/member' require 'models/membership' require 'models/sponsor' +require 'models/country' +require 'models/treaty' require 'active_support/core_ext/string/conversions' class ProjectWithAfterCreateHook < ActiveRecord::Base @@ -83,6 +85,22 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings + def test_should_property_quote_string_primary_keys + country = Country.new(:name => 'India') + country.country_id = 'c1' + country.save! + + treaty = Treaty.new(:name => 'peace') + treaty.treaty_id = 't1' + country.treaties << treaty + + con = ActiveRecord::Base.connection + sql = 'select * from countries_treaties' + record = con.select_rows(sql).last + assert_equal 'c1', record[0] + assert_equal 't1', record[1] + end + def test_has_and_belongs_to_many david = Developer.find(1) diff --git a/activerecord/test/models/country.rb b/activerecord/test/models/country.rb new file mode 100644 index 0000000000..15e3a1de0b --- /dev/null +++ b/activerecord/test/models/country.rb @@ -0,0 +1,7 @@ +class Country < ActiveRecord::Base + + set_primary_key :country_id + + has_and_belongs_to_many :treaties + +end diff --git a/activerecord/test/models/treaty.rb b/activerecord/test/models/treaty.rb new file mode 100644 index 0000000000..b46537f0d2 --- /dev/null +++ b/activerecord/test/models/treaty.rb @@ -0,0 +1,7 @@ +class Treaty < ActiveRecord::Base + + set_primary_key :treaty_id + + has_and_belongs_to_many :countries + +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 641726b43f..c4eed256bf 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -600,6 +600,19 @@ ActiveRecord::Schema.define do t.string :title end + create_table :countries, :force => true, :id => false, :primary_key => 'country_id' do |t| + t.string :country_id + t.string :name + end + create_table :treaties, :force => true, :id => false, :primary_key => 'treaty_id' do |t| + t.string :treaty_id + t.string :name + end + create_table :countries_treaties, :force => true, :id => false do |t| + t.string :country_id, :null => false + t.string :treaty_id, :null => false + end + except 'SQLite' do # fk_test_has_fk should be before fk_test_has_pk create_table :fk_test_has_fk, :force => true do |t| -- cgit v1.2.3 From fa8b290496789eb037d4fad89acea1cb0a534f35 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 21 Jul 2010 08:13:31 +0800 Subject: id is a public method --- .../active_record/associations/has_and_belongs_to_many_association.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index aba66d5a96..9ec63e3fca 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -49,9 +49,9 @@ module ActiveRecord attributes = columns.inject({}) do |attrs, column| case column.name.to_s when @reflection.primary_key_name.to_s - attrs[relation[column.name]] = @owner.send(:id) + attrs[relation[column.name]] = @owner.id when @reflection.association_foreign_key.to_s - attrs[relation[column.name]] = record.send(:id) + attrs[relation[column.name]] = record.id else if record.has_attribute?(column.name) value = @owner.send(:quote_value, record[column.name], column) -- cgit v1.2.3 From 6807b080996ee4bd6b80abb4f5e9964632c421c8 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Wed, 21 Jul 2010 10:37:09 +0200 Subject: Moved a few methods from RecordIdentifier to ActiveModel::Naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_controller/record_identifier.rb | 31 +---------- .../action_dispatch/routing/polymorphic_routes.rb | 6 +-- actionpack/lib/action_view/helpers/form_helper.rb | 10 ++-- .../test/controller/record_identifier_test.rb | 40 -------------- activemodel/lib/active_model/naming.rb | 29 ++++++++++ activemodel/test/cases/naming_helpers_test.rb | 63 ++++++++++++++++++++++ 6 files changed, 101 insertions(+), 78 deletions(-) create mode 100644 activemodel/test/cases/naming_helpers_test.rb diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index d20c3b64c5..975b03c0c9 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -46,7 +46,7 @@ module ActionController # dom_class(post, :edit) # => "edit_post" # dom_class(Person, :edit) # => "edit_person" def dom_class(record_or_class, prefix = nil) - singular = singular_class_name(record_or_class) + singular = ActiveModel::Naming.singular(record_or_class) prefix ? "#{prefix}#{JOIN}#{singular}" : singular end @@ -85,34 +85,5 @@ module ActionController def sanitize_dom_id(candidate_id) candidate_id # TODO implement conversion to valid DOM id values end - - # Returns the plural class name of a record or class. Examples: - # - # plural_class_name(post) # => "posts" - # plural_class_name(Highrise::Person) # => "highrise_people" - def plural_class_name(record_or_class) - model_name_from_record_or_class(record_or_class).plural - end - - # Returns the singular class name of a record or class. Examples: - # - # singular_class_name(post) # => "post" - # singular_class_name(Highrise::Person) # => "highrise_person" - def singular_class_name(record_or_class) - model_name_from_record_or_class(record_or_class).singular - end - - # Identifies whether the class name of a record or class is uncountable. Examples: - # - # uncountable?(Sheep) # => true - # uncountable?(Post) => false - def uncountable?(record_or_class) - plural_class_name(record_or_class) == singular_class_name(record_or_class) - end - - private - def model_name_from_record_or_class(record_or_class) - (record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name - end end end diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 18ea82c478..31dba835ac 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -155,7 +155,7 @@ module ActionDispatch if parent.is_a?(Symbol) || parent.is_a?(String) string << "#{parent}_" else - string << ActionController::RecordIdentifier.plural_class_name(parent).singularize + string << ActiveModel::Naming.plural(parent).singularize string << "_" end end @@ -164,10 +164,10 @@ module ActionDispatch if record.is_a?(Symbol) || record.is_a?(String) route << "#{record}_" else - route << ActionController::RecordIdentifier.plural_class_name(record) + route << ActiveModel::Naming.plural(record) route = route.singularize if inflection == :singular route << "_" - route << "index_" if ActionController::RecordIdentifier.uncountable?(record) && inflection == :plural + route << "index_" if ActiveModel::Naming.uncountable?(record) && inflection == :plural end action_prefix(options) + route + routing_type(options).to_s diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 711c455ded..524913ae29 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -302,12 +302,12 @@ module ActionView object_name = record_or_name_or_array when Array object = record_or_name_or_array.last - object_name = options[:as] || ActionController::RecordIdentifier.singular_class_name(object) + object_name = options[:as] || ActiveModel::Naming.singular(object) apply_form_for_options!(record_or_name_or_array, options) args.unshift object else object = record_or_name_or_array - object_name = options[:as] || ActionController::RecordIdentifier.singular_class_name(object) + object_name = options[:as] || ActiveModel::Naming.singular(object) apply_form_for_options!([object], options) args.unshift object end @@ -533,7 +533,7 @@ module ActionView object = args.first else object = record_or_name_or_array - object_name = ActionController::RecordIdentifier.singular_class_name(object) + object_name = ActiveModel::Naming.singular(object) end builder = options[:builder] || ActionView::Base.default_form_builder @@ -1156,11 +1156,11 @@ module ActionView end when Array object = record_or_name_or_array.last - name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]" + name = "#{object_name}#{index}[#{ActiveModel::Naming.singular(object)}]" args.unshift(object) else object = record_or_name_or_array - name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]" + name = "#{object_name}#{index}[#{ActiveModel::Naming.singular(object)}]" args.unshift(object) end diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb index 6a84475758..835a0e970b 100644 --- a/actionpack/test/controller/record_identifier_test.rb +++ b/actionpack/test/controller/record_identifier_test.rb @@ -26,20 +26,6 @@ class Sheep end end -class Comment::Nested < Comment; end - -class Test::Unit::TestCase - protected - def comments_url - 'http://www.example.com/comments' - end - - def comment_url(comment) - "http://www.example.com/comments/#{comment.id}" - end -end - - class RecordIdentifierTest < Test::Unit::TestCase include ActionController::RecordIdentifier @@ -76,30 +62,4 @@ class RecordIdentifierTest < Test::Unit::TestCase def test_dom_class_with_prefix assert_equal "custom_prefix_#{@singular}", dom_class(@record, :custom_prefix) end - - def test_singular_class_name - assert_equal @singular, singular_class_name(@record) - end - - def test_singular_class_name_for_class - assert_equal @singular, singular_class_name(@klass) - end - - def test_plural_class_name - assert_equal @plural, plural_class_name(@record) - end - - def test_plural_class_name_for_class - assert_equal @plural, plural_class_name(@klass) - end - - def test_uncountable - assert_equal true, uncountable?(@uncountable) - assert_equal false, uncountable?(@klass) - end - - private - def method_missing(method, *args) - RecordIdentifier.send(method, *args) - end end diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index ca1e9f0ee8..dc83932dde 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -57,6 +57,35 @@ module ActiveModel def model_name @_model_name ||= ActiveModel::Name.new(self) end + + # Returns the plural class name of a record or class. Examples: + # + # ActiveModel::Naming.plural(post) # => "posts" + # ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people" + def self.plural(record_or_class) + model_name_from_record_or_class(record_or_class).plural + end + + # Returns the singular class name of a record or class. Examples: + # + # ActiveModel::Naming.singular(post) # => "post" + # ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person" + def self.singular(record_or_class) + model_name_from_record_or_class(record_or_class).singular + end + + # Identifies whether the class name of a record or class is uncountable. Examples: + # + # ActiveModel::Naming.uncountable?(Sheep) # => true + # ActiveModel::Naming.uncountable?(Post) => false + def self.uncountable?(record_or_class) + plural(record_or_class) == singular(record_or_class) + end + + private + def self.model_name_from_record_or_class(record_or_class) + (record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name + end end end diff --git a/activemodel/test/cases/naming_helpers_test.rb b/activemodel/test/cases/naming_helpers_test.rb new file mode 100644 index 0000000000..e7234e009e --- /dev/null +++ b/activemodel/test/cases/naming_helpers_test.rb @@ -0,0 +1,63 @@ +require 'cases/helper' + +class Comment + extend ActiveModel::Naming + include ActiveModel::Conversion + + attr_reader :id + def to_key; id ? [id] : nil end + def save; @id = 1 end + def new_record?; @id.nil? end + def name + @id.nil? ? 'new comment' : "comment ##{@id}" + end +end + +class Sheep + extend ActiveModel::Naming + include ActiveModel::Conversion + + attr_reader :id + def to_key; id ? [id] : nil end + def save; @id = 1 end + def new_record?; @id.nil? end + def name + @id.nil? ? 'new sheep' : "sheep ##{@id}" + end +end + +class NamingHelpersTest < Test::Unit::TestCase + def setup + @klass = Comment + @record = @klass.new + @singular = 'comment' + @plural = 'comments' + @uncountable = Sheep + end + + def test_singular + assert_equal @singular, singular(@record) + end + + def test_singular_for_class + assert_equal @singular, singular(@klass) + end + + def test_plural + assert_equal @plural, plural(@record) + end + + def test_plural_for_class + assert_equal @plural, plural(@klass) + end + + def test_uncountable + assert_equal true, uncountable?(@uncountable) + assert_equal false, uncountable?(@klass) + end + + private + def method_missing(method, *args) + ActiveModel::Naming.send(method, *args) + end +end -- cgit v1.2.3 From c565f0060aab85db60390220679c5df419998f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 11:46:38 +0200 Subject: No need to delegate. Simply include the whole RecordIdentifier module. --- actionpack/lib/abstract_controller.rb | 1 + .../lib/action_controller/record_identifier.rb | 2 ++ actionpack/lib/action_dispatch.rb | 2 ++ actionpack/lib/action_view.rb | 1 + actionpack/lib/action_view/helpers.rb | 2 -- .../helpers/record_identification_helper.rb | 23 ---------------------- .../lib/action_view/helpers/record_tag_helper.rb | 4 ++++ 7 files changed, 10 insertions(+), 25 deletions(-) delete mode 100644 actionpack/lib/action_view/helpers/record_identification_helper.rb diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 5990a1bbd0..c565c940a1 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -1,6 +1,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +require 'action_pack' require 'active_support/ruby/shim' require 'active_support/dependencies/autoload' require 'active_support/core_ext/class/attribute' diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index 975b03c0c9..3de40b0de3 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -67,6 +67,8 @@ module ActionController end end + protected + # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id. # This can be overwritten to customize the default generated string representation if desired. # If you need to read back a key from a dom_id in order to query for the underlying database record, diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 1da4a0d4be..9bb0471ffc 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -27,6 +27,8 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.inc require 'active_support' require 'active_support/dependencies/autoload' +require 'action_pack' +require 'active_model' require 'rack' module Rack diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 9f56cca869..c0d7423682 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -23,6 +23,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + require 'active_support/ruby/shim' require 'active_support/core_ext/class/attribute_accessors' diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index ba3bdd0d18..b7ffa345cc 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -20,7 +20,6 @@ module ActionView #:nodoc: autoload :NumberHelper autoload :PrototypeHelper autoload :RawOutputHelper - autoload :RecordIdentificationHelper autoload :RecordTagHelper autoload :SanitizeHelper autoload :ScriptaculousHelper @@ -51,7 +50,6 @@ module ActionView #:nodoc: include NumberHelper include PrototypeHelper include RawOutputHelper - include RecordIdentificationHelper include RecordTagHelper include SanitizeHelper include ScriptaculousHelper diff --git a/actionpack/lib/action_view/helpers/record_identification_helper.rb b/actionpack/lib/action_view/helpers/record_identification_helper.rb deleted file mode 100644 index eea5de9bc6..0000000000 --- a/actionpack/lib/action_view/helpers/record_identification_helper.rb +++ /dev/null @@ -1,23 +0,0 @@ -module ActionView - # = Action View Record Identification Helpers - # - # See ActionController::RecordIdentifier for documentation on these methods. - module Helpers - module RecordIdentificationHelper - # See ActionController::RecordIdentifier.partial_path -- this is just a delegate to that for convenient access in the view. - def partial_path(*args, &block) - ActionController::RecordIdentifier.partial_path(*args, &block) - end - - # See ActionController::RecordIdentifier.dom_class -- this is just a delegate to that for convenient access in the view. - def dom_class(*args, &block) - ActionController::RecordIdentifier.dom_class(*args, &block) - end - - # See ActionController::RecordIdentifier.dom_id -- this is just a delegate to that for convenient access in the view. - def dom_id(*args, &block) - ActionController::RecordIdentifier.dom_id(*args, &block) - end - end - end -end diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 7433f08777..e4a9210cde 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -1,7 +1,11 @@ +require 'action_controller/record_identifier' + module ActionView # = Action View Record Tag Helpers module Helpers module RecordTagHelper + include ActionController::RecordIdentifier + # Produces a wrapper DIV element with id and class parameters that # relate to the specified Active Record object. Usage example: # -- cgit v1.2.3 From f1082bd51eb575f22f86b78f347ab75a4b2bff1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 11:56:28 +0200 Subject: Remove old install.rb files. --- actionmailer/README | 22 +++------------------- actionmailer/install.rb | 30 ------------------------------ actionpack/README | 17 ++++------------- actionpack/install.rb | 30 ------------------------------ activerecord/README | 21 +++------------------ activerecord/install.rb | 30 ------------------------------ activesupport/README | 15 +++------------ activesupport/install.rb | 30 ------------------------------ 8 files changed, 13 insertions(+), 182 deletions(-) delete mode 100644 actionmailer/install.rb delete mode 100644 actionpack/install.rb delete mode 100644 activerecord/install.rb delete mode 100644 activesupport/install.rb diff --git a/actionmailer/README b/actionmailer/README index 2a4d507d8a..3dd56a6fd8 100644 --- a/actionmailer/README +++ b/actionmailer/README @@ -126,36 +126,20 @@ or is accessible as a GEM. Additionally, Action Mailer requires the Mail gem, http://github.com/mikel/mail -== Bundled software - -* Text::Format 0.63 by Austin Ziegler released under OpenSource - Read more on http://www.halostatue.ca/ruby/Text__Format.html - == Download -The latest version of Action Mailer can be found at +The latest version of Action Mailer can be installed with Rubygems: -* http://rubyforge.org/project/showfiles.php?group_id=361 +* gem install actionmailer Documentation can be found at -* http://actionmailer.rubyonrails.org - - -== Installation - -You can install Action Mailer with the following command. - - % [sudo] ruby install.rb - -from its distribution directory. - +* http://api.rubyonrails.org == License Action Mailer is released under the MIT license. - == Support The Action Mailer homepage is http://www.rubyonrails.org. You can find diff --git a/actionmailer/install.rb b/actionmailer/install.rb deleted file mode 100644 index 8d7c140c3b..0000000000 --- a/actionmailer/install.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rbconfig' -require 'find' -require 'ftools' - -include Config - -# this was adapted from rdoc's install.rb by way of Log4r - -$sitedir = CONFIG["sitelibdir"] -unless $sitedir - version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"] - $libdir = File.join(CONFIG["libdir"], "ruby", version) - $sitedir = $:.find {|x| x =~ /site_ruby/ } - if !$sitedir - $sitedir = File.join($libdir, "site_ruby") - elsif $sitedir !~ Regexp.quote(version) - $sitedir = File.join($sitedir, version) - end -end - -# the actual gruntwork -Dir.chdir("lib") - -Find.find("action_mailer", "action_mailer.rb") { |f| - if f[-3..-1] == ".rb" - File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true) - else - File::makedirs(File.join($sitedir, *f.split(/\//))) - end -} diff --git a/actionpack/README b/actionpack/README index 1a59f728cc..20cc09c26f 100644 --- a/actionpack/README +++ b/actionpack/README @@ -366,22 +366,13 @@ an URL such as /weblog/5 (where 5 is the id of the post). == Download -The latest version of Action Pack can be found at +The latest version of Action Pack can be installed with Rubygems: -* http://rubyforge.org/project/showfiles.php?group_id=249 +* gem install actionpack -Documentation can be found at +Documentation can be found at -* http://api.rubyonrails.com - - -== Installation - -You can install Action Pack with the following command. - - % [sudo] ruby install.rb - -from its distribution directory. +* http://api.rubyonrails.org == License diff --git a/actionpack/install.rb b/actionpack/install.rb deleted file mode 100644 index d3b83c3b00..0000000000 --- a/actionpack/install.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rbconfig' -require 'find' -require 'ftools' - -include Config - -# this was adapted from rdoc's install.rb by way of Log4r - -$sitedir = CONFIG["sitelibdir"] -unless $sitedir - version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"] - $libdir = File.join(CONFIG["libdir"], "ruby", version) - $sitedir = $:.find {|x| x =~ /site_ruby/ } - if !$sitedir - $sitedir = File.join($libdir, "site_ruby") - elsif $sitedir !~ Regexp.quote(version) - $sitedir = File.join($sitedir, version) - end -end - -# the actual gruntwork -Dir.chdir("lib") - -Find.find("action_controller", "action_controller.rb", "action_view", "action_view.rb") { |f| - if f[-3..-1] == ".rb" - File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true) - else - File::makedirs(File.join($sitedir, *f.split(/\//))) - end -} \ No newline at end of file diff --git a/activerecord/README b/activerecord/README index d68eb28a64..0446180207 100644 --- a/activerecord/README +++ b/activerecord/README @@ -309,28 +309,13 @@ Admit the Database: == Download -The latest version of Active Record can be found at +The latest version of Active Record can be installed with Rubygems: -* http://rubyforge.org/project/showfiles.php?group_id=182 +* gem install activerecord Documentation can be found at -* http://ar.rubyonrails.com - - -== Installation - -The prefered method of installing Active Record is through its GEM file. You'll need to have -RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have, -then use: - - % [sudo] gem install activerecord-1.10.0.gem - -You can also install Active Record the old-fashioned way with the following command: - - % [sudo] ruby install.rb - -from its distribution directory. +* http://api.rubyonrails.org == License diff --git a/activerecord/install.rb b/activerecord/install.rb deleted file mode 100644 index c87398b1f4..0000000000 --- a/activerecord/install.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rbconfig' -require 'find' -require 'ftools' - -include Config - -# this was adapted from rdoc's install.rb by ways of Log4r - -$sitedir = CONFIG["sitelibdir"] -unless $sitedir - version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"] - $libdir = File.join(CONFIG["libdir"], "ruby", version) - $sitedir = $:.find {|x| x =~ /site_ruby/ } - if !$sitedir - $sitedir = File.join($libdir, "site_ruby") - elsif $sitedir !~ Regexp.quote(version) - $sitedir = File.join($sitedir, version) - end -end - -# the actual gruntwork -Dir.chdir("lib") - -Find.find("active_record", "active_record.rb") { |f| - if f[-3..-1] == ".rb" - File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true) - else - File::makedirs(File.join($sitedir, *f.split(/\//))) - end -} diff --git a/activesupport/README b/activesupport/README index 9fb9a80cbe..aa86f1fd65 100644 --- a/activesupport/README +++ b/activesupport/README @@ -7,22 +7,13 @@ Ruby sweeter. == Download -The latest version of Active Support can be found at +The latest version of Active Support can be installed with Rubygems: -* http://rubyforge.org/project/showfiles.php?group_id=182 +* gem install activesupport Documentation can be found at -* http://as.rubyonrails.com - - -== Installation - -The preferred method of installing Active Support is through its GEM file. You'll need to have -RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have it, -then use: - - % [sudo] gem install activesupport-1.0.0.gem +* http://api.rubyonrails.org == License diff --git a/activesupport/install.rb b/activesupport/install.rb deleted file mode 100644 index 9c54d8c1a9..0000000000 --- a/activesupport/install.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rbconfig' -require 'find' -require 'ftools' - -include Config - -# this was adapted from rdoc's install.rb by ways of Log4r - -$sitedir = CONFIG["sitelibdir"] -unless $sitedir - version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"] - $libdir = File.join(CONFIG["libdir"], "ruby", version) - $sitedir = $:.find {|x| x =~ /site_ruby/ } - if !$sitedir - $sitedir = File.join($libdir, "site_ruby") - elsif $sitedir !~ Regexp.quote(version) - $sitedir = File.join($sitedir, version) - end -end - -# the actual gruntwork -Dir.chdir("lib") - -Find.find("active_support", "active_support.rb") { |f| - if f[-3..-1] == ".rb" - File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true) - else - File::makedirs(File.join($sitedir, *f.split(/\//))) - end -} -- 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(-) 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. --- README | 58 +++++++++++ actionpack/README | 9 -- railties/README | 280 +++--------------------------------------------------- railties/Rakefile | 7 -- 4 files changed, 70 insertions(+), 284 deletions(-) create mode 100644 README diff --git a/README b/README new file mode 100644 index 0000000000..6198000279 --- /dev/null +++ b/README @@ -0,0 +1,58 @@ +== Welcome to Rails + +Rails is a web-application framework that includes everything needed to create +database-backed web applications according to the Model-View-Control pattern. + +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. + +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. + +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. + + +== Getting Started + +1. Install Rails at the command prompt if you haven't yet: + gem install rails + +2. At the command prompt, create a new Rails application: + rails new myapp (where myapp is the application name) + +3. Change directory to myapp and start the web server: + cd myapp; rails server (run with --help for options) + +4. Go to http://localhost:3000/ and you'll see: + "Welcome aboard: You're riding Ruby on Rails!" + +5. Follow the guidelines to start developing your application. You can find +the following resources handy: + +* The README file created within your application +* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html +* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ + + +== Contributing + +Check out the contributing guide at http://edgeguides.rubyonrails.org/contributing_to_rails.html + + +== License + +Ruby on Rails is released under the MIT license. diff --git a/actionpack/README b/actionpack/README index 20cc09c26f..272feb63d0 100644 --- a/actionpack/README +++ b/actionpack/README @@ -19,15 +19,6 @@ the HTML. To avoid cluttering the templates with code, a bunch of helper classes provide common behavior for forms, dates, and strings. And it's easy to add specific helpers to keep the separation as the application evolves. -Note: Some of the features, such as scaffolding and form building, are tied to -ActiveRecord[http://activerecord.rubyonrails.org] (an object-relational -mapping package), but that doesn't mean that Action Pack depends on Active -Record. Action Pack is an independent package that can be used with any sort -of backend (Instiki[http://www.instiki.org], which is based on an older version -of Action Pack, used Madeleine for example). Read more about the role Action -Pack can play when used together with Active Record on -http://www.rubyonrails.org. - A short rundown of the major features: * Actions grouped in controller as methods instead of separate command objects 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. --- README | 58 ------- README.rdoc | 58 +++++++ Rakefile | 16 +- actionmailer/README | 151 ------------------ actionmailer/README.rdoc | 151 ++++++++++++++++++ actionmailer/Rakefile | 2 +- actionpack/README | 382 --------------------------------------------- actionpack/README.rdoc | 382 +++++++++++++++++++++++++++++++++++++++++++++ actionpack/Rakefile | 2 +- activemodel/README | 206 ------------------------ activemodel/README.rdoc | 206 ++++++++++++++++++++++++ activemodel/Rakefile | 2 +- activerecord/README | 336 --------------------------------------- activerecord/README.rdoc | 336 +++++++++++++++++++++++++++++++++++++++ activerecord/Rakefile | 2 +- activeresource/README | 165 -------------------- activeresource/README.rdoc | 165 ++++++++++++++++++++ activeresource/Rakefile | 2 +- activesupport/README | 34 ---- activesupport/README.rdoc | 34 ++++ activesupport/Rakefile | 2 +- railties/README | 25 --- railties/README.rdoc | 25 +++ railties/Rakefile | 2 +- 24 files changed, 1372 insertions(+), 1372 deletions(-) delete mode 100644 README create mode 100644 README.rdoc delete mode 100644 actionmailer/README create mode 100644 actionmailer/README.rdoc delete mode 100644 actionpack/README create mode 100644 actionpack/README.rdoc delete mode 100644 activemodel/README create mode 100644 activemodel/README.rdoc delete mode 100644 activerecord/README create mode 100644 activerecord/README.rdoc delete mode 100644 activeresource/README create mode 100644 activeresource/README.rdoc delete mode 100644 activesupport/README create mode 100644 activesupport/README.rdoc delete mode 100644 railties/README create mode 100644 railties/README.rdoc diff --git a/README b/README deleted file mode 100644 index 6198000279..0000000000 --- a/README +++ /dev/null @@ -1,58 +0,0 @@ -== Welcome to Rails - -Rails is a web-application framework that includes everything needed to create -database-backed web applications according to the Model-View-Control pattern. - -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. - -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. - -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. - - -== Getting Started - -1. Install Rails at the command prompt if you haven't yet: - gem install rails - -2. At the command prompt, create a new Rails application: - rails new myapp (where myapp is the application name) - -3. Change directory to myapp and start the web server: - cd myapp; rails server (run with --help for options) - -4. Go to http://localhost:3000/ and you'll see: - "Welcome aboard: You're riding Ruby on Rails!" - -5. Follow the guidelines to start developing your application. You can find -the following resources handy: - -* The README file created within your application -* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html -* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ - - -== Contributing - -Check out the contributing guide at http://edgeguides.rubyonrails.org/contributing_to_rails.html - - -== License - -Ruby on Rails is released under the MIT license. diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000000..6198000279 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,58 @@ +== Welcome to Rails + +Rails is a web-application framework that includes everything needed to create +database-backed web applications according to the Model-View-Control pattern. + +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. + +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. + +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. + + +== Getting Started + +1. Install Rails at the command prompt if you haven't yet: + gem install rails + +2. At the command prompt, create a new Rails application: + rails new myapp (where myapp is the application name) + +3. Change directory to myapp and start the web server: + cd myapp; rails server (run with --help for options) + +4. Go to http://localhost:3000/ and you'll see: + "Welcome aboard: You're riding Ruby on Rails!" + +5. Follow the guidelines to start developing your application. You can find +the following resources handy: + +* The README file created within your application +* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html +* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ + + +== Contributing + +Check out the contributing guide at http://edgeguides.rubyonrails.org/contributing_to_rails.html + + +== License + +Ruby on Rails is released under the MIT license. diff --git a/Rakefile b/Rakefile index e608af0319..c49942c0bb 100644 --- a/Rakefile +++ b/Rakefile @@ -69,7 +69,7 @@ Rake::RDocTask.new do |rdoc| rdoc.options << '--line-numbers' << '--inline-source' rdoc.options << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' - rdoc.options << '--main' << 'railties/README' + rdoc.options << '--main' << 'README.rdoc' # Workaround: RDoc assumes that rdoc.template can be required, and that # rdoc.template.upcase is a constant living in RDoc::Generator::HTML @@ -83,38 +83,38 @@ Rake::RDocTask.new do |rdoc| rdoc.rdoc_files.include('railties/CHANGELOG') rdoc.rdoc_files.include('railties/MIT-LICENSE') - rdoc.rdoc_files.include('railties/README') + rdoc.rdoc_files.include('railties/README.rdoc') rdoc.rdoc_files.include('railties/lib/**/*.rb') rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/*') - rdoc.rdoc_files.include('activerecord/README') + rdoc.rdoc_files.include('activerecord/README.rdoc') rdoc.rdoc_files.include('activerecord/CHANGELOG') rdoc.rdoc_files.include('activerecord/lib/active_record/**/*.rb') rdoc.rdoc_files.exclude('activerecord/lib/active_record/vendor/*') - rdoc.rdoc_files.include('activeresource/README') + rdoc.rdoc_files.include('activeresource/README.rdoc') rdoc.rdoc_files.include('activeresource/CHANGELOG') rdoc.rdoc_files.include('activeresource/lib/active_resource.rb') rdoc.rdoc_files.include('activeresource/lib/active_resource/*') - rdoc.rdoc_files.include('actionpack/README') + rdoc.rdoc_files.include('actionpack/README.rdoc') rdoc.rdoc_files.include('actionpack/CHANGELOG') rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb') rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb') rdoc.rdoc_files.include('actionpack/lib/action_view/**/*.rb') rdoc.rdoc_files.exclude('actionpack/lib/action_controller/vendor/*') - rdoc.rdoc_files.include('actionmailer/README') + rdoc.rdoc_files.include('actionmailer/README.rdoc') rdoc.rdoc_files.include('actionmailer/CHANGELOG') rdoc.rdoc_files.include('actionmailer/lib/action_mailer/base.rb') rdoc.rdoc_files.exclude('actionmailer/lib/action_mailer/vendor/*') - rdoc.rdoc_files.include('activesupport/README') + rdoc.rdoc_files.include('activesupport/README.rdoc') rdoc.rdoc_files.include('activesupport/CHANGELOG') rdoc.rdoc_files.include('activesupport/lib/active_support/**/*.rb') rdoc.rdoc_files.exclude('activesupport/lib/active_support/vendor/*') - rdoc.rdoc_files.include('activemodel/README') + rdoc.rdoc_files.include('activemodel/README.rdoc') rdoc.rdoc_files.include('activemodel/CHANGELOG') rdoc.rdoc_files.include('activemodel/lib/active_model/**/*.rb') end diff --git a/actionmailer/README b/actionmailer/README deleted file mode 100644 index 3dd56a6fd8..0000000000 --- a/actionmailer/README +++ /dev/null @@ -1,151 +0,0 @@ -= Action Mailer -- Easy email delivery and testing - -Action Mailer is a framework for designing email-service layers. These layers -are used to consolidate code for sending out forgotten passwords, welcome -wishes on signup, invoices for billing, and any other use case that requires -a written notification to either a person or another system. - -Action Mailer is in essence a wrapper around Action Controller and the -Mail gem. It provides a way to make emails using templates in the same -way that Action Controller renders views using templates. - -Additionally, an Action Mailer class can be used to process incoming email, -such as allowing a weblog to accept new posts from an email (which could even -have been sent from a phone). - -== Sending emails - -The framework works by initializing any instance variables you want to be -available in the email template, followed by a call to +mail+ to deliver -the email. - -This can be as simple as: - - class Notifier < ActionMailer::Base - delivers_from 'system@loudthinking.com' - - def welcome(recipient) - @recipient = recipient - mail(:to => recipient, - :subject => "[Signed up] Welcome #{recipient}") - end - end - -The body of the email is created by using an Action View template (regular -ERb) that has the instance variables that are declared in the mailer action. - -So the corresponding body template for the method above could look like this: - - Hello there, - - Mr. <%= @recipient %> - - Thank you for signing up! - -And if the recipient was given as "david@loudthinking.com", the email -generated would look like this: - - Date: Mon, 25 Jan 2010 22:48:09 +1100 - From: system@loudthinking.com - To: david@loudthinking.com - Message-ID: <4b5d84f9dd6a5_7380800b81ac29578@void.loudthinking.com.mail> - Subject: [Signed up] Welcome david@loudthinking.com - Mime-Version: 1.0 - Content-Type: text/plain; - charset="US-ASCII"; - Content-Transfer-Encoding: 7bit - - Hello there, - - Mr. david@loudthinking.com - -In previous version of rails you would call create_method_name and -deliver_method_name. Rails 3.0 has a much simpler interface, you -simply call the method and optionally call +deliver+ on the return value. - -Calling the method returns a Mail Message object: - - message = Notifier.welcome #=> Returns a Mail::Message object - message.deliver #=> delivers the email - -Or you can just chain the methods together like: - - Notifier.welcome.deliver # Creates the email and sends it immediately - -== Receiving emails - -To receive emails, you need to implement a public instance method called receive that takes a -tmail object as its single parameter. The Action Mailer framework has a corresponding class method, -which is also called receive, that accepts a raw, unprocessed email as a string, which it then turns -into the tmail object and calls the receive instance method. - -Example: - - class Mailman < ActionMailer::Base - def receive(email) - page = Page.find_by_address(email.to.first) - page.emails.create( - :subject => email.subject, :body => email.body - ) - - if email.has_attachments? - for attachment in email.attachments - page.attachments.create({ - :file => attachment, :description => email.subject - }) - end - end - end - end - -This Mailman can be the target for Postfix or other MTAs. In Rails, you would use the runner in the -trivial case like this: - - rails runner 'Mailman.receive(STDIN.read)' - -However, invoking Rails in the runner for each mail to be received is very resource intensive. A single -instance of Rails should be run within a daemon if it is going to be utilized to process more than just -a limited number of email. - -== Configuration - -The Base class has the full list of configuration options. Here's an example: - - ActionMailer::Base.smtp_settings = { - :address => 'smtp.yourserver.com', # default: localhost - :port => '25', # default: 25 - :user_name => 'user', - :password => 'pass', - :authentication => :plain # :plain, :login or :cram_md5 - } - -== Dependencies - -Action Mailer requires that the Action Pack is either available to be required immediately -or is accessible as a GEM. - -Additionally, Action Mailer requires the Mail gem, http://github.com/mikel/mail - -== Download - -The latest version of Action Mailer can be installed with Rubygems: - -* gem install actionmailer - -Documentation can be found at - -* http://api.rubyonrails.org - -== License - -Action Mailer is released under the MIT license. - -== Support - -The Action Mailer homepage is http://www.rubyonrails.org. You can find -the Action Mailer RubyForge page at http://rubyforge.org/projects/actionmailer. -And as Jim from Rake says: - - Feel free to submit commits or feature requests. If you send a patch, - remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests. diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc new file mode 100644 index 0000000000..3dd56a6fd8 --- /dev/null +++ b/actionmailer/README.rdoc @@ -0,0 +1,151 @@ += Action Mailer -- Easy email delivery and testing + +Action Mailer is a framework for designing email-service layers. These layers +are used to consolidate code for sending out forgotten passwords, welcome +wishes on signup, invoices for billing, and any other use case that requires +a written notification to either a person or another system. + +Action Mailer is in essence a wrapper around Action Controller and the +Mail gem. It provides a way to make emails using templates in the same +way that Action Controller renders views using templates. + +Additionally, an Action Mailer class can be used to process incoming email, +such as allowing a weblog to accept new posts from an email (which could even +have been sent from a phone). + +== Sending emails + +The framework works by initializing any instance variables you want to be +available in the email template, followed by a call to +mail+ to deliver +the email. + +This can be as simple as: + + class Notifier < ActionMailer::Base + delivers_from 'system@loudthinking.com' + + def welcome(recipient) + @recipient = recipient + mail(:to => recipient, + :subject => "[Signed up] Welcome #{recipient}") + end + end + +The body of the email is created by using an Action View template (regular +ERb) that has the instance variables that are declared in the mailer action. + +So the corresponding body template for the method above could look like this: + + Hello there, + + Mr. <%= @recipient %> + + Thank you for signing up! + +And if the recipient was given as "david@loudthinking.com", the email +generated would look like this: + + Date: Mon, 25 Jan 2010 22:48:09 +1100 + From: system@loudthinking.com + To: david@loudthinking.com + Message-ID: <4b5d84f9dd6a5_7380800b81ac29578@void.loudthinking.com.mail> + Subject: [Signed up] Welcome david@loudthinking.com + Mime-Version: 1.0 + Content-Type: text/plain; + charset="US-ASCII"; + Content-Transfer-Encoding: 7bit + + Hello there, + + Mr. david@loudthinking.com + +In previous version of rails you would call create_method_name and +deliver_method_name. Rails 3.0 has a much simpler interface, you +simply call the method and optionally call +deliver+ on the return value. + +Calling the method returns a Mail Message object: + + message = Notifier.welcome #=> Returns a Mail::Message object + message.deliver #=> delivers the email + +Or you can just chain the methods together like: + + Notifier.welcome.deliver # Creates the email and sends it immediately + +== Receiving emails + +To receive emails, you need to implement a public instance method called receive that takes a +tmail object as its single parameter. The Action Mailer framework has a corresponding class method, +which is also called receive, that accepts a raw, unprocessed email as a string, which it then turns +into the tmail object and calls the receive instance method. + +Example: + + class Mailman < ActionMailer::Base + def receive(email) + page = Page.find_by_address(email.to.first) + page.emails.create( + :subject => email.subject, :body => email.body + ) + + if email.has_attachments? + for attachment in email.attachments + page.attachments.create({ + :file => attachment, :description => email.subject + }) + end + end + end + end + +This Mailman can be the target for Postfix or other MTAs. In Rails, you would use the runner in the +trivial case like this: + + rails runner 'Mailman.receive(STDIN.read)' + +However, invoking Rails in the runner for each mail to be received is very resource intensive. A single +instance of Rails should be run within a daemon if it is going to be utilized to process more than just +a limited number of email. + +== Configuration + +The Base class has the full list of configuration options. Here's an example: + + ActionMailer::Base.smtp_settings = { + :address => 'smtp.yourserver.com', # default: localhost + :port => '25', # default: 25 + :user_name => 'user', + :password => 'pass', + :authentication => :plain # :plain, :login or :cram_md5 + } + +== Dependencies + +Action Mailer requires that the Action Pack is either available to be required immediately +or is accessible as a GEM. + +Additionally, Action Mailer requires the Mail gem, http://github.com/mikel/mail + +== Download + +The latest version of Action Mailer can be installed with Rubygems: + +* gem install actionmailer + +Documentation can be found at + +* http://api.rubyonrails.org + +== License + +Action Mailer is released under the MIT license. + +== Support + +The Action Mailer homepage is http://www.rubyonrails.org. You can find +the Action Mailer RubyForge page at http://rubyforge.org/projects/actionmailer. +And as Jim from Rake says: + + Feel free to submit commits or feature requests. If you send a patch, + remember to update the corresponding unit tests. If fact, I prefer + new feature to be submitted in the form of new unit tests. diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index f20e7ea928..98aeae9818 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -32,7 +32,7 @@ Rake::RDocTask.new { |rdoc| rdoc.options << '--line-numbers' << '--inline-source' << '-A 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/action_mailer.rb') rdoc.rdoc_files.include('lib/action_mailer/*.rb') rdoc.rdoc_files.include('lib/action_mailer/delivery_method/*.rb') diff --git a/actionpack/README b/actionpack/README deleted file mode 100644 index 272feb63d0..0000000000 --- a/actionpack/README +++ /dev/null @@ -1,382 +0,0 @@ -= Action Pack -- On rails from request to response - -Action Pack splits the response to a web request into a controller part -(performing the logic) and a view part (rendering a template). This two-step -approach is known as an action, which will normally create, read, update, or -delete (CRUD for short) some sort of model part (often backed by a database) -before choosing either to render a template or redirecting to another action. - -Action Pack implements these actions as public methods on Action Controllers -and uses Action Views to implement the template rendering. Action Controllers -are then responsible for handling all the actions relating to a certain part -of an application. This grouping usually consists of actions for lists and for -CRUDs revolving around a single (or a few) model objects. So ContactsController -would be responsible for listing contacts, creating, deleting, and updating -contacts. A WeblogController could be responsible for both posts and comments. - -Action View templates are written using embedded Ruby in tags mingled in with -the HTML. To avoid cluttering the templates with code, a bunch of helper -classes provide common behavior for forms, dates, and strings. And it's easy -to add specific helpers to keep the separation as the application evolves. - -A short rundown of the major features: - -* Actions grouped in controller as methods instead of separate command objects - and can therefore share helper methods - - CustomersController < ActionController::Base - def show - @customer = find_customer - end - - def update - @customer = find_customer - @customer.attributes = params[:customer] - @customer.save ? - redirect_to(:action => "show") : - render(:action => "edit") - end - - private - def find_customer() Customer.find(params[:id]) end - end - - {Learn more}[link:classes/ActionController/Base.html] - - -* Embedded Ruby for templates (no new "easy" template language) - - <% for post in @posts %> - Title: <%= post.title %> - <% end %> - - All post titles: <%= @posts.collect{ |p| p.title }.join ", " %> - - <% unless @person.is_client? %> - Not for clients to see... - <% end %> - - {Learn more}[link:classes/ActionView.html] - - -* Builder-based templates (great for XML content, like RSS) - - xml.rss("version" => "2.0") do - xml.channel do - xml.title(@feed_title) - xml.link(@url) - xml.description "Basecamp: Recent items" - xml.language "en-us" - xml.ttl "40" - - for item in @recent_items - xml.item do - xml.title(item_title(item)) - xml.description(item_description(item)) - xml.pubDate(item_pubDate(item)) - xml.guid(@recent_items.url(item)) - xml.link(@recent_items.url(item)) - end - end - end - end - - {Learn more}[link:classes/ActionView/Base.html] - - -* Filters for pre and post processing of the response (as methods, procs, and classes) - - class WeblogController < ActionController::Base - before_filter :authenticate, :cache, :audit - after_filter { |c| c.response.body = Gzip::compress(c.response.body) } - after_filter LocalizeFilter - - def index - # Before this action is run, the user will be authenticated, the cache - # will be examined to see if a valid copy of the results already - # exists, and the action will be logged for auditing. - - # After this action has run, the output will first be localized then - # compressed to minimize bandwidth usage - end - - private - def authenticate - # Implement the filter with full access to both request and response - end - end - - {Learn more}[link:classes/ActionController/Filters/ClassMethods.html] - - -* Helpers for forms, dates, action links, and text - - <%= text_field "post", "title", "size" => 30 %> - <%= html_date_select(Date.today) %> - <%= link_to "New post", :controller => "post", :action => "new" %> - <%= truncate(post.title, :length => 25) %> - - {Learn more}[link:classes/ActionView/Helpers.html] - - -* Layout sharing for template reuse (think simple version of Struts - Tiles[http://jakarta.apache.org/struts/userGuide/dev_tiles.html]) - - class WeblogController < ActionController::Base - layout "weblog_layout" - - def hello_world - end - end - - Layout file (called weblog_layout): - <%= yield %> - - Template for hello_world action: -

Hello world

- - Result of running hello_world action: -

Hello world

- - {Learn more}[link:classes/ActionController/Layout/ClassMethods.html] - - -* Routing makes pretty urls incredibly easy - - map.connect 'clients/:client_name/:project_name/:controller/:action' - - Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with - { "client_name" => "37signals", "project_name" => "basecamp" } in params[:params] - - From that URL, you can rewrite the redirect in a number of ways: - - redirect_to(:action => "edit") => - /clients/37signals/basecamp/project/dash - - redirect_to(:client_name => "nextangle", :project_name => "rails") => - /clients/nextangle/rails/project/dash - - {Learn more}[link:classes/ActionController/Base.html] - - -* Easy testing of both controller and rendered template through ActionController::TestCase - - class LoginControllerTest < ActionController::TestCase - def test_failing_authenticate - process :authenticate, :user_name => "nop", :password => "" - assert flash.has_key?(:alert) - assert_redirected_to :action => "index" - end - end - - {Learn more}[link:classes/ActionController/TestCase.html] - - -* Automated benchmarking and integrated logging - - Started GET "/weblog" for 127.0.0.1 at Fri May 28 00:41:55 - Processing by WeblogController#index as HTML - Rendered weblog/index.html.erb within layouts/application (25.7ms) - Completed 200 OK in 29.3ms - - If Active Record is used as the model, you'll have the database debugging - as well: - - Started POST "/posts" for 127.0.0.1 at Sat Jun 19 14:04:23 - Processing by PostsController#create as HTML - Parameters: {"post"=>{"title"=>"this is good"}} - SQL (0.6ms) INSERT INTO posts (title) VALUES('this is good') - Redirected to http://example.com/posts/5 - Completed 302 Found in 221ms (Views: 215ms | ActiveRecord: 0.6ms) - - You specify a logger through a class method, such as: - - ActionController::Base.logger = Logger.new("Application Log") - ActionController::Base.logger = Log4r::Logger.new("Application Log") - - -* Caching at three levels of granularity (page, action, fragment) - - class WeblogController < ActionController::Base - caches_page :show - caches_action :account - - def show - # the output of the method will be cached as - # ActionController::Base.page_cache_directory + "/weblog/show/n.html" - # and the web server will pick it up without even hitting Rails - end - - def account - # the output of the method will be cached in the fragment store - # but Rails is hit to retrieve it, so filters are run - end - - def update - List.update(params[:list][:id], params[:list]) - expire_page :action => "show", :id => params[:list][:id] - expire_action :action => "account" - redirect_to :action => "show", :id => params[:list][:id] - end - end - - {Learn more}[link:classes/ActionController/Caching.html] - - -* Powerful debugging mechanism for local requests - - All exceptions raised on actions performed on the request of a local user - will be presented with a tailored debugging screen that includes exception - message, stack trace, request parameters, session contents, and the - half-finished response. - - {Learn more}[link:classes/ActionController/Rescue.html] - - -* Scaffolding for Active Record model objects - - class AccountController < ActionController::Base - scaffold :account - end - - The AccountController now has the full CRUD range of actions and default - templates: list, show, destroy, new, create, edit, update - - {Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html] - - -* Form building for Active Record model objects - - The post object has a title (varchar), content (text), and - written_on (date) - - <%= form "post" %> - - ...will generate something like (the selects will have more options, of - course): - -
-

- Title:
- -

-

- Content:
- -

-

- Written on:
- - - -

- - -
- - This form generates a params[:post] array that can be used directly in a save action: - - class WeblogController < ActionController::Base - def create - post = Post.create(params[:post]) - redirect_to :action => "show", :id => post.id - end - end - - {Learn more}[link:classes/ActionView/Helpers/ActiveRecordHelper.html] - - -* Runs on top of WEBrick, Mongrel, CGI, FCGI, and mod_ruby - - -== Simple example (from outside of Rails) - -This example will implement a simple weblog system using inline templates and -an Active Record model. So let's build that WeblogController with just a few -methods: - - require 'action_controller' - require 'post' - - class WeblogController < ActionController::Base - layout "weblog/layout" - - def index - @posts = Post.find(:all) - end - - def show - @post = Post.find(params[:id]) - end - - def new - @post = Post.new - end - - def create - @post = Post.create(params[:post]) - redirect_to :action => "show", :id => @post.id - end - end - - WeblogController::Base.view_paths = [ File.dirname(__FILE__) ] - WeblogController.process_cgi if $0 == __FILE__ - -The last two lines are responsible for telling ActionController where the -template files are located and actually running the controller on a new -request from the web-server (like to be Apache). - -And the templates look like this: - - weblog/layout.html.erb: - - <%= yield %> - - - weblog/index.html.erb: - <% for post in @posts %> -

<%= link_to(post.title, :action => "show", :id => post.id) %>

- <% end %> - - weblog/show.html.erb: -

- <%= @post.title %>
- <%= @post.content %> -

- - weblog/new.html.erb: - <%= form "post" %> - -This simple setup will list all the posts in the system on the index page, -which is called by accessing /weblog/. It uses the form builder for the Active -Record model to make the new screen, which in turn hands everything over to -the create action (that's the default target for the form builder when given a -new model). After creating the post, it'll redirect to the show page using -an URL such as /weblog/5 (where 5 is the id of the post). - - -== Download - -The latest version of Action Pack can be installed with Rubygems: - -* gem install actionpack - -Documentation can be found at - -* http://api.rubyonrails.org - - -== License - -Action Pack is released under the MIT license. - - -== Support - -The Action Pack homepage is http://www.rubyonrails.org. You can find -the Action Pack RubyForge page at http://rubyforge.org/projects/actionpack. -And as Jim from Rake says: - - Feel free to submit commits or feature requests. If you send a patch, - remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests. diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc new file mode 100644 index 0000000000..272feb63d0 --- /dev/null +++ b/actionpack/README.rdoc @@ -0,0 +1,382 @@ += Action Pack -- On rails from request to response + +Action Pack splits the response to a web request into a controller part +(performing the logic) and a view part (rendering a template). This two-step +approach is known as an action, which will normally create, read, update, or +delete (CRUD for short) some sort of model part (often backed by a database) +before choosing either to render a template or redirecting to another action. + +Action Pack implements these actions as public methods on Action Controllers +and uses Action Views to implement the template rendering. Action Controllers +are then responsible for handling all the actions relating to a certain part +of an application. This grouping usually consists of actions for lists and for +CRUDs revolving around a single (or a few) model objects. So ContactsController +would be responsible for listing contacts, creating, deleting, and updating +contacts. A WeblogController could be responsible for both posts and comments. + +Action View templates are written using embedded Ruby in tags mingled in with +the HTML. To avoid cluttering the templates with code, a bunch of helper +classes provide common behavior for forms, dates, and strings. And it's easy +to add specific helpers to keep the separation as the application evolves. + +A short rundown of the major features: + +* Actions grouped in controller as methods instead of separate command objects + and can therefore share helper methods + + CustomersController < ActionController::Base + def show + @customer = find_customer + end + + def update + @customer = find_customer + @customer.attributes = params[:customer] + @customer.save ? + redirect_to(:action => "show") : + render(:action => "edit") + end + + private + def find_customer() Customer.find(params[:id]) end + end + + {Learn more}[link:classes/ActionController/Base.html] + + +* Embedded Ruby for templates (no new "easy" template language) + + <% for post in @posts %> + Title: <%= post.title %> + <% end %> + + All post titles: <%= @posts.collect{ |p| p.title }.join ", " %> + + <% unless @person.is_client? %> + Not for clients to see... + <% end %> + + {Learn more}[link:classes/ActionView.html] + + +* Builder-based templates (great for XML content, like RSS) + + xml.rss("version" => "2.0") do + xml.channel do + xml.title(@feed_title) + xml.link(@url) + xml.description "Basecamp: Recent items" + xml.language "en-us" + xml.ttl "40" + + for item in @recent_items + xml.item do + xml.title(item_title(item)) + xml.description(item_description(item)) + xml.pubDate(item_pubDate(item)) + xml.guid(@recent_items.url(item)) + xml.link(@recent_items.url(item)) + end + end + end + end + + {Learn more}[link:classes/ActionView/Base.html] + + +* Filters for pre and post processing of the response (as methods, procs, and classes) + + class WeblogController < ActionController::Base + before_filter :authenticate, :cache, :audit + after_filter { |c| c.response.body = Gzip::compress(c.response.body) } + after_filter LocalizeFilter + + def index + # Before this action is run, the user will be authenticated, the cache + # will be examined to see if a valid copy of the results already + # exists, and the action will be logged for auditing. + + # After this action has run, the output will first be localized then + # compressed to minimize bandwidth usage + end + + private + def authenticate + # Implement the filter with full access to both request and response + end + end + + {Learn more}[link:classes/ActionController/Filters/ClassMethods.html] + + +* Helpers for forms, dates, action links, and text + + <%= text_field "post", "title", "size" => 30 %> + <%= html_date_select(Date.today) %> + <%= link_to "New post", :controller => "post", :action => "new" %> + <%= truncate(post.title, :length => 25) %> + + {Learn more}[link:classes/ActionView/Helpers.html] + + +* Layout sharing for template reuse (think simple version of Struts + Tiles[http://jakarta.apache.org/struts/userGuide/dev_tiles.html]) + + class WeblogController < ActionController::Base + layout "weblog_layout" + + def hello_world + end + end + + Layout file (called weblog_layout): + <%= yield %> + + Template for hello_world action: +

Hello world

+ + Result of running hello_world action: +

Hello world

+ + {Learn more}[link:classes/ActionController/Layout/ClassMethods.html] + + +* Routing makes pretty urls incredibly easy + + map.connect 'clients/:client_name/:project_name/:controller/:action' + + Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with + { "client_name" => "37signals", "project_name" => "basecamp" } in params[:params] + + From that URL, you can rewrite the redirect in a number of ways: + + redirect_to(:action => "edit") => + /clients/37signals/basecamp/project/dash + + redirect_to(:client_name => "nextangle", :project_name => "rails") => + /clients/nextangle/rails/project/dash + + {Learn more}[link:classes/ActionController/Base.html] + + +* Easy testing of both controller and rendered template through ActionController::TestCase + + class LoginControllerTest < ActionController::TestCase + def test_failing_authenticate + process :authenticate, :user_name => "nop", :password => "" + assert flash.has_key?(:alert) + assert_redirected_to :action => "index" + end + end + + {Learn more}[link:classes/ActionController/TestCase.html] + + +* Automated benchmarking and integrated logging + + Started GET "/weblog" for 127.0.0.1 at Fri May 28 00:41:55 + Processing by WeblogController#index as HTML + Rendered weblog/index.html.erb within layouts/application (25.7ms) + Completed 200 OK in 29.3ms + + If Active Record is used as the model, you'll have the database debugging + as well: + + Started POST "/posts" for 127.0.0.1 at Sat Jun 19 14:04:23 + Processing by PostsController#create as HTML + Parameters: {"post"=>{"title"=>"this is good"}} + SQL (0.6ms) INSERT INTO posts (title) VALUES('this is good') + Redirected to http://example.com/posts/5 + Completed 302 Found in 221ms (Views: 215ms | ActiveRecord: 0.6ms) + + You specify a logger through a class method, such as: + + ActionController::Base.logger = Logger.new("Application Log") + ActionController::Base.logger = Log4r::Logger.new("Application Log") + + +* Caching at three levels of granularity (page, action, fragment) + + class WeblogController < ActionController::Base + caches_page :show + caches_action :account + + def show + # the output of the method will be cached as + # ActionController::Base.page_cache_directory + "/weblog/show/n.html" + # and the web server will pick it up without even hitting Rails + end + + def account + # the output of the method will be cached in the fragment store + # but Rails is hit to retrieve it, so filters are run + end + + def update + List.update(params[:list][:id], params[:list]) + expire_page :action => "show", :id => params[:list][:id] + expire_action :action => "account" + redirect_to :action => "show", :id => params[:list][:id] + end + end + + {Learn more}[link:classes/ActionController/Caching.html] + + +* Powerful debugging mechanism for local requests + + All exceptions raised on actions performed on the request of a local user + will be presented with a tailored debugging screen that includes exception + message, stack trace, request parameters, session contents, and the + half-finished response. + + {Learn more}[link:classes/ActionController/Rescue.html] + + +* Scaffolding for Active Record model objects + + class AccountController < ActionController::Base + scaffold :account + end + + The AccountController now has the full CRUD range of actions and default + templates: list, show, destroy, new, create, edit, update + + {Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html] + + +* Form building for Active Record model objects + + The post object has a title (varchar), content (text), and + written_on (date) + + <%= form "post" %> + + ...will generate something like (the selects will have more options, of + course): + +
+

+ Title:
+ +

+

+ Content:
+ +

+

+ Written on:
+ + + +

+ + +
+ + This form generates a params[:post] array that can be used directly in a save action: + + class WeblogController < ActionController::Base + def create + post = Post.create(params[:post]) + redirect_to :action => "show", :id => post.id + end + end + + {Learn more}[link:classes/ActionView/Helpers/ActiveRecordHelper.html] + + +* Runs on top of WEBrick, Mongrel, CGI, FCGI, and mod_ruby + + +== Simple example (from outside of Rails) + +This example will implement a simple weblog system using inline templates and +an Active Record model. So let's build that WeblogController with just a few +methods: + + require 'action_controller' + require 'post' + + class WeblogController < ActionController::Base + layout "weblog/layout" + + def index + @posts = Post.find(:all) + end + + def show + @post = Post.find(params[:id]) + end + + def new + @post = Post.new + end + + def create + @post = Post.create(params[:post]) + redirect_to :action => "show", :id => @post.id + end + end + + WeblogController::Base.view_paths = [ File.dirname(__FILE__) ] + WeblogController.process_cgi if $0 == __FILE__ + +The last two lines are responsible for telling ActionController where the +template files are located and actually running the controller on a new +request from the web-server (like to be Apache). + +And the templates look like this: + + weblog/layout.html.erb: + + <%= yield %> + + + weblog/index.html.erb: + <% for post in @posts %> +

<%= link_to(post.title, :action => "show", :id => post.id) %>

+ <% end %> + + weblog/show.html.erb: +

+ <%= @post.title %>
+ <%= @post.content %> +

+ + weblog/new.html.erb: + <%= form "post" %> + +This simple setup will list all the posts in the system on the index page, +which is called by accessing /weblog/. It uses the form builder for the Active +Record model to make the new screen, which in turn hands everything over to +the create action (that's the default target for the form builder when given a +new model). After creating the post, it'll redirect to the show page using +an URL such as /weblog/5 (where 5 is the id of the post). + + +== Download + +The latest version of Action Pack can be installed with Rubygems: + +* gem install actionpack + +Documentation can be found at + +* http://api.rubyonrails.org + + +== License + +Action Pack is released under the MIT license. + + +== Support + +The Action Pack homepage is http://www.rubyonrails.org. You can find +the Action Pack RubyForge page at http://rubyforge.org/projects/actionpack. +And as Jim from Rake says: + + Feel free to submit commits or feature requests. If you send a patch, + remember to update the corresponding unit tests. If fact, I prefer + new feature to be submitted in the form of new unit tests. diff --git a/actionpack/Rakefile b/actionpack/Rakefile index aed5278e38..3a988d832d 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -47,7 +47,7 @@ Rake::RDocTask.new { |rdoc| if ENV['DOC_FILES'] rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/)) else - rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG') + rdoc.rdoc_files.include('README.rdoc', 'RUNNING_UNIT_TESTS', 'CHANGELOG') rdoc.rdoc_files.include(Dir['lib/**/*.rb'] - Dir['lib/*/vendor/**/*.rb']) rdoc.rdoc_files.exclude('lib/actionpack.rb') diff --git a/activemodel/README b/activemodel/README deleted file mode 100644 index 6f162ef408..0000000000 --- a/activemodel/README +++ /dev/null @@ -1,206 +0,0 @@ -= Active Model - defined interfaces for Rails - -Prior to Rails 3.0, if a plugin or gem developer wanted to be able to have -an object interact with Action Pack helpers, it was required to either -copy chunks of code from Rails, or monkey patch entire helpers to make them -handle objects that did not look like Active Record. This generated code -duplication and fragile applications that broke on upgrades. - -Active Model is a solution for this problem. - -Active Model provides a known set of interfaces that your objects can implement -to then present a common interface to the Action Pack helpers. You can include -functionality from the following modules: - -* Adding attribute magic to your objects - - Add prefixes and suffixes to defined attribute methods... - - class Person - include ActiveModel::AttributeMethods - - attribute_method_prefix 'clear_' - define_attribute_methods [:name, :age] - - attr_accessor :name, :age - - def clear_attribute(attr) - send("#{attr}=", nil) - end - end - - ...gives you clear_name, clear_age. - - {Learn more}[link:classes/ActiveModel/AttributeMethods.html] - -* Adding callbacks to your objects - - class Person - extend ActiveModel::Callbacks - define_model_callbacks :create - - def create - _run_create_callbacks do - # Your create action methods here - end - end - end - - ...gives you before_create, around_create and after_create class methods that - wrap your create method. - - {Learn more}[link:classes/ActiveModel/CallBacks.html] - -* For classes that already look like an Active Record object - - class Person - include ActiveModel::Conversion - end - - ...returns the class itself when sent :to_model - - {Learn more}[link:classes/ActiveModel/Conversion.html] - -* Tracking changes in your object - - Provides all the value tracking features implemented by ActiveRecord... - - person = Person.new - person.name # => nil - person.changed? # => false - person.name = 'bob' - person.changed? # => true - person.changed # => ['name'] - person.changes # => { 'name' => [nil, 'bob'] } - person.name = 'robert' - person.save - person.previous_changes # => {'name' => ['bob, 'robert']} - - {Learn more}[link:classes/ActiveModel/Dirty.html] - -* Adding +errors+ support to your object - - Provides the error messages to allow your object to interact with Action Pack - helpers seamlessly... - - class Person - - def initialize - @errors = ActiveModel::Errors.new(self) - end - - attr_accessor :name - attr_reader :errors - - def validate! - errors.add(:name, "can not be nil") if name == nil - end - - def ErrorsPerson.human_attribute_name(attr, options = {}) - "Name" - end - - end - - ... gives you... - - person.errors.full_messages - # => ["Name Can not be nil"] - person.errors.full_messages - # => ["Name Can not be nil"] - - {Learn more}[link:classes/ActiveModel/Errors.html] - -* Testing the compliance of your object - - Use ActiveModel::Lint to test the compliance of your object to the - basic ActiveModel API... - - {Learn more}[link:classes/ActiveModel/Lint/Tests.html] - -* Providing a human face to your object - - ActiveModel::Naming provides your model with the model_name convention - and a human_name attribute... - - class NamedPerson - extend ActiveModel::Naming - end - - ...gives you... - - NamedPerson.model_name #=> "NamedPerson" - NamedPerson.model_name.human #=> "Named person" - - {Learn more}[link:classes/ActiveModel/Naming.html] - -* Adding observer support to your objects - - ActiveModel::Observers allows your object to implement the Observer - pattern in a Rails App and take advantage of all the standard observer - functions. - - {Learn more}[link:classes/ActiveModel/Observer.html] - -* Making your object serializable - - ActiveModel::Serialization provides a standard interface for your object - to provide to_json or to_xml serialization... - - s = SerialPerson.new - s.serializable_hash # => {"name"=>nil} - s.to_json # => "{\"name\":null}" - s.to_xml # => "\n false - - {Learn more}[link:classes/ActiveModel/Validations.html] - -* Make custom validators - - class Person - include ActiveModel::Validations - validates_with HasNameValidator - attr_accessor :name - end - - class HasNameValidator < ActiveModel::Validator - def validate(record) - record.errors[:name] = "must exist" if record.name.blank? - end - end - - p = ValidatorPerson.new - p.valid? #=> false - p.errors.full_messages #=> ["Name must exist"] - p.name = "Bob" - p.valid? #=> true - - {Learn more}[link:classes/ActiveModel/Validator.html] diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc new file mode 100644 index 0000000000..6f162ef408 --- /dev/null +++ b/activemodel/README.rdoc @@ -0,0 +1,206 @@ += Active Model - defined interfaces for Rails + +Prior to Rails 3.0, if a plugin or gem developer wanted to be able to have +an object interact with Action Pack helpers, it was required to either +copy chunks of code from Rails, or monkey patch entire helpers to make them +handle objects that did not look like Active Record. This generated code +duplication and fragile applications that broke on upgrades. + +Active Model is a solution for this problem. + +Active Model provides a known set of interfaces that your objects can implement +to then present a common interface to the Action Pack helpers. You can include +functionality from the following modules: + +* Adding attribute magic to your objects + + Add prefixes and suffixes to defined attribute methods... + + class Person + include ActiveModel::AttributeMethods + + attribute_method_prefix 'clear_' + define_attribute_methods [:name, :age] + + attr_accessor :name, :age + + def clear_attribute(attr) + send("#{attr}=", nil) + end + end + + ...gives you clear_name, clear_age. + + {Learn more}[link:classes/ActiveModel/AttributeMethods.html] + +* Adding callbacks to your objects + + class Person + extend ActiveModel::Callbacks + define_model_callbacks :create + + def create + _run_create_callbacks do + # Your create action methods here + end + end + end + + ...gives you before_create, around_create and after_create class methods that + wrap your create method. + + {Learn more}[link:classes/ActiveModel/CallBacks.html] + +* For classes that already look like an Active Record object + + class Person + include ActiveModel::Conversion + end + + ...returns the class itself when sent :to_model + + {Learn more}[link:classes/ActiveModel/Conversion.html] + +* Tracking changes in your object + + Provides all the value tracking features implemented by ActiveRecord... + + person = Person.new + person.name # => nil + person.changed? # => false + person.name = 'bob' + person.changed? # => true + person.changed # => ['name'] + person.changes # => { 'name' => [nil, 'bob'] } + person.name = 'robert' + person.save + person.previous_changes # => {'name' => ['bob, 'robert']} + + {Learn more}[link:classes/ActiveModel/Dirty.html] + +* Adding +errors+ support to your object + + Provides the error messages to allow your object to interact with Action Pack + helpers seamlessly... + + class Person + + def initialize + @errors = ActiveModel::Errors.new(self) + end + + attr_accessor :name + attr_reader :errors + + def validate! + errors.add(:name, "can not be nil") if name == nil + end + + def ErrorsPerson.human_attribute_name(attr, options = {}) + "Name" + end + + end + + ... gives you... + + person.errors.full_messages + # => ["Name Can not be nil"] + person.errors.full_messages + # => ["Name Can not be nil"] + + {Learn more}[link:classes/ActiveModel/Errors.html] + +* Testing the compliance of your object + + Use ActiveModel::Lint to test the compliance of your object to the + basic ActiveModel API... + + {Learn more}[link:classes/ActiveModel/Lint/Tests.html] + +* Providing a human face to your object + + ActiveModel::Naming provides your model with the model_name convention + and a human_name attribute... + + class NamedPerson + extend ActiveModel::Naming + end + + ...gives you... + + NamedPerson.model_name #=> "NamedPerson" + NamedPerson.model_name.human #=> "Named person" + + {Learn more}[link:classes/ActiveModel/Naming.html] + +* Adding observer support to your objects + + ActiveModel::Observers allows your object to implement the Observer + pattern in a Rails App and take advantage of all the standard observer + functions. + + {Learn more}[link:classes/ActiveModel/Observer.html] + +* Making your object serializable + + ActiveModel::Serialization provides a standard interface for your object + to provide to_json or to_xml serialization... + + s = SerialPerson.new + s.serializable_hash # => {"name"=>nil} + s.to_json # => "{\"name\":null}" + s.to_xml # => "\n false + + {Learn more}[link:classes/ActiveModel/Validations.html] + +* Make custom validators + + class Person + include ActiveModel::Validations + validates_with HasNameValidator + attr_accessor :name + end + + class HasNameValidator < ActiveModel::Validator + def validate(record) + record.errors[:name] = "must exist" if record.name.blank? + end + end + + p = ValidatorPerson.new + p.valid? #=> false + p.errors.full_messages #=> ["Name must exist"] + p.name = "Bob" + p.valid? #=> true + + {Learn more}[link:classes/ActiveModel/Validator.html] diff --git a/activemodel/Rakefile b/activemodel/Rakefile index 1dba664539..4e4bbcee96 100644 --- a/activemodel/Rakefile +++ b/activemodel/Rakefile @@ -32,7 +32,7 @@ Rake::RDocTask.new do |rdoc| rdoc.options << '--line-numbers' << '--inline-source' << '-A 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") end diff --git a/activerecord/README b/activerecord/README deleted file mode 100644 index 0446180207..0000000000 --- a/activerecord/README +++ /dev/null @@ -1,336 +0,0 @@ -= Active Record -- Object-relation mapping put on rails - -Active Record connects business objects and database tables to create a persistable -domain model where logic and data are presented in one wrapping. It's an implementation -of the object-relational mapping (ORM) pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] -by the same name as described by Martin Fowler: - - "An object that wraps a row in a database table or view, encapsulates - the database access, and adds domain logic on that data." - -Active Record's main contribution to the pattern is to relieve the original of two stunting problems: -lack of associations and inheritance. By adding a simple domain language-like set of macros to describe -the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the -gap of functionality between the data mapper and active record approach. - -A short rundown of the major features: - -* Automated mapping between classes and tables, attributes and columns. - - class Product < ActiveRecord::Base; end - - ...is automatically mapped to the table named "products", such as: - - CREATE TABLE products ( - id int(11) NOT NULL auto_increment, - name varchar(255), - PRIMARY KEY (id) - ); - - ...which again gives Product#name and Product#name=(new_name) - - {Learn more}[link:classes/ActiveRecord/Base.html] - - -* Associations between objects controlled by simple meta-programming macros. - - class Firm < ActiveRecord::Base - has_many :clients - has_one :account - belongs_to :conglomorate - end - - {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html] - - -* Aggregations of value objects controlled by simple meta-programming macros. - - class Account < ActiveRecord::Base - composed_of :balance, :class_name => "Money", - :mapping => %w(balance amount) - composed_of :address, - :mapping => [%w(address_street street), %w(address_city city)] - end - - {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html] - - -* Validation rules that can differ for new or existing objects. - - class Account < ActiveRecord::Base - validates_presence_of :subdomain, :name, :email_address, :password - validates_uniqueness_of :subdomain - validates_acceptance_of :terms_of_service, :on => :create - validates_confirmation_of :password, :email_address, :on => :create - end - - {Learn more}[link:classes/ActiveRecord/Validations.html] - -* Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc). - - class Person < ActiveRecord::Base - def before_destroy # is called just before Person#destroy - CreditCard.find(credit_card_id).destroy - end - end - - class Account < ActiveRecord::Base - after_find :eager_load, 'self.class.announce(#{id})' - end - - {Learn more}[link:classes/ActiveRecord/Callbacks.html] - - -* Observers for the entire lifecycle - - class CommentObserver < ActiveRecord::Observer - def after_create(comment) # is called just after Comment#save - Notifications.deliver_new_comment("david@loudthinking.com", comment) - end - end - - {Learn more}[link:classes/ActiveRecord/Observer.html] - - -* Inheritance hierarchies - - class Company < ActiveRecord::Base; end - class Firm < Company; end - class Client < Company; end - class PriorityClient < Client; end - - {Learn more}[link:classes/ActiveRecord/Base.html] - - -* Transactions - - # Database transaction - Account.transaction do - david.withdrawal(100) - mary.deposit(100) - end - - {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html] - - -* Reflections on columns, associations, and aggregations - - reflection = Firm.reflect_on_association(:clients) - reflection.klass # => Client (class) - Firm.columns # Returns an array of column descriptors for the firms table - - {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html] - - -* Direct manipulation (instead of service invocation) - - So instead of (Hibernate[http://www.hibernate.org/] example): - - long pkId = 1234; - DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) ); - // something interesting involving a cat... - sess.save(cat); - sess.flush(); // force the SQL INSERT - - Active Record lets you: - - pkId = 1234 - cat = Cat.find(pkId) - # something even more interesting involving the same cat... - cat.save - - {Learn more}[link:classes/ActiveRecord/Base.html] - - -* Database abstraction through simple adapters (~100 lines) with a shared connector - - ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile") - - ActiveRecord::Base.establish_connection( - :adapter => "mysql", - :host => "localhost", - :username => "me", - :password => "secret", - :database => "activerecord" - ) - - {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for - MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OracleAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html]. - - -* Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc] - - ActiveRecord::Base.logger = Logger.new(STDOUT) - ActiveRecord::Base.logger = Log4r::Logger.new("Application Log") - - -* Database agnostic schema management with Migrations - - class AddSystemSettings < ActiveRecord::Migration - def self.up - create_table :system_settings do |t| - t.string :name - t.string :label - t.text :value - t.string :type - t.integer :position - end - - SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1 - end - - def self.down - drop_table :system_settings - end - end - - {Learn more}[link:classes/ActiveRecord/Migration.html] - -== Simple example (1/2): Defining tables and classes (using MySQL) - -Data definitions are specified only in the database. Active Record queries the database for -the column names (that then serves to determine which attributes are valid) on regular -object instantiation through the new constructor and relies on the column names in the rows -with the finders. - - # CREATE TABLE companies ( - # id int(11) unsigned NOT NULL auto_increment, - # client_of int(11), - # name varchar(255), - # type varchar(100), - # PRIMARY KEY (id) - # ) - -Active Record automatically links the "Company" object to the "companies" table - - class Company < ActiveRecord::Base - has_many :people, :class_name => "Person" - end - - class Firm < Company - has_many :clients - - def people_with_all_clients - clients.inject([]) { |people, client| people + client.people } - end - end - -The foreign_key is only necessary because we didn't use "firm_id" in the data definition - - class Client < Company - belongs_to :firm, :foreign_key => "client_of" - end - - # CREATE TABLE people ( - # id int(11) unsigned NOT NULL auto_increment, - # name text, - # company_id text, - # PRIMARY KEY (id) - # ) - -Active Record will also automatically link the "Person" object to the "people" table - - class Person < ActiveRecord::Base - belongs_to :company - end - -== Simple example (2/2): Using the domain - -Picking a database connection for all the Active Records - - ActiveRecord::Base.establish_connection( - :adapter => "mysql", - :host => "localhost", - :username => "me", - :password => "secret", - :database => "activerecord" - ) - -Create some fixtures - - firm = Firm.new("name" => "Next Angle") - # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") - firm.save - - client = Client.new("name" => "37signals", "client_of" => firm.id) - # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm") - client.save - -Lots of different finders - - # SQL: SELECT * FROM companies WHERE id = 1 - next_angle = Company.find(1) - - # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm' - next_angle = Firm.find(1) - - # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle' - next_angle = Company.find(:first, :conditions => "name = 'Next Angle'") - - next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first - -The supertype, Company, will return subtype instances - - Firm === next_angle - -All the dynamic methods added by the has_many macro - - next_angle.clients.empty? # true - next_angle.clients.size # total number of clients - all_clients = next_angle.clients - -Constrained finds makes access security easier when ID comes from a web-app - - # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2 - thirty_seven_signals = next_angle.clients.find(2) - -Bi-directional associations thanks to the "belongs_to" macro - - thirty_seven_signals.firm.nil? # true - - -== Philosophy - -Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is -object-relational mapping. The prime directive for this mapping has been to minimize -the amount of code needed to build a real-world domain model. This is made possible -by relying on a number of conventions that make it easy for Active Record to infer -complex relations and structures from a minimal amount of explicit direction. - -Convention over Configuration: -* No XML-files! -* Lots of reflection and run-time extension -* Magic is not inherently a bad word - -Admit the Database: -* Lets you drop down to SQL for odd cases and performance -* Doesn't attempt to duplicate or replace data definitions - - -== Download - -The latest version of Active Record can be installed with Rubygems: - -* gem install activerecord - -Documentation can be found at - -* http://api.rubyonrails.org - - -== License - -Active Record is released under the MIT license. - - -== Support - -The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record -RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says: - - Feel free to submit commits or feature requests. If you send a patch, - remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests. - -For other information, feel free to ask on the rubyonrails-talk -(http://groups.google.com/group/rubyonrails-talk) mailing list. diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc new file mode 100644 index 0000000000..0446180207 --- /dev/null +++ b/activerecord/README.rdoc @@ -0,0 +1,336 @@ += Active Record -- Object-relation mapping put on rails + +Active Record connects business objects and database tables to create a persistable +domain model where logic and data are presented in one wrapping. It's an implementation +of the object-relational mapping (ORM) pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] +by the same name as described by Martin Fowler: + + "An object that wraps a row in a database table or view, encapsulates + the database access, and adds domain logic on that data." + +Active Record's main contribution to the pattern is to relieve the original of two stunting problems: +lack of associations and inheritance. By adding a simple domain language-like set of macros to describe +the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the +gap of functionality between the data mapper and active record approach. + +A short rundown of the major features: + +* Automated mapping between classes and tables, attributes and columns. + + class Product < ActiveRecord::Base; end + + ...is automatically mapped to the table named "products", such as: + + CREATE TABLE products ( + id int(11) NOT NULL auto_increment, + name varchar(255), + PRIMARY KEY (id) + ); + + ...which again gives Product#name and Product#name=(new_name) + + {Learn more}[link:classes/ActiveRecord/Base.html] + + +* Associations between objects controlled by simple meta-programming macros. + + class Firm < ActiveRecord::Base + has_many :clients + has_one :account + belongs_to :conglomorate + end + + {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html] + + +* Aggregations of value objects controlled by simple meta-programming macros. + + class Account < ActiveRecord::Base + composed_of :balance, :class_name => "Money", + :mapping => %w(balance amount) + composed_of :address, + :mapping => [%w(address_street street), %w(address_city city)] + end + + {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html] + + +* Validation rules that can differ for new or existing objects. + + class Account < ActiveRecord::Base + validates_presence_of :subdomain, :name, :email_address, :password + validates_uniqueness_of :subdomain + validates_acceptance_of :terms_of_service, :on => :create + validates_confirmation_of :password, :email_address, :on => :create + end + + {Learn more}[link:classes/ActiveRecord/Validations.html] + +* Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc). + + class Person < ActiveRecord::Base + def before_destroy # is called just before Person#destroy + CreditCard.find(credit_card_id).destroy + end + end + + class Account < ActiveRecord::Base + after_find :eager_load, 'self.class.announce(#{id})' + end + + {Learn more}[link:classes/ActiveRecord/Callbacks.html] + + +* Observers for the entire lifecycle + + class CommentObserver < ActiveRecord::Observer + def after_create(comment) # is called just after Comment#save + Notifications.deliver_new_comment("david@loudthinking.com", comment) + end + end + + {Learn more}[link:classes/ActiveRecord/Observer.html] + + +* Inheritance hierarchies + + class Company < ActiveRecord::Base; end + class Firm < Company; end + class Client < Company; end + class PriorityClient < Client; end + + {Learn more}[link:classes/ActiveRecord/Base.html] + + +* Transactions + + # Database transaction + Account.transaction do + david.withdrawal(100) + mary.deposit(100) + end + + {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html] + + +* Reflections on columns, associations, and aggregations + + reflection = Firm.reflect_on_association(:clients) + reflection.klass # => Client (class) + Firm.columns # Returns an array of column descriptors for the firms table + + {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html] + + +* Direct manipulation (instead of service invocation) + + So instead of (Hibernate[http://www.hibernate.org/] example): + + long pkId = 1234; + DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) ); + // something interesting involving a cat... + sess.save(cat); + sess.flush(); // force the SQL INSERT + + Active Record lets you: + + pkId = 1234 + cat = Cat.find(pkId) + # something even more interesting involving the same cat... + cat.save + + {Learn more}[link:classes/ActiveRecord/Base.html] + + +* Database abstraction through simple adapters (~100 lines) with a shared connector + + ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile") + + ActiveRecord::Base.establish_connection( + :adapter => "mysql", + :host => "localhost", + :username => "me", + :password => "secret", + :database => "activerecord" + ) + + {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for + MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OracleAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html]. + + +* Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc] + + ActiveRecord::Base.logger = Logger.new(STDOUT) + ActiveRecord::Base.logger = Log4r::Logger.new("Application Log") + + +* Database agnostic schema management with Migrations + + class AddSystemSettings < ActiveRecord::Migration + def self.up + create_table :system_settings do |t| + t.string :name + t.string :label + t.text :value + t.string :type + t.integer :position + end + + SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1 + end + + def self.down + drop_table :system_settings + end + end + + {Learn more}[link:classes/ActiveRecord/Migration.html] + +== Simple example (1/2): Defining tables and classes (using MySQL) + +Data definitions are specified only in the database. Active Record queries the database for +the column names (that then serves to determine which attributes are valid) on regular +object instantiation through the new constructor and relies on the column names in the rows +with the finders. + + # CREATE TABLE companies ( + # id int(11) unsigned NOT NULL auto_increment, + # client_of int(11), + # name varchar(255), + # type varchar(100), + # PRIMARY KEY (id) + # ) + +Active Record automatically links the "Company" object to the "companies" table + + class Company < ActiveRecord::Base + has_many :people, :class_name => "Person" + end + + class Firm < Company + has_many :clients + + def people_with_all_clients + clients.inject([]) { |people, client| people + client.people } + end + end + +The foreign_key is only necessary because we didn't use "firm_id" in the data definition + + class Client < Company + belongs_to :firm, :foreign_key => "client_of" + end + + # CREATE TABLE people ( + # id int(11) unsigned NOT NULL auto_increment, + # name text, + # company_id text, + # PRIMARY KEY (id) + # ) + +Active Record will also automatically link the "Person" object to the "people" table + + class Person < ActiveRecord::Base + belongs_to :company + end + +== Simple example (2/2): Using the domain + +Picking a database connection for all the Active Records + + ActiveRecord::Base.establish_connection( + :adapter => "mysql", + :host => "localhost", + :username => "me", + :password => "secret", + :database => "activerecord" + ) + +Create some fixtures + + firm = Firm.new("name" => "Next Angle") + # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") + firm.save + + client = Client.new("name" => "37signals", "client_of" => firm.id) + # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm") + client.save + +Lots of different finders + + # SQL: SELECT * FROM companies WHERE id = 1 + next_angle = Company.find(1) + + # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm' + next_angle = Firm.find(1) + + # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle' + next_angle = Company.find(:first, :conditions => "name = 'Next Angle'") + + next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first + +The supertype, Company, will return subtype instances + + Firm === next_angle + +All the dynamic methods added by the has_many macro + + next_angle.clients.empty? # true + next_angle.clients.size # total number of clients + all_clients = next_angle.clients + +Constrained finds makes access security easier when ID comes from a web-app + + # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2 + thirty_seven_signals = next_angle.clients.find(2) + +Bi-directional associations thanks to the "belongs_to" macro + + thirty_seven_signals.firm.nil? # true + + +== Philosophy + +Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is +object-relational mapping. The prime directive for this mapping has been to minimize +the amount of code needed to build a real-world domain model. This is made possible +by relying on a number of conventions that make it easy for Active Record to infer +complex relations and structures from a minimal amount of explicit direction. + +Convention over Configuration: +* No XML-files! +* Lots of reflection and run-time extension +* Magic is not inherently a bad word + +Admit the Database: +* Lets you drop down to SQL for odd cases and performance +* Doesn't attempt to duplicate or replace data definitions + + +== Download + +The latest version of Active Record can be installed with Rubygems: + +* gem install activerecord + +Documentation can be found at + +* http://api.rubyonrails.org + + +== License + +Active Record is released under the MIT license. + + +== Support + +The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record +RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says: + + Feel free to submit commits or feature requests. If you send a patch, + remember to update the corresponding unit tests. If fact, I prefer + new feature to be submitted in the form of new unit tests. + +For other information, feel free to ask on the rubyonrails-talk +(http://groups.google.com/group/rubyonrails-talk) mailing list. diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 392b717e0a..d9124c9776 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -172,7 +172,7 @@ Rake::RDocTask.new { |rdoc| rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' - rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG') + rdoc.rdoc_files.include('README.rdoc', 'RUNNING_UNIT_TESTS', 'CHANGELOG') rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.exclude('lib/active_record/vendor/*') rdoc.rdoc_files.include('dev-utils/*.rb') diff --git a/activeresource/README b/activeresource/README deleted file mode 100644 index 127ac5b4a9..0000000000 --- a/activeresource/README +++ /dev/null @@ -1,165 +0,0 @@ -= Active Resource - -Active Resource (ARes) connects business objects and Representational State Transfer (REST) -web services. It implements object-relational mapping for REST web services to provide transparent -proxying capabilities between a client (ActiveResource) and a RESTful service (which is provided by Simply RESTful routing -in ActionController::Resources). - -== Philosophy - -Active Resource attempts to provide a coherent wrapper object-relational mapping for REST -web services. It follows the same philosophy as Active Record, in that one of its prime aims -is to reduce the amount of code needed to map to these resources. This is made possible -by relying on a number of code- and protocol-based conventions that make it easy for Active Resource -to infer complex relations and structures. These conventions are outlined in detail in the documentation -for ActiveResource::Base. - -== Overview - -Model classes are mapped to remote REST resources by Active Resource much the same way Active Record maps model classes to database -tables. When a request is made to a remote resource, a REST XML request is generated, transmitted, and the result -received and serialized into a usable Ruby object. - -=== Configuration and Usage - -Putting Active Resource to use is very similar to Active Record. It's as simple as creating a model class -that inherits from ActiveResource::Base and providing a site class variable to it: - - class Person < ActiveResource::Base - self.site = "http://api.people.com:3000/" - end - -Now the Person class is REST enabled and can invoke REST services very similarly to how Active Record invokes -lifecycle methods that operate against a persistent store. - - # Find a person with id = 1 - ryan = Person.find(1) - Person.exists?(1) #=> true - -As you can see, the methods are quite similar to Active Record's methods for dealing with database -records. But rather than dealing directly with a database record, you're dealing with HTTP resources (which may or may not be database records). - -==== Protocol - -Active Resource is built on a standard XML format for requesting and submitting resources over HTTP. It mirrors the RESTful routing -built into Action Controller but will also work with any other REST service that properly implements the protocol. -REST uses HTTP, but unlike "typical" web applications, it makes use of all the verbs available in the HTTP specification: - -* GET requests are used for finding and retrieving resources. -* POST requests are used to create new resources. -* PUT requests are used to update existing resources. -* DELETE requests are used to delete resources. - -For more information on how this protocol works with Active Resource, see the ActiveResource::Base documentation; -for more general information on REST web services, see the article here[http://en.wikipedia.org/wiki/Representational_State_Transfer]. - -==== Find - -Find requests use the GET method and expect the XML form of whatever resource/resources is/are being requested. So, -for a request for a single element, the XML of that item is expected in response: - - # Expects a response of - # - # 1value1.. - # - # for GET http://api.people.com:3000/people/1.xml - # - ryan = Person.find(1) - -The XML document that is received is used to build a new object of type Person, with each -XML element becoming an attribute on the object. - - ryan.is_a? Person #=> true - ryan.attribute1 #=> 'value1' - -Any complex element (one that contains other elements) becomes its own object: - - # With this response: - # - # 1value1value2 - # - # for GET http://api.people.com:3000/people/1.xml - # - ryan = Person.find(1) - ryan.complex #=> - ryan.complex.attribute2 #=> 'value2' - -Collections can also be requested in a similar fashion - - # Expects a response of - # - # - # 1Ryan - # 2Jim - # - # - # for GET http://api.people.com:3000/people.xml - # - people = Person.find(:all) - people.first #=> 'Ryan' ...> - people.last #=> 'Jim' ...> - -==== Create - -Creating a new resource submits the XML form of the resource as the body of the request and expects -a 'Location' header in the response with the RESTful URL location of the newly created resource. The -id of the newly created resource is parsed out of the Location response header and automatically set -as the id of the ARes object. - - # Ryan - # - # is submitted as the body on - # - # POST http://api.people.com:3000/people.xml - # - # when save is called on a new Person object. An empty response is - # is expected with a 'Location' header value: - # - # Response (201): Location: http://api.people.com:3000/people/2 - # - ryan = Person.new(:first => 'Ryan') - ryan.new? #=> true - ryan.save #=> true - ryan.new? #=> false - ryan.id #=> 2 - -==== Update - -'save' is also used to update an existing resource - and follows the same protocol as creating a resource -with the exception that no response headers are needed - just an empty response when the update on the -server side was successful. - - # Ryan - # - # is submitted as the body on - # - # PUT http://api.people.com:3000/people/1.xml - # - # when save is called on an existing Person object. An empty response is - # is expected with code (204) - # - ryan = Person.find(1) - ryan.first #=> 'Ryan' - ryan.first = 'Rizzle' - ryan.save #=> true - -==== Delete - -Destruction of a resource can be invoked as a class and instance method of the resource. - - # A request is made to - # - # DELETE http://api.people.com:3000/people/1.xml - # - # for both of these forms. An empty response with - # is expected with response code (200) - # - ryan = Person.find(1) - ryan.destroy #=> true - ryan.exists? #=> false - Person.delete(2) #=> true - Person.exists?(2) #=> false - - -You can find more usage information in the ActiveResource::Base documentation. - diff --git a/activeresource/README.rdoc b/activeresource/README.rdoc new file mode 100644 index 0000000000..127ac5b4a9 --- /dev/null +++ b/activeresource/README.rdoc @@ -0,0 +1,165 @@ += Active Resource + +Active Resource (ARes) connects business objects and Representational State Transfer (REST) +web services. It implements object-relational mapping for REST web services to provide transparent +proxying capabilities between a client (ActiveResource) and a RESTful service (which is provided by Simply RESTful routing +in ActionController::Resources). + +== Philosophy + +Active Resource attempts to provide a coherent wrapper object-relational mapping for REST +web services. It follows the same philosophy as Active Record, in that one of its prime aims +is to reduce the amount of code needed to map to these resources. This is made possible +by relying on a number of code- and protocol-based conventions that make it easy for Active Resource +to infer complex relations and structures. These conventions are outlined in detail in the documentation +for ActiveResource::Base. + +== Overview + +Model classes are mapped to remote REST resources by Active Resource much the same way Active Record maps model classes to database +tables. When a request is made to a remote resource, a REST XML request is generated, transmitted, and the result +received and serialized into a usable Ruby object. + +=== Configuration and Usage + +Putting Active Resource to use is very similar to Active Record. It's as simple as creating a model class +that inherits from ActiveResource::Base and providing a site class variable to it: + + class Person < ActiveResource::Base + self.site = "http://api.people.com:3000/" + end + +Now the Person class is REST enabled and can invoke REST services very similarly to how Active Record invokes +lifecycle methods that operate against a persistent store. + + # Find a person with id = 1 + ryan = Person.find(1) + Person.exists?(1) #=> true + +As you can see, the methods are quite similar to Active Record's methods for dealing with database +records. But rather than dealing directly with a database record, you're dealing with HTTP resources (which may or may not be database records). + +==== Protocol + +Active Resource is built on a standard XML format for requesting and submitting resources over HTTP. It mirrors the RESTful routing +built into Action Controller but will also work with any other REST service that properly implements the protocol. +REST uses HTTP, but unlike "typical" web applications, it makes use of all the verbs available in the HTTP specification: + +* GET requests are used for finding and retrieving resources. +* POST requests are used to create new resources. +* PUT requests are used to update existing resources. +* DELETE requests are used to delete resources. + +For more information on how this protocol works with Active Resource, see the ActiveResource::Base documentation; +for more general information on REST web services, see the article here[http://en.wikipedia.org/wiki/Representational_State_Transfer]. + +==== Find + +Find requests use the GET method and expect the XML form of whatever resource/resources is/are being requested. So, +for a request for a single element, the XML of that item is expected in response: + + # Expects a response of + # + # 1value1.. + # + # for GET http://api.people.com:3000/people/1.xml + # + ryan = Person.find(1) + +The XML document that is received is used to build a new object of type Person, with each +XML element becoming an attribute on the object. + + ryan.is_a? Person #=> true + ryan.attribute1 #=> 'value1' + +Any complex element (one that contains other elements) becomes its own object: + + # With this response: + # + # 1value1value2 + # + # for GET http://api.people.com:3000/people/1.xml + # + ryan = Person.find(1) + ryan.complex #=> + ryan.complex.attribute2 #=> 'value2' + +Collections can also be requested in a similar fashion + + # Expects a response of + # + # + # 1Ryan + # 2Jim + # + # + # for GET http://api.people.com:3000/people.xml + # + people = Person.find(:all) + people.first #=> 'Ryan' ...> + people.last #=> 'Jim' ...> + +==== Create + +Creating a new resource submits the XML form of the resource as the body of the request and expects +a 'Location' header in the response with the RESTful URL location of the newly created resource. The +id of the newly created resource is parsed out of the Location response header and automatically set +as the id of the ARes object. + + # Ryan + # + # is submitted as the body on + # + # POST http://api.people.com:3000/people.xml + # + # when save is called on a new Person object. An empty response is + # is expected with a 'Location' header value: + # + # Response (201): Location: http://api.people.com:3000/people/2 + # + ryan = Person.new(:first => 'Ryan') + ryan.new? #=> true + ryan.save #=> true + ryan.new? #=> false + ryan.id #=> 2 + +==== Update + +'save' is also used to update an existing resource - and follows the same protocol as creating a resource +with the exception that no response headers are needed - just an empty response when the update on the +server side was successful. + + # Ryan + # + # is submitted as the body on + # + # PUT http://api.people.com:3000/people/1.xml + # + # when save is called on an existing Person object. An empty response is + # is expected with code (204) + # + ryan = Person.find(1) + ryan.first #=> 'Ryan' + ryan.first = 'Rizzle' + ryan.save #=> true + +==== Delete + +Destruction of a resource can be invoked as a class and instance method of the resource. + + # A request is made to + # + # DELETE http://api.people.com:3000/people/1.xml + # + # for both of these forms. An empty response with + # is expected with response code (200) + # + ryan = Person.find(1) + ryan.destroy #=> true + ryan.exists? #=> false + Person.delete(2) #=> true + Person.exists?(2) #=> false + + +You can find more usage information in the ActiveResource::Base documentation. + diff --git a/activeresource/Rakefile b/activeresource/Rakefile index 04b08ed8cb..b1e5ca91d3 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -35,7 +35,7 @@ Rake::RDocTask.new { |rdoc| rdoc.options << '--line-numbers' << '--inline-source' << '-A 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/activeresource.rb') } diff --git a/activesupport/README b/activesupport/README deleted file mode 100644 index aa86f1fd65..0000000000 --- a/activesupport/README +++ /dev/null @@ -1,34 +0,0 @@ -= Active Support -- Utility classes and standard library extensions from Rails - -Active Support is a collection of various utility classes and standard library extensions that were found useful -for Rails. All these additions have hence been collected in this bundle as way to gather all that sugar that makes -Ruby sweeter. - - -== Download - -The latest version of Active Support can be installed with Rubygems: - -* gem install activesupport - -Documentation can be found at - -* http://api.rubyonrails.org - - -== License - -Active Support is released under the MIT license. - - -== Support - -The Active Support homepage is http://www.rubyonrails.com. You can find the Active Support -RubyForge page at http://rubyforge.org/projects/activesupport. And as Jim from Rake says: - - Feel free to submit commits or feature requests. If you send a patch, - remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests. - -For other information, feel free to ask on the ruby-talk mailing list -(which is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com. diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc new file mode 100644 index 0000000000..aa86f1fd65 --- /dev/null +++ b/activesupport/README.rdoc @@ -0,0 +1,34 @@ += Active Support -- Utility classes and standard library extensions from Rails + +Active Support is a collection of various utility classes and standard library extensions that were found useful +for Rails. All these additions have hence been collected in this bundle as way to gather all that sugar that makes +Ruby sweeter. + + +== Download + +The latest version of Active Support can be installed with Rubygems: + +* gem install activesupport + +Documentation can be found at + +* http://api.rubyonrails.org + + +== License + +Active Support is released under the MIT license. + + +== Support + +The Active Support homepage is http://www.rubyonrails.com. You can find the Active Support +RubyForge page at http://rubyforge.org/projects/activesupport. And as Jim from Rake says: + + Feel free to submit commits or feature requests. If you send a patch, + remember to update the corresponding unit tests. If fact, I prefer + new feature to be submitted in the form of new unit tests. + +For other information, feel free to ask on the ruby-talk mailing list +(which is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com. diff --git a/activesupport/Rakefile b/activesupport/Rakefile index 2aebe05de2..77b1a8431d 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -28,7 +28,7 @@ Rake::RDocTask.new { |rdoc| rdoc.options << '--line-numbers' << '--inline-source' 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/active_support.rb') rdoc.rdoc_files.include('lib/active_support/**/*.rb') } 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 53310614d7ef279e3b2d5b02e4468e3e3f25e0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 13:00:56 +0200 Subject: Reuse already existing classes for ActiveModel tests. --- activemodel/test/cases/naming_helpers_test.rb | 63 --------------------------- activemodel/test/cases/naming_test.rb | 39 +++++++++++++++++ activemodel/test/models/contact.rb | 1 + activemodel/test/models/sheep.rb | 4 ++ 4 files changed, 44 insertions(+), 63 deletions(-) delete mode 100644 activemodel/test/cases/naming_helpers_test.rb create mode 100644 activemodel/test/models/sheep.rb diff --git a/activemodel/test/cases/naming_helpers_test.rb b/activemodel/test/cases/naming_helpers_test.rb deleted file mode 100644 index e7234e009e..0000000000 --- a/activemodel/test/cases/naming_helpers_test.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'cases/helper' - -class Comment - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - def to_key; id ? [id] : nil end - def save; @id = 1 end - def new_record?; @id.nil? end - def name - @id.nil? ? 'new comment' : "comment ##{@id}" - end -end - -class Sheep - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - def to_key; id ? [id] : nil end - def save; @id = 1 end - def new_record?; @id.nil? end - def name - @id.nil? ? 'new sheep' : "sheep ##{@id}" - end -end - -class NamingHelpersTest < Test::Unit::TestCase - def setup - @klass = Comment - @record = @klass.new - @singular = 'comment' - @plural = 'comments' - @uncountable = Sheep - end - - def test_singular - assert_equal @singular, singular(@record) - end - - def test_singular_for_class - assert_equal @singular, singular(@klass) - end - - def test_plural - assert_equal @plural, plural(@record) - end - - def test_plural_for_class - assert_equal @plural, plural(@klass) - end - - def test_uncountable - assert_equal true, uncountable?(@uncountable) - assert_equal false, uncountable?(@klass) - end - - private - def method_missing(method, *args) - ActiveModel::Naming.send(method, *args) - end -end diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb index dc39b84ed8..5a8bff378a 100644 --- a/activemodel/test/cases/naming_test.rb +++ b/activemodel/test/cases/naming_test.rb @@ -1,4 +1,6 @@ require 'cases/helper' +require 'models/contact' +require 'models/sheep' require 'models/track_back' class NamingTest < ActiveModel::TestCase @@ -26,3 +28,40 @@ class NamingTest < ActiveModel::TestCase assert_equal 'post/track_backs/track_back', @model_name.partial_path end end + +class NamingHelpersTest < Test::Unit::TestCase + def setup + @klass = Contact + @record = @klass.new + @singular = 'contact' + @plural = 'contacts' + @uncountable = Sheep + end + + def test_singular + assert_equal @singular, singular(@record) + end + + def test_singular_for_class + assert_equal @singular, singular(@klass) + end + + def test_plural + assert_equal @plural, plural(@record) + end + + def test_plural_for_class + assert_equal @plural, plural(@klass) + end + + def test_uncountable + assert uncountable?(@uncountable), "Expected 'sheep' to be uncoutable" + assert !uncountable?(@klass), "Expected 'contact' to be countable" + end + + private + def method_missing(method, *args) + ActiveModel::Naming.send(method, *args) + end +end + diff --git a/activemodel/test/models/contact.rb b/activemodel/test/models/contact.rb index 605e435f39..f4f3078473 100644 --- a/activemodel/test/models/contact.rb +++ b/activemodel/test/models/contact.rb @@ -1,4 +1,5 @@ class Contact + extend ActiveModel::Naming include ActiveModel::Conversion attr_accessor :id, :name, :age, :created_at, :awesome, :preferences diff --git a/activemodel/test/models/sheep.rb b/activemodel/test/models/sheep.rb new file mode 100644 index 0000000000..175dbe6477 --- /dev/null +++ b/activemodel/test/models/sheep.rb @@ -0,0 +1,4 @@ +class Sheep + extend ActiveModel::Naming +end + \ No newline at end of file -- cgit v1.2.3 From 0a729b0a9138841676e86e1a33337c573028e9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 14:16:37 +0200 Subject: Fix failing railties tests. --- actionpack/lib/action_dispatch.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 9bb0471ffc..50d8e91c47 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -24,6 +24,9 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) +$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) + require 'active_support' require 'active_support/dependencies/autoload' -- cgit v1.2.3 From 6ba7d5e6544d636a763a40d1543f96d0e0bd77d5 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 9 Jul 2010 06:07:00 -0400 Subject: - without the id test is passing even if I change :allow_destroy from 'false' - adding more tests to strengthen the test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/nested_attributes_test.rb | 49 ++++++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index c9ea0d8c40..84ab61f591 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -59,6 +59,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase pirate.save! assert_equal 1, pirate.birds_with_reject_all_blank.count + assert_equal 'Tweetie', pirate.birds_with_reject_all_blank.first.name end def test_should_raise_an_ArgumentError_for_non_existing_associations @@ -74,7 +75,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase ship = pirate.create_ship(:name => 'Nights Dirty Lightning') assert_no_difference('Ship.count') do - pirate.update_attributes(:ship_attributes => { '_destroy' => true }) + pirate.update_attributes(:ship_attributes => { '_destroy' => true, :id => ship.id }) end end @@ -100,7 +101,8 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true } assert_no_difference('Ship.count') { pirate.save! } - # pirate.reject_empty_ships_on_create returns false for saved records + # pirate.reject_empty_ships_on_create returns false for saved pirate records + # in the previous step note that pirate gets saved but ship fails pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true } assert_difference('Ship.count') { pirate.save! } end @@ -266,6 +268,28 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase end assert_equal 'Mayflower', @ship.reload.name end + + def test_should_update_existing_when_update_only_is_true_and_id_is_given + @ship.delete + @ship = @pirate.create_update_only_ship(:name => 'Nights Dirty Lightning') + + assert_no_difference('Ship.count') do + @pirate.update_attributes(:update_only_ship_attributes => { :name => 'Mayflower', :id => @ship.id }) + end + assert_equal 'Mayflower', @ship.reload.name + end + + def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction + Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => true + @ship.delete + @ship = @pirate.create_update_only_ship(:name => 'Nights Dirty Lightning') + + assert_difference('Ship.count', -1) do + @pirate.update_attributes(:update_only_ship_attributes => { :name => 'Mayflower', :id => @ship.id, :_destroy => true }) + end + Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => false + end + end class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase @@ -411,6 +435,27 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase end assert_equal 'Arr', @pirate.reload.catchphrase end + + def test_should_update_existing_when_update_only_is_true_and_id_is_given + @pirate.delete + @pirate = @ship.create_update_only_pirate(:catchphrase => 'Aye') + + assert_no_difference('Pirate.count') do + @ship.update_attributes(:update_only_pirate_attributes => { :catchphrase => 'Arr', :id => @pirate.id }) + end + assert_equal 'Arr', @pirate.reload.catchphrase + end + + def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction + Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => true + @pirate.delete + @pirate = @ship.create_update_only_pirate(:catchphrase => 'Aye') + + assert_difference('Pirate.count', -1) do + @ship.update_attributes(:update_only_pirate_attributes => { :catchphrase => 'Arr', :id => @pirate.id, :_destroy => true }) + end + Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => false + end end module NestedAttributesOnACollectionAssociationTests -- cgit v1.2.3 From c0bfa0bfc17f4aa615cd9d1006509e0d84b5692d Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Sat, 10 Jul 2010 12:29:09 -0400 Subject: In nested_attributes when association is not loaded and association record is saved and then in memory record attributes should be saved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5053 state:resolved] Signed-off-by: José Valim --- .../lib/active_record/associations/association_collection.rb | 9 ++++++++- activerecord/test/cases/nested_attributes_test.rb | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 7abb738a74..7100be0245 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -477,7 +477,14 @@ module ActiveRecord callback(:before_add, record) yield(record) if block_given? @target ||= [] unless loaded? - @target << record unless @reflection.options[:uniq] && @target.include?(record) + index = @target.index(record) + unless @reflection.options[:uniq] && index + if index + @target[index] = record + else + @target << record + end + end callback(:after_add, record) set_inverse_instance(record, @owner) record diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 84ab61f591..20bd4f6a76 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -856,6 +856,12 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR @part = @ship.parts.create!(:name => "Mast") @trinket = @part.trinkets.create!(:name => "Necklace") end + + test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do + @ship.parts_attributes=[{:id => @part.id,:name =>'Deck'}] + assert_equal 1, @ship.parts.proxy_target.size + assert_equal 'Deck', @ship.parts[0].name + end test "when grandchild changed in memory, saving parent should save grandchild" do @trinket.name = "changed" -- cgit v1.2.3 From 0057d2df71ec7c2a788acd3b4bade263fc0fe361 Mon Sep 17 00:00:00 2001 From: Szetobo Date: Mon, 12 Jul 2010 09:47:47 +0800 Subject: association load target shouldn't replace records from db if it is already loaded by nested attributes assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5053 state:resolved] Signed-off-by: José Valim --- .../active_record/associations/association_collection.rb | 7 ++++--- activerecord/test/cases/nested_attributes_test.rb | 14 +++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 7100be0245..692badcc5c 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -396,11 +396,12 @@ module ActiveRecord if @target.is_a?(Array) && @target.any? @target = find_target.map do |f| i = @target.index(f) - t = @target.delete_at(i) if i - if t && t.changed? + if i + t = @target.delete_at(i) + keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names) + t.attributes = f.attributes.except(*keys) t else - f.mark_for_destruction if t && t.marked_for_destruction? f end end + @target diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 20bd4f6a76..df09bbd46a 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -862,7 +862,19 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR assert_equal 1, @ship.parts.proxy_target.size assert_equal 'Deck', @ship.parts[0].name end - + + test "if association is not loaded and child doesn't change and I am saving a grandchild then in memory record should be used" do + @ship.parts_attributes=[{:id => @part.id,:trinkets_attributes =>[{:id => @trinket.id, :name => 'Ruby'}]}] + assert_equal 1, @ship.parts.proxy_target.size + assert_equal 'Mast', @ship.parts[0].name + assert_no_difference("@ship.parts[0].trinkets.proxy_target.size") do + @ship.parts[0].trinkets.proxy_target.size + end + assert_equal 'Ruby', @ship.parts[0].trinkets[0].name + @ship.save + assert_equal 'Ruby', @ship.parts[0].trinkets[0].name + end + test "when grandchild changed in memory, saving parent should save grandchild" do @trinket.name = "changed" @ship.save -- cgit v1.2.3 From 96b2516c3cbcf900f2e84163baba3db7cb0e37d9 Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Wed, 14 Jul 2010 23:13:56 -0400 Subject: Strengthening the test for nested_attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loading the associate target in nested_attributes should load most recent attributes for child records marked for destruction Signed-off-by: José Valim --- activerecord/test/cases/associations_test.rb | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 4ae776c35a..ff9e9a472a 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -17,7 +17,8 @@ require 'models/tagging' require 'models/person' require 'models/reader' require 'models/parrot' -require 'models/pirate' +require 'models/ship_part' +require 'models/ship' require 'models/treasure' require 'models/price_estimate' require 'models/club' @@ -29,6 +30,24 @@ class AssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :developers_projects, :computers, :people, :readers + def test_loading_the_association_target_should_keep_child_records_marked_for_destruction + ship = Ship.create!(:name => "The good ship Dollypop") + part = ship.parts.create!(:name => "Mast") + part.mark_for_destruction + ship.parts.send(:load_target) + assert ship.parts[0].marked_for_destruction? + end + + def test_loading_the_association_target_should_load_most_recent_attributes_for_child_records_marked_for_destruction + ship = Ship.create!(:name => "The good ship Dollypop") + part = ship.parts.create!(:name => "Mast") + part.mark_for_destruction + ShipPart.find(part.id).update_attribute(:name, 'Deck') + ship.parts.send(:load_target) + assert_equal 'Deck', ship.parts[0].name + end + + def test_include_with_order_works assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)} assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)} -- cgit v1.2.3 From 01add55d6a6a67fb487afa040998575111511b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 14:44:46 +0200 Subject: Revert "save on parent should not cascade to child unless child changed [#3353 state:open]" Please check Lighthouse for more information. This reverts commit 4a0d7c1a439c6ad8d35bf514761824e51fa07df2. --- activerecord/lib/active_record/autosave_association.rb | 4 +--- activerecord/test/cases/autosave_association_test.rb | 18 ------------------ 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 6af384367f..7517896235 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -372,9 +372,7 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy elsif autosave != false - if association.new_record? || ( autosave && association.changed? ) - saved = association.save(:validate => !autosave) - end + saved = association.save(:validate => !autosave) if association.new_record? || autosave if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 48479bb429..3b89c12a3f 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -632,8 +632,6 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent - #association save method only trigged when association is changed - @ship.pirate.catchphrase = "new catch phrase" # Stub the save method of the @ship.pirate instance to destroy and then raise an exception class << @ship.pirate def save(*args) @@ -882,22 +880,6 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?") end - def test_should_not_call_belongs_to_after_save_callbacks_if_no_changes - @ship.attributes = { :name => "Titanic", :pirate_attributes => {:id => @pirate.id} } - #here there are no changes to pirate so if save on ship causes save on pirate - #this callback will fail pirate save.(pirate save shouldn't happen) - @ship.pirate.cancel_save_from_callback = true - @ship.save - assert_equal 'Titanic', @ship.reload.name - end - - def test_should_call_belongs_to_save_if_belongs_to_has_changes - @ship.attributes = { :name => "Titanic", :pirate_attributes => { :catchphrase => 'Jack', :id => @pirate.id} } - @ship.save - assert_equal 'Titanic', @ship.reload.name - assert_equal 'Jack', @pirate.reload.catchphrase - end - def test_should_still_work_without_an_associated_model @pirate.destroy @ship.reload.name = "The Vile Insanity" -- cgit v1.2.3 From 8bb3b634c07a327b9f1c7e21b4d1fcc407737a9f Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 20 Jul 2010 23:52:24 -0400 Subject: Timestamp columns of HABTM join table should record timestamps [#5161 state:resolved] --- .../has_and_belongs_to_many_association.rb | 6 +++- activerecord/lib/active_record/timestamp.rb | 19 ++++++++++-- .../has_and_belongs_to_many_associations_test.rb | 35 +++++++++++++++++++++- activerecord/test/schema/schema.rb | 2 ++ 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 9ec63e3fca..177d7905c7 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -53,7 +53,11 @@ module ActiveRecord when @reflection.association_foreign_key.to_s attrs[relation[column.name]] = record.id else - if record.has_attribute?(column.name) + if record.send(:all_timestamp_attributes).include?(column.name.to_sym) + if record.record_timestamps + attrs[relation[column.name]] = record.send(:current_time_from_proper_timezone) + end + elsif record.has_attribute?(column.name) value = @owner.send(:quote_value, record[column.name], column) attrs[relation[column.name]] = value unless value.nil? end diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 341cc87be5..6c1e376745 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -39,8 +39,9 @@ module ActiveRecord if record_timestamps current_time = current_time_from_proper_timezone - write_attribute('created_at', current_time) if respond_to?(:created_at) && created_at.nil? - write_attribute('created_on', current_time) if respond_to?(:created_on) && created_on.nil? + timestamp_attributes_for_create.each do |column| + write_attribute(column.to_s, current_time) if respond_to?(column) && self.send(column).nil? + end timestamp_attributes_for_update_in_model.each do |column| write_attribute(column.to_s, current_time) if self.send(column).nil? @@ -65,7 +66,19 @@ module ActiveRecord end def timestamp_attributes_for_update_in_model #:nodoc: - [:updated_at, :updated_on].select { |elem| respond_to?(elem) } + timestamp_attributes_for_update.select { |elem| respond_to?(elem) } + end + + def timestamp_attributes_for_update #:nodoc: + [:updated_at, :updated_on] + end + + def timestamp_attributes_for_create #:nodoc: + [:created_at, :created_on] + end + + def all_timestamp_attributes #:nodoc: + timestamp_attributes_for_update + timestamp_attributes_for_create end def current_time_from_proper_timezone #:nodoc: diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index d4d3d8e43e..d7e9ca0bcb 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -85,7 +85,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings - def test_should_property_quote_string_primary_keys + def setup_data_for_habtm_case + ActiveRecord::Base.connection.execute('delete from countries_treaties') + country = Country.new(:name => 'India') country.country_id = 'c1' country.save! @@ -93,6 +95,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase treaty = Treaty.new(:name => 'peace') treaty.treaty_id = 't1' country.treaties << treaty + end + + def test_should_property_quote_string_primary_keys + setup_data_for_habtm_case con = ActiveRecord::Base.connection sql = 'select * from countries_treaties' @@ -101,6 +107,33 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 't1', record[1] end + def test_should_record_timestamp_for_join_table + setup_data_for_habtm_case + + con = ActiveRecord::Base.connection + sql = 'select * from countries_treaties' + record = con.select_rows(sql).last + assert_not_nil record[2] + assert_not_nil record[3] + assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2] + assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3] + end + + def test_should_record_timestamp_for_join_table_only_if_timestamp_should_be_recorded + begin + Treaty.record_timestamps = false + setup_data_for_habtm_case + + con = ActiveRecord::Base.connection + sql = 'select * from countries_treaties' + record = con.select_rows(sql).last + assert_nil record[2] + assert_nil record[3] + ensure + Treaty.record_timestamps = true + end + end + def test_has_and_belongs_to_many david = Developer.find(1) diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index c4eed256bf..a69e38f414 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -611,6 +611,8 @@ ActiveRecord::Schema.define do create_table :countries_treaties, :force => true, :id => false do |t| t.string :country_id, :null => false t.string :treaty_id, :null => false + t.datetime :created_at + t.datetime :updated_at end except 'SQLite' do -- cgit v1.2.3 From f3e42292a5792f77681a3fbf03bc8cfc75aeb887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 14:50:04 +0200 Subject: Ensure all join table attributes will be in the same timestamp. --- .../has_and_belongs_to_many_association.rb | 32 ++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 177d7905c7..e61af93d1e 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -45,21 +45,23 @@ module ActiveRecord if @reflection.options[:insert_sql] @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record)) else - relation = Arel::Table.new(@reflection.options[:join_table]) + relation = Arel::Table.new(@reflection.options[:join_table]) + timestamps = record_timestamp_columns(record) + timezone = record.send(:current_time_from_proper_timezone) if timestamps.any? + attributes = columns.inject({}) do |attrs, column| - case column.name.to_s + name = column.name + case name.to_s when @reflection.primary_key_name.to_s - attrs[relation[column.name]] = @owner.id + attrs[relation[name]] = @owner.id when @reflection.association_foreign_key.to_s - attrs[relation[column.name]] = record.id + attrs[relation[name]] = record.id + when *timestamps + attrs[relation[name]] = timezone else - if record.send(:all_timestamp_attributes).include?(column.name.to_sym) - if record.record_timestamps - attrs[relation[column.name]] = record.send(:current_time_from_proper_timezone) - end - elsif record.has_attribute?(column.name) - value = @owner.send(:quote_value, record[column.name], column) - attrs[relation[column.name]] = value unless value.nil? + if record.has_attribute?(name) + value = @owner.send(:quote_value, record[name], column) + attrs[relation[name]] = value unless value.nil? end end attrs @@ -121,6 +123,14 @@ module ActiveRecord build_record(attributes, &block) end end + + def record_timestamp_columns(record) + if record.record_timestamps + record.send(:all_timestamp_attributes).map(&:to_s) + else + [] + end + end end end end -- cgit v1.2.3 From c96a50539121c4a722f354220c7f0e314b24804d Mon Sep 17 00:00:00 2001 From: Thiago Pradi Date: Tue, 20 Jul 2010 14:59:10 -0300 Subject: rake db:seed should check if the database have pending migrations [#5163 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 5024787c3c..2c17c74ab4 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -274,7 +274,7 @@ namespace :db do task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ] desc 'Load the seed data from db/seeds.rb' - task :seed => :environment do + task :seed => 'db:abort_if_pending_migrations' do seed_file = File.join(Rails.root, 'db', 'seeds.rb') load(seed_file) if File.exist?(seed_file) end -- cgit v1.2.3 From 992711a86bc4ddd4460f9067e49eea36b37ca94f Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sun, 18 Jul 2010 07:54:48 -0400 Subject: update_attribute should not update readonly attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5106 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/persistence.rb | 2 ++ activerecord/test/cases/persistence_test.rb | 8 +++++++- activerecord/test/fixtures/minivans.yml | 1 + activerecord/test/models/minivan.rb | 5 ++++- activerecord/test/schema/schema.rb | 1 + 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 7ec443ccc7..e2d92c860c 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -105,6 +105,8 @@ module ActiveRecord # Updates a single attribute and saves the record without going through the normal validation procedure # or callbacks. This is especially useful for boolean flags on existing records. def update_attribute(name, value) + raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s + changes = record_update_timestamps || {} if name diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 4ea5df0945..54fe991648 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -17,13 +17,14 @@ require 'models/comment' require 'models/minimalistic' require 'models/warehouse_thing' require 'models/parrot' +require 'models/minivan' require 'models/loose_person' require 'rexml/document' require 'active_support/core_ext/exception' class PersistencesTest < ActiveRecord::TestCase - fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts + fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans def test_create topic = Topic.new @@ -220,6 +221,11 @@ class PersistencesTest < ActiveRecord::TestCase assert !Topic.find(1).approved? end + def test_update_attribute_for_readonly_attribute + minivan = Minivan.find('m1') + assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') } + end + def test_update_attribute_with_one_changed_and_one_updated t = Topic.order('id').limit(1).first title, author_name = t.title, t.author_name diff --git a/activerecord/test/fixtures/minivans.yml b/activerecord/test/fixtures/minivans.yml index e7a2ab77eb..f1224a4c1a 100644 --- a/activerecord/test/fixtures/minivans.yml +++ b/activerecord/test/fixtures/minivans.yml @@ -2,3 +2,4 @@ cool_first: minivan_id: m1 name: my_minivan speedometer_id: s1 + color: blue diff --git a/activerecord/test/models/minivan.rb b/activerecord/test/models/minivan.rb index c753319a20..602438d16f 100644 --- a/activerecord/test/models/minivan.rb +++ b/activerecord/test/models/minivan.rb @@ -3,4 +3,7 @@ class Minivan < ActiveRecord::Base belongs_to :speedometer has_one :dashboard, :through => :speedometer -end \ No newline at end of file + + attr_readonly :color + +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index a69e38f414..f3fd37cd61 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -300,6 +300,7 @@ ActiveRecord::Schema.define do t.string :minivan_id t.string :name t.string :speedometer_id + t.string :color end create_table :minimalistics, :force => true do |t| -- cgit v1.2.3 From d77c3b669ce052234868b3d8e4f066e1baf9dbd5 Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Mon, 19 Jul 2010 21:26:57 -0400 Subject: eagerly loaded association records should respect default_scope [#2931 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/association_preload.rb | 2 +- activerecord/test/cases/relation_scoping_test.rb | 18 ++++++++++++++++-- activerecord/test/models/person.rb | 1 + activerecord/test/models/reference.rb | 5 +++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 9172ab2a20..08601f8ef9 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -378,7 +378,7 @@ module ActiveRecord :order => preload_options[:order] || options[:order] } - reflection.klass.unscoped.apply_finder_options(find_options).to_a + reflection.klass.scoped.apply_finder_options(find_options).to_a end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index a5a3b3ef38..fdf4536bc0 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -5,6 +5,8 @@ require 'models/developer' require 'models/project' require 'models/comment' require 'models/category' +require 'models/person' +require 'models/reference' class RelationScopingTest < ActiveRecord::TestCase fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects @@ -218,7 +220,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase end class HasManyScopingTest< ActiveRecord::TestCase - fixtures :comments, :posts + fixtures :comments, :posts, :people, :references def setup @welcome = Post.find(1) @@ -250,6 +252,18 @@ class HasManyScopingTest< ActiveRecord::TestCase assert_equal 'a comment...', @welcome.comments.what_are_you end end + + def test_should_maintain_default_scope_on_associations + person = people(:michael) + magician = BadReference.find(1) + assert_equal [magician], people(:michael).bad_references + end + + def test_should_maintain_default_scope_on_eager_loaded_associations + michael = Person.where(:id => people(:michael).id).includes(:bad_references).first + magician = BadReference.find(1) + assert_equal [magician], michael.bad_references + end end class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase @@ -399,4 +413,4 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary end -end \ No newline at end of file +end diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 2a73b1ee01..be7463f7c8 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -4,6 +4,7 @@ class Person < ActiveRecord::Base has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments, :conditions => 'comments.id is null' has_many :references + has_many :bad_references has_many :jobs, :through => :references has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true] has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id' diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb index 479e8b72c6..4a17c936f5 100644 --- a/activerecord/test/models/reference.rb +++ b/activerecord/test/models/reference.rb @@ -2,3 +2,8 @@ class Reference < ActiveRecord::Base belongs_to :person belongs_to :job end + +class BadReference < ActiveRecord::Base + self.table_name ='references' + default_scope :conditions => {:favourite => false } +end -- cgit v1.2.3 From 71312443133c6bbcc518d594789f19a213369f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 15:06:23 +0200 Subject: Ensure default_scope can be overwriten by association conditions. --- activerecord/test/cases/relation_scoping_test.rb | 5 +++++ activerecord/test/models/person.rb | 1 + 2 files changed, 6 insertions(+) diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index fdf4536bc0..a50a4d4165 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -259,6 +259,11 @@ class HasManyScopingTest< ActiveRecord::TestCase assert_equal [magician], people(:michael).bad_references end + def test_should_default_scope_on_associations_is_overriden_by_association_conditions + person = people(:michael) + assert_equal [], people(:michael).fixed_bad_references + end + def test_should_maintain_default_scope_on_eager_loaded_associations michael = Person.where(:id => people(:michael).id).includes(:bad_references).first magician = BadReference.find(1) diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index be7463f7c8..951ec93c53 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -5,6 +5,7 @@ class Person < ActiveRecord::Base has_many :references has_many :bad_references + has_many :fixed_bad_references, :conditions => { :favourite => true }, :class_name => 'BadReference' has_many :jobs, :through => :references has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true] has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id' -- cgit v1.2.3 From 9dfe9fa693c98a828dae6ad96eb9798bbdea7d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 15:17:04 +0200 Subject: Ensure insert_before in middleware stack raises a meaningful error message [#3679 state:resolved] --- actionpack/lib/action_dispatch/middleware/stack.rb | 15 +++++++++++---- actionpack/test/dispatch/middleware_stack_test.rb | 10 ++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index 4618f3befc..41078eced7 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -46,7 +46,7 @@ module ActionDispatch end def insert(index, *args, &block) - index = self.index(index) unless index.is_a?(Integer) + index = assert_index(index, :before) middleware = self.class::Middleware.new(*args, &block) super(index, middleware) end @@ -54,9 +54,8 @@ module ActionDispatch alias_method :insert_before, :insert def insert_after(index, *args, &block) - i = index.is_a?(Integer) ? index : self.index(index) - raise "No such middleware to insert after: #{index.inspect}" unless i - insert(i + 1, *args, &block) + index = assert_index(index, :after) + insert(index + 1, *args, &block) end def swap(target, *args, &block) @@ -79,5 +78,13 @@ module ActionDispatch raise "MiddlewareStack#build requires an app" unless app reverse.inject(app) { |a, e| e.build(a) } end + + protected + + def assert_index(index, where) + i = index.is_a?(Integer) ? index : self.index(index) + raise "No such middleware to insert #{where}: #{index.inspect}" unless i + i + end end end diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index 170c5b8565..6a1a4f556f 100644 --- a/actionpack/test/dispatch/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -66,6 +66,16 @@ class MiddlewareStackTest < ActiveSupport::TestCase assert_equal BazMiddleware, @stack[0].klass end + test "raise an error on invalid index" do + assert_raise RuntimeError do + @stack.insert("HiyaMiddleware", BazMiddleware) + end + + assert_raise RuntimeError do + @stack.insert_after("HiyaMiddleware", BazMiddleware) + end + end + test "lazy evaluates middleware class" do assert_difference "@stack.size" do @stack.use "MiddlewareStackTest::BazMiddleware" -- cgit v1.2.3 From e13e8dcf44fd27332ecd4e5c943d3d09efb58c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 15:23:07 +0200 Subject: Use capture instead of yield in link_to_unless. [#5162 state:resolved] --- actionpack/lib/action_view/helpers/url_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 0e3cc54fd1..b493a0cb0e 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -398,7 +398,7 @@ module ActionView def link_to_unless(condition, name, options = {}, html_options = {}, &block) if condition if block_given? - block.arity <= 1 ? yield(name) : yield(name, options, html_options) + block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block) else name end -- cgit v1.2.3 From 659e3b02ab98c8f043dfb02d997758d122034181 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 20 Jul 2010 09:51:06 -0400 Subject: renaming tests by removing proxy_options from names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/named_scope_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index dc85b395d3..7c037b20c5 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -270,27 +270,27 @@ class NamedScopeTest < ActiveRecord::TestCase assert Topic.base.many? end - def test_should_build_with_proxy_options + def test_should_build_on_top_of_named_scope topic = Topic.approved.build({}) assert topic.approved end - def test_should_build_new_with_proxy_options + def test_should_build_new_on_top_of_named_scope topic = Topic.approved.new assert topic.approved end - def test_should_create_with_proxy_options + def test_should_create_on_top_of_named_scope topic = Topic.approved.create({}) assert topic.approved end - def test_should_create_with_bang_with_proxy_options + def test_should_create_with_bang_on_top_of_named_scope topic = Topic.approved.create!({}) assert topic.approved end - def test_should_build_with_proxy_options_chained + def test_should_build_on_top_of_chained_named_scopes topic = Topic.approved.by_lifo.build({}) assert topic.approved assert_equal 'lifo', topic.author_name -- 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(-) 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(+) 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 ef5ae60a07c7d45855a9a2a4b695f153ef9faa79 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 21 Jul 2010 11:04:21 -0300 Subject: Make use of tap to return a previously used var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_record/associations/association_collection.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 692badcc5c..f346a19a3a 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -397,10 +397,10 @@ module ActiveRecord @target = find_target.map do |f| i = @target.index(f) if i - t = @target.delete_at(i) - keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names) - t.attributes = f.attributes.except(*keys) - t + @target.delete_at(i).tap do |t| + keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names) + t.attributes = f.attributes.except(*keys) + end else f end -- cgit v1.2.3 From a1a41a3939318e9481469ea194660f3d8a9c1e66 Mon Sep 17 00:00:00 2001 From: Andrew Kaspick Date: Sat, 17 Jul 2010 17:25:08 -0500 Subject: remote_function patch with more detailed test Signed-off-by: wycats --- actionpack/lib/action_view/helpers/prototype_helper.rb | 2 +- actionpack/test/template/prototype_helper_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 5c0ff5d59c..28b8a27eef 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -139,7 +139,7 @@ module ActionView function = "if (#{options[:condition]}) { #{function}; }" if options[:condition] function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm] - return function + return function.html_safe end # All the methods were moved to GeneratorMethods so that diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 0ff37f44c2..036a44730c 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -104,6 +104,12 @@ class PrototypeHelperTest < PrototypeHelperBaseTest assert_equal javascript_tag(create_generator(&block).to_s, {:defer => 'true'}), update_page_tag({:defer => 'true'}, &block) end + def test_remote_function + res = remote_function(:url => authors_path, :with => "'author[name]='+$F('author_name')+'&author[dob]='+$F('author_dob')") + assert_equal "new Ajax.Request('/authors', {asynchronous:true, evalScripts:true, parameters:'author[name]='+$F('author_name')+'&author[dob]='+$F('author_dob')})", res + assert res.html_safe? + end + protected def author_path(record) "/authors/#{record.id}" -- 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 --- actionmailer/actionmailer.gemspec | 2 +- actionpack/actionpack.gemspec | 2 +- activemodel/activemodel.gemspec | 2 +- activerecord/activerecord.gemspec | 6 +++--- activeresource/activeresource.gemspec | 6 +++--- activesupport/activesupport.gemspec | 2 +- railties/lib/rails/tasks/documentation.rake | 16 ++++++++-------- railties/railties.gemspec | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index fa0ee778c9..daf30e434a 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -13,7 +13,7 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'actionmailer' - s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] + s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*'] s.require_path = 'lib' s.requirements << 'none' diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 9ce7ba5f02..99deff234c 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -13,7 +13,7 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'actionpack' - s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] + s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*'] s.require_path = 'lib' s.requirements << 'none' diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index 6bd6fe49ff..c483ecbc3c 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'activemodel' - s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README', 'lib/**/*'] + s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' s.has_rdoc = true diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index ce10404feb..67d521d56b 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -14,12 +14,12 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'activerecord' - s.files = Dir['CHANGELOG', 'README', 'examples/**/*', 'lib/**/*'] + s.files = Dir['CHANGELOG', 'README.rdoc', 'examples/**/*', 'lib/**/*'] s.require_path = 'lib' s.has_rdoc = true - s.extra_rdoc_files = %w( README ) - s.rdoc_options.concat ['--main', 'README'] + s.extra_rdoc_files = %w( README.rdoc ) + s.rdoc_options.concat ['--main', 'README.rdoc'] s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) diff --git a/activeresource/activeresource.gemspec b/activeresource/activeresource.gemspec index ca74c0dd7d..a71168722b 100644 --- a/activeresource/activeresource.gemspec +++ b/activeresource/activeresource.gemspec @@ -14,12 +14,12 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'activeresource' - s.files = Dir['CHANGELOG', 'README', 'examples/**/*', 'lib/**/*'] + s.files = Dir['CHANGELOG', 'README.rdoc', 'examples/**/*', 'lib/**/*'] s.require_path = 'lib' s.has_rdoc = true - s.extra_rdoc_files = %w( README ) - s.rdoc_options.concat ['--main', 'README'] + s.extra_rdoc_files = %w( README.rdoc ) + s.rdoc_options.concat ['--main', 'README.rdoc'] s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index 8611a1e5fa..df7f68fecf 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'activesupport' - s.files = Dir['CHANGELOG', 'README', 'lib/**/*'] + s.files = Dir['CHANGELOG', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' s.has_rdoc = true 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 622092d33e8d326217ab1ed6138e2c572c95b8ba Mon Sep 17 00:00:00 2001 From: Brian Rose Date: Fri, 16 Jul 2010 15:26:21 -0600 Subject: Fixed a globbed route issue where slashes were being escaped, causing assert_routing to fail. [#5135 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/route_set.rb | 3 ++- actionpack/test/controller/test_test.rb | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 36c52eb65a..a9b97a17eb 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -414,7 +414,8 @@ module ActionDispatch elsif value.is_a?(Array) value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/') else - Rack::Mount::Utils.escape_uri(value.to_param) + return nil unless param = value.to_param + param.split('/').map { |v| Rack::Mount::Utils.escape_uri(v) }.join("/") end end {:parameterize => parameterize} diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index f9fc7a0976..950ad9266f 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -461,6 +461,13 @@ XML def test_assert_routing_in_module assert_routing 'admin/user', :controller => 'admin/user', :action => 'index' end + + def test_assert_routing_with_glob + with_routing do |set| + set.draw { |map| match('*path' => "pages#show") } + assert_routing('/company/about', { :controller => 'pages', :action => 'show', :path => 'company/about' }) + end + end def test_params_passing get :test_params, :page => {:name => "Page name", :month => '4', :year => '2004', :day => '6'} -- cgit v1.2.3 From b0c7dee4f2744286069c5ff4e65c3d081a4cb24a Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Mon, 19 Jul 2010 22:53:54 -0400 Subject: removing unused models from tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5153 state:resolved] Signed-off-by: José Valim --- .../test/cases/associations/belongs_to_associations_test.rb | 2 -- activerecord/test/cases/associations/callbacks_test.rb | 2 -- .../test/cases/associations/cascaded_eager_loading_test.rb | 1 - .../associations/has_and_belongs_to_many_associations_test.rb | 6 ------ activerecord/test/cases/associations_test.rb | 11 ----------- activerecord/test/cases/finder_test.rb | 1 - activerecord/test/cases/method_scoping_test.rb | 3 +-- activerecord/test/cases/persistence_test.rb | 7 ------- activerecord/test/cases/query_cache_test.rb | 2 -- activerecord/test/cases/relations_test.rb | 1 - activerecord/test/cases/transaction_callbacks_test.rb | 1 - .../validations/i18n_generate_message_validation_test.rb | 1 - activerecord/test/cases/validations_test.rb | 5 ----- activerecord/test/cases/xml_serialization_test.rb | 1 - 14 files changed, 1 insertion(+), 43 deletions(-) diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index fb1e6e7e70..4d5769d173 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -5,8 +5,6 @@ require 'models/company' require 'models/topic' require 'models/reply' require 'models/computer' -require 'models/customer' -require 'models/order' require 'models/post' require 'models/author' require 'models/tag' diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb index 91b1af125e..15537d6940 100644 --- a/activerecord/test/cases/associations/callbacks_test.rb +++ b/activerecord/test/cases/associations/callbacks_test.rb @@ -1,8 +1,6 @@ require "cases/helper" require 'models/post' -require 'models/comment' require 'models/author' -require 'models/category' require 'models/project' require 'models/developer' diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index f5d59c9a43..b93e49613d 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -2,7 +2,6 @@ require "cases/helper" require 'models/post' require 'models/comment' require 'models/author' -require 'models/category' require 'models/categorization' require 'models/company' require 'models/topic' diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index d7e9ca0bcb..6b4a1d9408 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -2,20 +2,14 @@ require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' -require 'models/topic' -require 'models/reply' -require 'models/computer' require 'models/customer' require 'models/order' require 'models/categorization' require 'models/category' require 'models/post' require 'models/author' -require 'models/comment' require 'models/tag' require 'models/tagging' -require 'models/person' -require 'models/reader' require 'models/parrot' require 'models/pirate' require 'models/treasure' diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index ff9e9a472a..a1c794c084 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -2,11 +2,6 @@ require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' -require 'models/topic' -require 'models/reply' -require 'models/computer' -require 'models/customer' -require 'models/order' require 'models/categorization' require 'models/category' require 'models/post' @@ -19,12 +14,6 @@ require 'models/reader' require 'models/parrot' require 'models/ship_part' require 'models/ship' -require 'models/treasure' -require 'models/price_estimate' -require 'models/club' -require 'models/member' -require 'models/membership' -require 'models/sponsor' class AssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :developers_projects, diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 860d330a7f..a107c1a474 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -10,7 +10,6 @@ require 'models/entrant' require 'models/project' require 'models/developer' require 'models/customer' -require 'models/job' class DynamicFinderMatchTest < ActiveRecord::TestCase def test_find_no_match diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 4e8ce1dac1..774b50e2e4 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -8,7 +8,6 @@ require 'models/author' require 'models/developer' require 'models/project' require 'models/comment' -require 'models/category' class MethodScopingTest < ActiveRecord::TestCase fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects @@ -543,4 +542,4 @@ class NestedScopingTest < ActiveRecord::TestCase assert_equal 1, scoped_authors.size assert_equal authors(:david).attributes, scoped_authors.first.attributes end -end \ No newline at end of file +end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 54fe991648..1cc3a337c3 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -5,15 +5,8 @@ require 'models/topic' require 'models/reply' require 'models/category' require 'models/company' -require 'models/customer' require 'models/developer' require 'models/project' -require 'models/default' -require 'models/auto_id' -require 'models/column_name' -require 'models/subscriber' -require 'models/keyboard' -require 'models/comment' require 'models/minimalistic' require 'models/warehouse_thing' require 'models/parrot' diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 68abca70b3..f0d97a00d0 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -1,8 +1,6 @@ require "cases/helper" require 'models/topic' -require 'models/reply' require 'models/task' -require 'models/course' require 'models/category' require 'models/post' diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ffde8daa07..cb252d56fe 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1,5 +1,4 @@ require "cases/helper" -require 'models/tag' require 'models/tagging' require 'models/post' require 'models/topic' diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index df123c9de8..ffc2cd638f 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -1,6 +1,5 @@ require "cases/helper" require 'models/topic' -require 'models/reply' class TransactionCallbacksTest < ActiveRecord::TestCase self.use_transactional_fixtures = false diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb index 454e42ed37..628029f8df 100644 --- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb @@ -1,6 +1,5 @@ require "cases/helper" require 'models/topic' -require 'models/reply' class I18nGenerateMessageValidationTest < ActiveRecord::TestCase def setup diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 3f1b0e333f..fd771ef4be 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -4,11 +4,6 @@ require 'models/topic' require 'models/reply' require 'models/person' require 'models/developer' -require 'models/warehouse_thing' -require 'models/guid' -require 'models/owner' -require 'models/pet' -require 'models/event' require 'models/parrot' require 'models/company' diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb index 751946ffc5..b11b340e94 100644 --- a/activerecord/test/cases/xml_serialization_test.rb +++ b/activerecord/test/cases/xml_serialization_test.rb @@ -2,7 +2,6 @@ require "cases/helper" require 'models/contact' require 'models/post' require 'models/author' -require 'models/tagging' require 'models/comment' require 'models/company_in_module' -- cgit v1.2.3 From 5c858220085dc4ddc1bec496747059dfbe32f1da Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 05:08:34 +0800 Subject: Hash#to_param is doesn't use sort anymore, some tests added for Hash#to_param --- .../lib/active_support/core_ext/object/to_param.rb | 2 +- activesupport/test/core_ext/hash_ext_test.rb | 25 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index 06f077e920..f2e7c2351e 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -44,6 +44,6 @@ class Hash def to_param(namespace = nil) collect do |key, value| value.to_query(namespace ? "#{namespace}[#{key}]" : key) - end.sort * '&' + end * '&' end end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 7b2c10908f..f369f55675 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -2,6 +2,8 @@ require 'abstract_unit' require 'active_support/core_ext/hash' require 'bigdecimal' require 'active_support/core_ext/string/access' +require 'active_support/ordered_hash' +require 'active_support/core_ext/object/conversions' class HashExtTest < Test::Unit::TestCase def setup @@ -449,6 +451,29 @@ class IWriteMyOwnXML end end +class HashExtToParamTests < Test::Unit::TestCase + class ToParam < String + def to_param + "#{self}-1" + end + end + + def test_string_hash + assert_equal '', {}.to_param + assert_equal 'hello=world', { :hello => "world" }.to_param + assert_equal 'hello=10', { "hello" => 10 }.to_param + assert_equal 'hello=world&say_bye=true', ActiveSupport::OrderedHash[:hello, "world", "say_bye", true].to_param + end + + def test_number_hash + assert_equal '10=20&30=40&50=60', ActiveSupport::OrderedHash[10, 20, 30, 40, 50, 60].to_param + end + + def test_to_param_hash + assert_equal 'custom=param-1&custom2=param2-1', ActiveSupport::OrderedHash[ToParam.new('custom'), ToParam.new('param'), ToParam.new('custom2'), ToParam.new('param2')].to_param + end +end + class HashToXmlTest < Test::Unit::TestCase def setup @xml_options = { :root => :person, :skip_instruct => true, :indent => 0 } -- cgit v1.2.3 From 6cbd085f692aae7518ac67380e805ebb65896951 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 06:16:27 +0800 Subject: Test Hash#to_param escapes keys and values [#5175] --- activesupport/test/core_ext/hash_ext_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index f369f55675..5d9846a216 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -472,6 +472,10 @@ class HashExtToParamTests < Test::Unit::TestCase def test_to_param_hash assert_equal 'custom=param-1&custom2=param2-1', ActiveSupport::OrderedHash[ToParam.new('custom'), ToParam.new('param'), ToParam.new('custom2'), ToParam.new('param2')].to_param end + + def test_to_param_hash_escapes_its_keys_and_values + assert_equal 'param+1=A+string+with+%2F+characters+%26+that+should+be+%3F+escaped', { 'param 1' => 'A string with / characters & that should be ? escaped' }.to_param + end end class HashToXmlTest < Test::Unit::TestCase -- cgit v1.2.3 From aa2235be7bad43fcd5c314b76811d736b4084c93 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sun, 18 Jul 2010 18:02:40 -0400 Subject: replacing around with for in the comments for callbacks --- activemodel/lib/active_model/callbacks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb index e7aad17021..8c10c54b54 100644 --- a/activemodel/lib/active_model/callbacks.rb +++ b/activemodel/lib/active_model/callbacks.rb @@ -19,7 +19,7 @@ module ActiveModel # # define_model_callbacks :create, :update # - # This will provide all three standard callbacks (before, around and after) around + # This will provide all three standard callbacks (before, around and after) for # both the :create and :update methods. To implement, you need to wrap the methods # you want callbacks on in a block so that the callbacks get a chance to fire: # -- cgit v1.2.3 From efdfcf13257769cf2d6f9ad85c8538e64007d97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 19 Jul 2010 12:06:19 +0200 Subject: correct typos in Routing examples --- actionpack/lib/action_dispatch/routing.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index b7e09b99d1..683dd72555 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -105,7 +105,7 @@ module ActionDispatch # You can specify a regular expression to define a format for a parameter. # # controller 'geocode' do - # match 'geocode/:postalcode' => :show', :constraints => { + # match 'geocode/:postalcode' => :show, :constraints => { # :postalcode => /\d{5}(-\d{4})?/ # } # @@ -113,13 +113,13 @@ module ActionDispatch # expression modifiers: # # controller 'geocode' do - # match 'geocode/:postalcode' => :show', :constraints => { + # match 'geocode/:postalcode' => :show, :constraints => { # :postalcode => /hx\d\d\s\d[a-z]{2}/i # } # end # # controller 'geocode' do - # match 'geocode/:postalcode' => :show', :constraints => { + # match 'geocode/:postalcode' => :show, :constraints => { # :postalcode => /# Postcode format # \d{5} #Prefix # (-\d{4})? #Suffix -- cgit v1.2.3 From 5c137939a7b9d32a46eb07c90d6b535c7c67db64 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 20 Jul 2010 06:56:20 -0400 Subject: expanded comment for update_attribute method --- activerecord/lib/active_record/persistence.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index e2d92c860c..b587abd5d0 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -102,8 +102,15 @@ module ActiveRecord became end - # Updates a single attribute and saves the record without going through the normal validation procedure - # or callbacks. This is especially useful for boolean flags on existing records. + # Updates a single attribute and saves the record. + # This is especially useful for boolean flags on existing records. Also note that + # + # * validation is skipped + # * No callbacks are invoked + # * updated_at/updated_on column is updated if that column is available + # * does not work on associations + # * does not work on attr_accessor attributes. The attribute that is being updated must be column name. + # def update_attribute(name, value) raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s -- 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(-) 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(-) 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 ab62aa1c4309b670c5e3bdce2cf35113e1aa874b Mon Sep 17 00:00:00 2001 From: Jeroen van Dijk Date: Wed, 21 Jul 2010 17:10:24 +0200 Subject: Mention that ActionMailer::Base.default_url_options is now deprecated --- actionmailer/lib/action_mailer/base.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 7f2ed5ba64..7bbde53306 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -125,14 +125,13 @@ module ActionMailer #:nodoc: # make sense to generate relative URLs in email messages. # # It is also possible to set a default host that will be used in all mailers by setting the :host - # option in the ActionMailer::Base.default_url_options hash as follows: - # - # ActionMailer::Base.default_url_options[:host] = "example.com" - # - # This can also be set as a configuration option in config/application.rb: + # option as a configuration option in config/application.rb: # # config.action_mailer.default_url_options = { :host => "example.com" } # + # Setting ActionMailer::Base.default_url_options directly is now deprecated, use the configuration + # option mentioned above to set the default host. + # # If you do decide to set a default :host for your mailers you will want to use the # :only_path => false option when using url_for. This will ensure that absolute URLs are # generated because the url_for view helper will, by default, generate relative URLs when a -- cgit v1.2.3 From 198975ecee96632ef23064d2b8e772d042bca4ac Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Wed, 21 Jul 2010 19:21:05 +0200 Subject: doc: form_for does return output rather than merely evaluate its block --- actionpack/lib/action_view/helpers/form_helper.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 524913ae29..b0af836522 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -150,10 +150,6 @@ module ActionView # here a named route directly as well. Defaults to the current action. # * :html - Optional HTML attributes for the form tag. # - # Worth noting is that the +form_for+ tag is called in a ERb evaluation - # block, not an ERb output block. So that's <% %>, not - # <%= %>. - # # Also note that +form_for+ doesn't create an exclusive scope. It's still # possible to use both the stand-alone FormHelper methods and methods # from FormTagHelper. For example: -- 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(-) 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(-) 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]

---
 activesupport/lib/active_support/inflector/methods.rb        | 12 +++++++++++-
 .../guides/source/active_support_core_extensions.textile     |  6 +++++-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index b3dc5b2f3a..de49750083 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -20,6 +20,11 @@ module ActiveSupport
     #   "active_record".camelize(:lower)        # => "activeRecord"
     #   "active_record/errors".camelize         # => "ActiveRecord::Errors"
     #   "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
+    #
+    # 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 # => "SslError"
     def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
       if first_letter_in_uppercase
         lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
@@ -28,13 +33,18 @@ module ActiveSupport
       end
     end
 
-    # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
+    # Makes an underscored, lowercase form from the expression in the string.
     #
     # Changes '::' to '/' to convert namespaces to paths.
     #
     # Examples:
     #   "ActiveRecord".underscore         # => "active_record"
     #   "ActiveRecord::Errors".underscore # => active_record/errors
+    #
+    # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
+    # though there are cases where that does not hold:
+    #
+    #   "SSLError".underscore.camelize # => "SslError"
     def underscore(camel_cased_word)
       word = camel_cased_word.to_s.dup
       word.gsub!(/::/, '/')
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