From 45c233ef819dc7b67e259dd73f24721fec28b8c8 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Sat, 26 Mar 2011 11:42:26 -0300 Subject: Removed #update_attribute method. New #update_column method. Signed-off-by: Santiago Pastorino --- .../associations/has_one_association.rb | 3 +- activerecord/lib/active_record/persistence.rb | 18 ++--- .../has_and_belongs_to_many_associations_test.rb | 2 +- .../associations/has_many_associations_test.rb | 6 +- .../has_many_through_associations_test.rb | 4 +- .../has_one_through_associations_test.rb | 4 +- .../test/cases/associations/join_model_test.rb | 12 +-- activerecord/test/cases/associations_test.rb | 4 +- activerecord/test/cases/base_test.rb | 2 +- activerecord/test/cases/calculations_test.rb | 4 +- activerecord/test/cases/dirty_test.rb | 5 +- activerecord/test/cases/persistence_test.rb | 88 +++++++++++----------- activerecord/test/cases/timestamp_test.rb | 6 +- 13 files changed, 78 insertions(+), 80 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 1d2e8667e4..dfcb116392 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -34,7 +34,8 @@ module ActiveRecord when :destroy target.destroy when :nullify - target.update_attribute(reflection.foreign_key, nil) + target.send("#{reflection.foreign_key}=", nil) + target.save(:validations => false) end end end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 17a64b6e86..3377a5934b 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -104,19 +104,17 @@ module ActiveRecord became end - # Updates a single attribute and saves the record. - # This is especially useful for boolean flags on existing records. Also note that + # Updates a single attribute of an object, without calling save. # # * Validation is skipped. - # * Callbacks are invoked. - # * updated_at/updated_on column is updated if that column is available. - # * Updates all the attributes that are dirty in this object. + # * Callbacks are skipped. + # * updated_at/updated_on column is not updated in any case. # - def update_attribute(name, value) + def update_column(name, value) name = name.to_s raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name) send("#{name}=", value) - save(:validate => false) + self.class.update_all({ name => value }, self.class.primary_key => id) end # Updates the attributes of the model from the passed-in hash and saves the @@ -156,7 +154,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def increment!(attribute, by = 1) - increment(attribute, by).update_attribute(attribute, self[attribute]) + increment(attribute, by).update_column(attribute, self[attribute]) end # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1). @@ -173,7 +171,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def decrement!(attribute, by = 1) - decrement(attribute, by).update_attribute(attribute, self[attribute]) + decrement(attribute, by).update_column(attribute, self[attribute]) end # Assigns to +attribute+ the boolean opposite of attribute?. So @@ -190,7 +188,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def toggle!(attribute) - toggle(attribute).update_attribute(attribute, self[attribute]) + toggle(attribute).update_column(attribute, self[attribute]) end # Reloads the attributes of this object from the database. 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 73d02c9676..f4d14853d3 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 @@ -604,7 +604,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase project = SpecialProject.create("name" => "Special Project") assert developer.save developer.projects << project - developer.update_attribute("name", "Bruza") + developer.update_column("name", "Bruza") assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i SELECT count(*) FROM developers_projects WHERE project_id = #{project.id} diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index ad774eb9ce..16d4877fe8 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -639,7 +639,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_deleting_updates_counter_cache_with_dependent_delete_all post = posts(:welcome) - post.update_attribute(:taggings_with_delete_all_count, post.taggings_count) + post.update_column(:taggings_with_delete_all_count, post.taggings_count) assert_difference "post.reload.taggings_with_delete_all_count", -1 do post.taggings_with_delete_all.delete(post.taggings_with_delete_all.first) @@ -648,7 +648,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_deleting_updates_counter_cache_with_dependent_destroy post = posts(:welcome) - post.update_attribute(:taggings_with_destroy_count, post.taggings_count) + post.update_column(:taggings_with_destroy_count, post.taggings_count) assert_difference "post.reload.taggings_with_destroy_count", -1 do post.taggings_with_destroy.delete(post.taggings_with_destroy.first) @@ -787,7 +787,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase firm = Firm.find(:first) # break the vanilla firm_id foreign key assert_equal 2, firm.clients.count - firm.clients.first.update_attribute(:firm_id, nil) + firm.clients.first.update_column(:firm_id, nil) assert_equal 1, firm.clients(true).count assert_equal 1, firm.clients_using_primary_key_with_delete_all.count old_record = firm.clients_using_primary_key_with_delete_all.first diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 9adaebe924..1efe3420a0 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -286,7 +286,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_update_counter_caches_on_delete_with_dependent_destroy post = posts(:welcome) tag = post.tags.create!(:name => 'doomed') - post.update_attribute(:tags_with_destroy_count, post.tags.count) + post.update_column(:tags_with_destroy_count, post.tags.count) assert_difference ['post.reload.taggings_count', 'post.reload.tags_with_destroy_count'], -1 do posts(:welcome).tags_with_destroy.delete(tag) @@ -296,7 +296,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_update_counter_caches_on_delete_with_dependent_nullify post = posts(:welcome) tag = post.tags.create!(:name => 'doomed') - post.update_attribute(:tags_with_nullify_count, post.tags.count) + post.update_column(:tags_with_nullify_count, post.tags.count) assert_no_difference 'post.reload.taggings_count' do assert_difference 'post.reload.tags_with_nullify_count', -1 do diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 9ba5549277..968025ade8 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -90,12 +90,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_with_conditions_eager_loading # conditions on the through table assert_equal clubs(:moustache_club), Member.find(@member.id, :include => :favourite_club).favourite_club - memberships(:membership_of_favourite_club).update_attribute(:favourite, false) + memberships(:membership_of_favourite_club).update_column(:favourite, false) assert_equal nil, Member.find(@member.id, :include => :favourite_club).reload.favourite_club # conditions on the source table assert_equal clubs(:moustache_club), Member.find(@member.id, :include => :hairy_club).hairy_club - clubs(:moustache_club).update_attribute(:name, "Association of Clean-Shaven Persons") + clubs(:moustache_club).update_column(:name, "Association of Clean-Shaven Persons") assert_equal nil, Member.find(@member.id, :include => :hairy_club).reload.hairy_club end diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 1f95b31497..5a7b139030 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -161,7 +161,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_many_with_delete_all assert_equal 1, posts(:welcome).taggings.count - posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDeleteAll' + posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyDeleteAll' post = find_post_with_dependency(1, :has_many, :taggings, :delete_all) old_count = Tagging.count @@ -172,7 +172,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_many_with_destroy assert_equal 1, posts(:welcome).taggings.count - posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDestroy' + posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyDestroy' post = find_post_with_dependency(1, :has_many, :taggings, :destroy) old_count = Tagging.count @@ -183,7 +183,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_many_with_nullify assert_equal 1, posts(:welcome).taggings.count - posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyNullify' + posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyNullify' post = find_post_with_dependency(1, :has_many, :taggings, :nullify) old_count = Tagging.count @@ -194,7 +194,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_one_with_destroy assert posts(:welcome).tagging - posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneDestroy' + posts(:welcome).tagging.update_column :taggable_type, 'PostWithHasOneDestroy' post = find_post_with_dependency(1, :has_one, :tagging, :destroy) old_count = Tagging.count @@ -205,7 +205,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_one_with_nullify assert posts(:welcome).tagging - posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneNullify' + posts(:welcome).tagging.update_column :taggable_type, 'PostWithHasOneNullify' post = find_post_with_dependency(1, :has_one, :tagging, :nullify) old_count = Tagging.count @@ -707,7 +707,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase # create dynamic Post models to allow different dependency options def find_post_with_dependency(post_id, association, association_name, dependency) class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}" - Post.find(post_id).update_attribute :type, class_name + Post.find(post_id).update_column :type, class_name klass = Object.const_set(class_name, Class.new(ActiveRecord::Base)) klass.set_table_name 'posts' klass.send(association, association_name, :as => :taggable, :dependent => dependency) diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 47b8e48582..04f628a398 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -66,7 +66,7 @@ class AssociationsTest < ActiveRecord::TestCase 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') + ShipPart.find(part.id).update_column(:name, 'Deck') ship.parts.send(:load_target) assert_equal 'Deck', ship.parts[0].name end @@ -170,7 +170,7 @@ class AssociationProxyTest < ActiveRecord::TestCase david = developers(:david) assert !david.projects.loaded? - david.update_attribute(:created_at, Time.now) + david.update_column(:created_at, Time.now) assert !david.projects.loaded? end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index fba7af741d..aeb0b28bab 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -484,7 +484,7 @@ class BasicsTest < ActiveRecord::TestCase weird.reload assert_equal 'value', weird.send('a$b') - weird.update_attribute('a$b', 'value2') + weird.update_column('a$b', 'value2') weird.reload assert_equal 'value2', weird.send('a$b') end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index caf07a7357..c97f1ae634 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -311,8 +311,8 @@ class CalculationsTest < ActiveRecord::TestCase def test_should_count_scoped_select_with_options Account.update_all("credit_limit = NULL") - Account.last.update_attribute('credit_limit', 49) - Account.first.update_attribute('credit_limit', 51) + Account.last.update_column('credit_limit', 49) + Account.first.update_column('credit_limit', 51) assert_equal 1, Account.scoped(:select => "credit_limit").count(:conditions => ['credit_limit >= 50']) end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index a6738fb654..b75482a956 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -413,7 +413,7 @@ class DirtyTest < ActiveRecord::TestCase with_partial_updates(Topic) do Topic.create!(:author_name => 'Bill', :content => {:a => "a"}) topic = Topic.select('id, author_name').first - topic.update_attribute :author_name, 'John' + topic.update_column :author_name, 'John' topic = Topic.first assert_not_nil topic.content end @@ -487,7 +487,8 @@ class DirtyTest < ActiveRecord::TestCase assert !pirate.previous_changes.key?('created_on') pirate = Pirate.find_by_catchphrase("Ahoy!") - pirate.update_attribute(:catchphrase, "Ninjas suck!") + pirate.catchphrase = "Ninjas suck!" + pirate.save(:validations => false) assert_equal 2, pirate.previous_changes.size assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes['catchphrase'] diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 8ca9d626d1..68d861c9c2 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -327,66 +327,62 @@ class PersistencesTest < ActiveRecord::TestCase assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } end - def test_update_attribute - assert !Topic.find(1).approved? - Topic.find(1).update_attribute("approved", true) - assert Topic.find(1).approved? + def test_update_column + topic = Topic.find(1) + topic.update_column("approved", true) + assert topic.approved? + topic.reload + assert topic.approved? - Topic.find(1).update_attribute(:approved, false) - assert !Topic.find(1).approved? + topic.update_column(:approved, false) + assert !topic.approved? + topic.reload + assert !topic.approved? end - def test_update_attribute_for_readonly_attribute + def test_update_column_with_model_having_primary_key_other_than_id minivan = Minivan.find('m1') - assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') } - end - - # This test is correct, but it is hard to fix it since - # update_attribute trigger simply call save! that triggers - # all callbacks. - # 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 - # t.author_name = 'John' - # t.update_attribute(:title, 'super_title') - # assert_equal 'John', t.author_name - # assert_equal 'super_title', t.title - # assert t.changed?, "topic should have changed" - # assert t.author_name_changed?, "author_name should have changed" - # assert !t.title_changed?, "title should not have changed" - # assert_nil t.title_change, 'title change should be nil' - # assert_equal ['author_name'], t.changed - # - # t.reload - # assert_equal 'David', t.author_name - # assert_equal 'super_title', t.title - # end - - def test_update_attribute_with_one_updated - t = Topic.first - title = t.title - t.update_attribute(:title, 'super_title') - assert_equal 'super_title', t.title - assert !t.changed?, "topic should not have changed" - assert !t.title_changed?, "title should not have changed" - assert_nil t.title_change, 'title change should be nil' + new_name = 'sebavan' - t.reload - assert_equal 'super_title', t.title + minivan.update_column(:name, new_name) + assert_equal new_name, minivan.name end - def test_update_attribute_for_updated_at_on + def test_update_column_for_readonly_attribute + minivan = Minivan.find('m1') + prev_color = minivan.color + assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_column(:color, 'black') } + assert_equal prev_color, minivan.color + end + + def test_update_column_should_not_modify_updated_at developer = Developer.find(1) prev_month = Time.now.prev_month - developer.update_attribute(:updated_at, prev_month) + developer.update_column(:updated_at, prev_month) assert_equal prev_month, developer.updated_at - developer.update_attribute(:salary, 80001) - assert_not_equal prev_month, developer.updated_at + developer.update_column(:salary, 80001) + assert_equal prev_month, developer.updated_at developer.reload - assert_not_equal prev_month, developer.updated_at + assert_equal prev_month, developer.updated_at + end + + def test_update_column_with_one_changed_and_one_updated + t = Topic.order('id').limit(1).first + title, author_name = t.title, t.author_name + t.author_name = 'John' + t.update_column(:title, 'super_title') + assert_equal 'John', t.author_name + assert_equal 'super_title', t.title + assert t.changed?, "topic should have changed" + assert t.author_name_changed?, "author_name should have changed" + assert t.title_changed?, "title should have changed" + + t.reload + assert_equal author_name, t.author_name + assert_equal 'super_title', t.title end def test_update_attributes diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 1c21f0f3b6..08bbd14d38 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -113,7 +113,8 @@ class TimestampTest < ActiveRecord::TestCase pet = Pet.first owner = pet.owner - owner.update_attribute(:happy_at, 3.days.ago) + owner.happy_at = 3.days.ago + owner.save previously_owner_updated_at = owner.updated_at pet.name = "I'm a parrot" @@ -131,8 +132,9 @@ class TimestampTest < ActiveRecord::TestCase toy = Toy.first pet = toy.pet owner = pet.owner + time = 3.days.ago - owner.update_attribute(:updated_at, (time = 3.days.ago)) + owner.update_column(:updated_at, time) toy.touch owner.reload -- cgit v1.2.3 From cb3e96a447df592947ae10221c7494eb8bf08012 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 26 Mar 2011 10:28:39 -0700 Subject: Make JavaScriptHelper#j() an alias for JavaScriptHelper#escape_javascript() -- note this then supersedes the Object#j() method that the JSON gem adds within templates using the JavaScriptHelper [DHH] --- actionpack/CHANGELOG | 2 ++ actionpack/lib/action_view/helpers/javascript_helper.rb | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 5ab92c8cfc..a8a4338630 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.1.0 (unreleased)* +* Make JavaScriptHelper#j() an alias for JavaScriptHelper#escape_javascript() -- note this then supersedes the Object#j() method that the JSON gem adds within templates using the JavaScriptHelper [DHH] + * Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. [Prem Sichanugrist, fxn] * URL parameters which return false for to_param now appear in the query string (previously they were removed) [Andrew White] diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index cd3a3eac80..6eda23a9a3 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -47,6 +47,9 @@ module ActionView "'" => "\\'" } # Escape carrier returns and single and double quotes for JavaScript segments. + # Also available through the alias j(). This is particularly helpful in JavaScript responses, like: + # + # $('some_element').replaceWith('<%=j render 'some/element_template' %>'); def escape_javascript(javascript) if javascript javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] } @@ -55,6 +58,8 @@ module ActionView end end + alias_method :escape_javascript, :j + # Returns a JavaScript tag with the +content+ inside. Example: # javascript_tag "alert('All is good')" # -- cgit v1.2.3 From dea3d2dd203fe2008ccfae6b6575da0b0001e466 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 26 Mar 2011 11:47:20 -0700 Subject: adding a test for attributes after type cast. thanks nragaz. :heart: --- activerecord/test/cases/attribute_methods_test.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index d03c3ee1a1..eacb18980c 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -132,6 +132,23 @@ class AttributeMethodsTest < ActiveRecord::TestCase end end + def test_read_attributes_after_type_cast_on_datetime + in_time_zone "Pacific Time (US & Canada)" do + record = @target.new + + record.written_on = "2011-03-24" + assert_equal "2011-03-24", record.written_on_before_type_cast + assert_equal Time.zone.parse("2011-03-24"), record.written_on + assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], + record.written_on.time_zone + + record.save + record.reload + + assert_equal Time.zone.parse("2011-03-24"), record.written_on + end + end + def test_hash_content topic = Topic.new topic.content = { "one" => 1, "two" => 2 } -- cgit v1.2.3 From f8a05ad297d637596d029b013bb65128ca0aa8bd Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 26 Mar 2011 14:44:36 -0700 Subject: Allow FormHelper#form_for to specify the :method as a direct option instead of through the :html hash [DHH] --- actionpack/CHANGELOG | 4 ++++ actionpack/lib/action_view/helpers/form_helper.rb | 15 +++++++++++++-- actionpack/test/template/form_helper_test.rb | 21 +++++++++++++++++++-- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index a8a4338630..cc19402243 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,9 @@ *Rails 3.1.0 (unreleased)* +* Allow FormHelper#form_for to specify the :method as a direct option instead of through the :html hash [DHH] + + form_for(@post, remote: true, method: :delete) instead of form_for(@post, remote: true, html: { method: :delete }) + * Make JavaScriptHelper#j() an alias for JavaScriptHelper#escape_javascript() -- note this then supersedes the Object#j() method that the JSON gem adds within templates using the JavaScriptHelper [DHH] * Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. [Prem Sichanugrist, fxn] diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 48abf119f1..9025d9e24c 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -185,7 +185,7 @@ module ActionView # # is equivalent to something like: # - # <%= form_for @post, :as => :post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %> + # <%= form_for @post, :as => :post, :url => post_path(@post), :method => :put, :html => { :class => "edit_post", :id => "edit_post_45" } do |f| %> # ... # <% end %> # @@ -236,6 +236,16 @@ module ActionView # Where @document = Document.find(params[:id]) and # @comment = Comment.new. # + # === Setting the method + # + # You can force the form to use the full array of HTTP verbs by setting + # + # :method => (:get|:post|:put|:delete) + # + # in the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the + # form will be set to POST and a hidden input called _method will carry the intended verb for the server + # to interpret. + # # === Unobtrusive JavaScript # # Specifying: @@ -298,7 +308,7 @@ module ActionView # # In this case, if you use this: # - # <%= render :partial => f %> + # <%= render f %> # # The rendered template is people/_labelling_form and the local # variable referencing the form builder is called @@ -350,6 +360,7 @@ module ActionView end options[:html][:remote] = options.delete(:remote) + options[:html][:method] = options.delete(:method) if options.has_key?(:method) options[:html][:authenticity_token] = options.delete(:authenticity_token) builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &proc) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 359b078466..ff183d097d 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -715,14 +715,31 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_form_for_with_method_as_part_of_html_options + form_for(@post, :url => '/', :html => { :id => 'create-post', :method => :delete }) do |f| + concat f.text_field(:title) + concat f.text_area(:body) + concat f.check_box(:secret) + end + + expected = whole_form("/", "create-post", "edit_post", "delete") do + "" + + "" + + "" + + "" + end + + assert_dom_equal expected, output_buffer + end + def test_form_for_with_method - form_for(@post, :url => '/', :html => { :id => 'create-post', :method => :put }) do |f| + form_for(@post, :url => '/', :method => :delete, :html => { :id => 'create-post' }) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form("/", "create-post", "edit_post", "put") do + expected = whole_form("/", "create-post", "edit_post", "delete") do "" + "" + "" + -- cgit v1.2.3 From 5013fe3a1dcc0472ff11b6f94260d5dc224b5305 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 26 Mar 2011 17:04:54 -0700 Subject: refactoring tz to a variable rather than repeating it --- activerecord/test/cases/attribute_methods_test.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index eacb18980c..34271be8b0 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -133,13 +133,15 @@ class AttributeMethodsTest < ActiveRecord::TestCase end def test_read_attributes_after_type_cast_on_datetime - in_time_zone "Pacific Time (US & Canada)" do + tz = "Pacific Time (US & Canada)" + + in_time_zone tz do record = @target.new record.written_on = "2011-03-24" assert_equal "2011-03-24", record.written_on_before_type_cast assert_equal Time.zone.parse("2011-03-24"), record.written_on - assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], + assert_equal ActiveSupport::TimeZone[tz], record.written_on.time_zone record.save -- cgit v1.2.3 From a9d27c04abf24dc85be061ff9772d71897af02b1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 26 Mar 2011 17:16:24 -0700 Subject: cleaning up typecast test a little --- activerecord/test/cases/attribute_methods_test.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 34271be8b0..84f75cc803 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -138,16 +138,18 @@ class AttributeMethodsTest < ActiveRecord::TestCase in_time_zone tz do record = @target.new - record.written_on = "2011-03-24" - assert_equal "2011-03-24", record.written_on_before_type_cast - assert_equal Time.zone.parse("2011-03-24"), record.written_on - assert_equal ActiveSupport::TimeZone[tz], - record.written_on.time_zone + date_string = "2011-03-24" + time = Time.zone.parse date_string + + record.written_on = date_string + assert_equal date_string, record.written_on_before_type_cast + assert_equal time, record.written_on + assert_equal ActiveSupport::TimeZone[tz], record.written_on.time_zone record.save record.reload - assert_equal Time.zone.parse("2011-03-24"), record.written_on + assert_equal time, record.written_on end end -- cgit v1.2.3 From cc6fa2f4d718c2d7a990fe23c38fc0ea4f2391d9 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 27 Mar 2011 11:20:54 -0700 Subject: Fix alias_method, add test --- actionpack/lib/action_view/helpers/javascript_helper.rb | 2 +- actionpack/test/template/javascript_helper_test.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 6eda23a9a3..a19ba7a968 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -58,7 +58,7 @@ module ActionView end end - alias_method :escape_javascript, :j + alias_method :j, :escape_javascript # Returns a JavaScript tag with the +content+ inside. Example: # javascript_tag "alert('All is good')" diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index 2e7484afaf..8aa2730da1 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -27,6 +27,7 @@ class JavaScriptHelperTest < ActionView::TestCase assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos')) assert_equal %(backslash\\\\test), escape_javascript( %(backslash\\test) ) assert_equal %(dont <\\/close> tags), escape_javascript(%(dont tags)) + assert_equal %(dont <\\/close> tags), j(%(dont tags)) end def test_button_to_function -- cgit v1.2.3 From b2d94322e6f2c2324154465147938ca8b16c610d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 27 Mar 2011 20:45:23 +0200 Subject: fixes a couple of regexps, the suite showed warnings about them A couple of things worth mentioning here: - "{" is a metacharacter, should be escaped if it is meant to match a "{". The code worked, though, because the regexp engine is tolerant to this, but issued warnings. - gsub accepts a string as first argument. That's the best idiom to use when your pattern has no metacharacters, since gsub interprets the string as an exact substring to look for, rather than a regexp. The benefit is that your pattern is crystal clear and needs no backslashes. --- actionpack/lib/action_view/template/resolver.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 6c1063592f..41c6310ae2 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -157,8 +157,8 @@ module ActionView query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}") } - query.gsub!(/\.{html,/, ".{html,text.html,") - query.gsub!(/\.{text,/, ".{text,text.plain,") + query.gsub!('.{html,', '.{html,text.html,') + query.gsub!('.{text,', '.{text,text.plain,') File.expand_path(query, @path) end -- cgit v1.2.3 From 2c0c4d754e34b13379dfc53121a970c25fab5dae Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Sun, 27 Mar 2011 23:55:46 +0700 Subject: Add `config.force_ssl` configuration which will load `Rack::SSL` middleware if set to true This will allow user to be able to force all requests to be under HTTPS protocol. This commit was a request from DHH. Special thanks to Josh Peek as well for making `Rack::SSL`. --- railties/CHANGELOG | 2 ++ railties/guides/source/configuring.textile | 3 +++ railties/guides/source/security.textile | 6 +++++- railties/lib/rails/application.rb | 17 +++++++++++++---- railties/lib/rails/application/configuration.rb | 3 ++- .../templates/config/environments/development.rb.tt | 2 +- .../app/templates/config/environments/production.rb.tt | 18 +++++++----------- railties/railties.gemspec | 1 + railties/test/application/middleware_test.rb | 6 ++++++ 9 files changed, 40 insertions(+), 18 deletions(-) diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 75f1df44e7..c1e0a214d2 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.1.0 (unreleased)* +* Added `config.force_ssl` configuration which loads Rack::SSL middleware and force all requests to be under HTTPS protocol [DHH, Prem Sichanugrist, and Josh Peek] + * Added `rails plugin new` command which generates rails plugin with gemspec, tests and dummy application for testing [Piotr Sarnacki] * Added -j parameter with jquery/prototype as options. Now you can create your apps with jQuery using `rails new myapp -j jquery`. The default is still Prototype. [siong1987] diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 62b846e871..04e2d6daed 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -81,6 +81,8 @@ end * +config.filter_parameters+ used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers. +* +config.force_ssl+ forcing all requests to be under HTTPS protocol by using +Rack::SSL+ middleware. This will secure your application from a session hijack attempt. + * +config.helper_paths+ configures where Rails can find helpers for this application. * +config.log_level+ defines the verbosity of the Rails logger. In production mode, this defaults to +:info+. In development mode, it defaults to +:debug+. @@ -147,6 +149,7 @@ h4. Configuring Middleware Every Rails application comes with a standard set of middleware which it uses in this order in the development environment: +* +Rack::SSL+ Will force every requests to be under HTTPS protocal. Will be available if +config.force_ssl+ is set to _true_. * +ActionDispatch::Static+ is used to serve static assets. Disabled if +config.serve_static_assets+ is _true_. * +Rack::Lock+ Will wrap the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to _false_, which it is by default. * +ActiveSupport::Cache::Strategy::LocalCache+ Serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile index 182f3631ef..893f65856c 100644 --- a/railties/guides/source/security.textile +++ b/railties/guides/source/security.textile @@ -57,7 +57,11 @@ Many web applications have an authentication system: a user provides a user name Hence, the cookie serves as temporary authentication for the web application. Everyone who seizes a cookie from someone else, may use the web application as this user – with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures: -* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to _(highlight)provide a secure connection over SSL_. +* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to _(highlight)provide a secure connection over SSL_. In Rails 3.1 and later, this could be accomplished by always forcing SSL connection in your application config file: + + +config.force_ssl = true + * Most people don't clear out the cookies after working at a public terminal. So if the last user didn't log out of a web application, you would be able to use it as this user. Provide the user with a _(highlight)log-out button_ in the web application, and _(highlight)make it prominent_. diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 94819820bc..1b834275a7 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -145,15 +145,21 @@ module Rails def default_middleware_stack ActionDispatch::MiddlewareStack.new.tap do |middleware| - rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache + if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache + require "action_dispatch/http/rack_cache" + middleware.use ::Rack::Cache, rack_cache + end - require "action_dispatch/http/rack_cache" if rack_cache - middleware.use ::Rack::Cache, rack_cache if rack_cache + if config.force_ssl + require "rack/ssl" + middleware.use ::Rack::SSL + end if config.serve_static_assets asset_paths = ActiveSupport::OrderedHash[config.static_asset_paths.to_a.reverse] middleware.use ::ActionDispatch::Static, asset_paths end + middleware.use ::Rack::Lock unless config.allow_concurrency middleware.use ::Rack::Runtime middleware.use ::Rails::Rack::Logger @@ -174,7 +180,10 @@ module Rails middleware.use ::ActionDispatch::Head middleware.use ::Rack::ConditionalGet middleware.use ::Rack::ETag, "no-cache" - middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support if config.action_dispatch.best_standards_support + + if config.action_dispatch.best_standards_support + middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support + end end end diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index c74bcbedf2..23b0e765ae 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -9,7 +9,7 @@ module Rails :filter_parameters, :helpers_paths, :logger, :preload_frameworks, :reload_plugins, :secret_token, :serve_static_assets, :session_options, - :time_zone, :whiny_nils + :time_zone, :whiny_nils, :force_ssl attr_writer :log_level @@ -22,6 +22,7 @@ module Rails @helpers_paths = [] @dependency_loading = true @serve_static_assets = true + @force_ssl = false @session_store = :cookie_store @session_options = {} @time_zone = "UTC" diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index 91d3133ea4..bdb897ad33 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -3,7 +3,7 @@ # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development - # since you don't have to restart the webserver when you make code changes. + # since you don't have to restart the web server when you make code changes. config.cache_classes = false # Log error messages when you accidentally call methods on nil. diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 89bb891ddd..874cc403ba 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -1,7 +1,6 @@ <%= app_const %>.configure do # Settings specified here will take precedence over those in config/application.rb - # The production environment is meant for finished, "live" apps. # Code is not reloaded between requests config.cache_classes = true @@ -9,14 +8,15 @@ config.consider_all_requests_local = false config.action_controller.perform_caching = true - # Specifies the header that your server uses for sending files - config.action_dispatch.x_sendfile_header = "X-Sendfile" + # Disable Rails's static asset server (Apache or nginx will already do this) + config.serve_static_assets = false - # For nginx: - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' + # Specifies the header that your server uses for sending files + # (comment out if your front-end server doesn't support this) + config.action_dispatch.x_sendfile_header = "X-Sendfile" # Use 'X-Accel-Redirect' for nginx - # If you have no front-end server that supports something like X-Sendfile, - # just comment this out and Rails will serve the files + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true # See everything in the log (default is :info) # config.log_level = :debug @@ -27,10 +27,6 @@ # Use a different cache store in production # config.cache_store = :mem_cache_store - # Disable Rails's static asset server - # In production, Apache or nginx will already do this - config.serve_static_assets = false - # Enable serving of images, stylesheets, and javascripts from an asset server # config.action_controller.asset_host = "http://assets.example.com" diff --git a/railties/railties.gemspec b/railties/railties.gemspec index c3793d3ac3..c51fe856be 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -21,6 +21,7 @@ Gem::Specification.new do |s| s.add_dependency('rake', '>= 0.8.7') s.add_dependency('thor', '~> 0.14.4') + s.add_dependency('rack-ssl', '~> 1.3.2') s.add_dependency('activesupport', version) s.add_dependency('actionpack', version) end diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index b314832685..0df6f33e8c 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -52,6 +52,12 @@ module ApplicationTests assert_equal "Rack::Cache", middleware.first end + test "Rack::SSL is present with force_ssl is set" do + add_to_config "config.force_ssl = true" + boot! + assert middleware.include?("Rack::SSL") + end + test "removing Active Record omits its middleware" do use_frameworks [] boot! -- cgit v1.2.3 From 84aab7aa53e0ec4430df89807aa8220353b2d0c9 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 27 Mar 2011 22:31:42 +0200 Subject: s/with/when/ --- railties/test/application/middleware_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 0df6f33e8c..01e6c49d9c 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -52,7 +52,7 @@ module ApplicationTests assert_equal "Rack::Cache", middleware.first end - test "Rack::SSL is present with force_ssl is set" do + test "Rack::SSL is present when force_ssl is set" do add_to_config "config.force_ssl = true" boot! assert middleware.include?("Rack::SSL") -- cgit v1.2.3 From 7cbdfa83035aacb0d4dbfa84525b54e9122efb75 Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Mon, 28 Mar 2011 03:05:14 +0800 Subject: Add controller-specific `force_ssl` method to force web browser to use HTTPS protocol This would become useful for site which sometime transferring sensitive information such as account information on particular controller or action. This featured was requested by DHH. --- actionpack/CHANGELOG | 2 + actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/base.rb | 1 + .../lib/action_controller/metal/force_ssl.rb | 35 +++++++++ actionpack/test/controller/force_ssl_test.rb | 83 ++++++++++++++++++++++ .../source/action_controller_overview.textile | 22 ++++++ 6 files changed, 144 insertions(+) create mode 100644 actionpack/lib/action_controller/metal/force_ssl.rb create mode 100644 actionpack/test/controller/force_ssl_test.rb diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index cc19402243..a90a7b37f7 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.1.0 (unreleased)* +* Allow you to add `force_ssl` into controller to force browser to transfer data via HTTPS protocol on that particular controller. You can also specify `:only` or `:except` to specific it to particular action. [DHH and Prem Sichanugrist] + * Allow FormHelper#form_for to specify the :method as a direct option instead of through the :html hash [DHH] form_for(@post, remote: true, method: :delete) instead of form_for(@post, remote: true, html: { method: :delete }) diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 5b81cd39f4..62cc18b253 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -14,6 +14,7 @@ module ActionController autoload :ConditionalGet autoload :Cookies autoload :Flash + autoload :ForceSSL autoload :Head autoload :Helpers autoload :HideActions diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 81c0698fb8..e6523e56d2 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -198,6 +198,7 @@ module ActionController Cookies, Flash, RequestForgeryProtection, + ForceSSL, Streaming, RecordIdentifier, HttpAuthentication::Basic::ControllerMethods, diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb new file mode 100644 index 0000000000..eb8ed7dfbd --- /dev/null +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -0,0 +1,35 @@ +module ActionController + # This module provides a method which will redirects browser to use HTTPS + # protocol. This will ensure that user's sensitive information will be + # transferred safely over the internet. You _should_ always force browser + # to use HTTPS when you're transferring sensitive information such as + # user authentication, account information, or credit card information. + # + # Note that if you really concern about your application safety, you might + # consider using +config.force_ssl+ in your configuration config file instead. + # That will ensure all the data transferred via HTTPS protocol and prevent + # user from getting session hijacked when accessing the site under unsecured + # HTTP protocol. + module ForceSSL + extend ActiveSupport::Concern + include AbstractController::Callbacks + + module ClassMethods + # Force the request to this particular controller or specified actions to be + # under HTTPS protocol. + # + # Note that this method will not be effective on development environment. + # + # ==== Options + # * only - The callback should be run only for this action + # * except - The callback should be run for all actions except this action + def force_ssl(options = {}) + before_filter(options) do + if !request.ssl? && !Rails.env.development? + redirect_to :protocol => 'https://', :status => :moved_permanently + end + end + end + end + end +end \ No newline at end of file diff --git a/actionpack/test/controller/force_ssl_test.rb b/actionpack/test/controller/force_ssl_test.rb new file mode 100644 index 0000000000..3e723e20d9 --- /dev/null +++ b/actionpack/test/controller/force_ssl_test.rb @@ -0,0 +1,83 @@ +require 'abstract_unit' + +class ForceSSLController < ActionController::Base + def banana + render :text => "monkey" + end + + def cheeseburger + render :text => "sikachu" + end +end + +class ForceSSLControllerLevel < ForceSSLController + force_ssl +end + +class ForceSSLOnlyAction < ForceSSLController + force_ssl :only => :cheeseburger +end + +class ForceSSLExceptAction < ForceSSLController + force_ssl :except => :banana +end + +class ForceSSLControllerLevelTest < ActionController::TestCase + tests ForceSSLControllerLevel + + def test_banana_redirects_to_https + get :banana + assert_response 301 + assert_equal "https://test.host/force_ssl_controller_level/banana", redirect_to_url + end + + def test_cheeseburger_redirects_to_https + get :cheeseburger + assert_response 301 + assert_equal "https://test.host/force_ssl_controller_level/cheeseburger", redirect_to_url + end +end + +class ForceSSLOnlyActionTest < ActionController::TestCase + tests ForceSSLOnlyAction + + def test_banana_not_redirects_to_https + get :banana + assert_response 200 + end + + def test_cheeseburger_redirects_to_https + get :cheeseburger + assert_response 301 + assert_equal "https://test.host/force_ssl_only_action/cheeseburger", redirect_to_url + end +end + +class ForceSSLExceptActionTest < ActionController::TestCase + tests ForceSSLExceptAction + + def test_banana_not_redirects_to_https + get :banana + assert_response 200 + end + + def test_cheeseburger_redirects_to_https + get :cheeseburger + assert_response 301 + assert_equal "https://test.host/force_ssl_except_action/cheeseburger", redirect_to_url + end +end + +class ForceSSLExcludeDevelopmentTest < ActionController::TestCase + tests ForceSSLControllerLevel + + def setup + Rails.env.stubs(:development?).returns(false) + end + + def test_development_environment_not_redirects_to_https + Rails.env.stubs(:development?).returns(true) + get :banana + assert_response 200 + end +end \ No newline at end of file diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile index ecb03a48e4..178d98c2d6 100644 --- a/railties/guides/source/action_controller_overview.textile +++ b/railties/guides/source/action_controller_overview.textile @@ -816,6 +816,28 @@ end NOTE: Certain exceptions are only rescuable from the +ApplicationController+ class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's "article":http://m.onkey.org/2008/7/20/rescue-from-dispatching on the subject for more information. +h3. Force HTTPS protocol + +Sometime you might want to force a particular controller to only be accessible via an HTTPS protocol for security reason. Since Rails 3.1 you can now use +force_ssl+ method in your controller to enforce that: + + +class DinnerController + force_ssl +end + + +Just like the filter, you could also passing +:only+ and +:except+ to enforce the secure connection only to specific actions + + +class DinnerController + force_ssl :only => :cheeseburger + # or + force_ssl :except => :cheeseburger +end + + +Please note that if you found yourself adding +force_ssl+ to many controllers, you may found yourself wanting to force the whole application to use HTTPS instead. In that case, you can set the +config.force_ssl+ in your environment file. + h3. Changelog * February 17, 2009: Yet another proofread by Xavier Noria. -- cgit v1.2.3 From 884e39f69eb8012b7ce455747d1b8aa7b34d83d3 Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Mon, 28 Mar 2011 04:31:19 +0800 Subject: Fix documentation typo --- railties/guides/source/configuring.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 04e2d6daed..298335d484 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -149,7 +149,7 @@ h4. Configuring Middleware Every Rails application comes with a standard set of middleware which it uses in this order in the development environment: -* +Rack::SSL+ Will force every requests to be under HTTPS protocal. Will be available if +config.force_ssl+ is set to _true_. +* +Rack::SSL+ Will force every requests to be under HTTPS protocol. Will be available if +config.force_ssl+ is set to _true_. * +ActionDispatch::Static+ is used to serve static assets. Disabled if +config.serve_static_assets+ is _true_. * +Rack::Lock+ Will wrap the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to _false_, which it is by default. * +ActiveSupport::Cache::Strategy::LocalCache+ Serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. -- cgit v1.2.3 From 6194c6d13b1349228baa85bf19b4f502011365a4 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 27 Mar 2011 23:49:11 +0200 Subject: AR validations and callbacks guide: put update_attribute back --- railties/guides/source/active_record_validations_callbacks.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 6c80ec39f2..514d0322b9 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -83,6 +83,7 @@ The following methods skip validations, and will save the object to the database * +increment_counter+ * +toggle!+ * +update_all+ +* +update_attribute+ * +update_column+ * +update_counters+ @@ -964,6 +965,7 @@ The following methods trigger callbacks: * +save(false)+ * +toggle!+ * +update+ +* +update_attribute+ * +update_attributes+ * +update_attributes!+ * +valid?+ -- cgit v1.2.3 From 62dd3458e326b1f2927d43401e7b10004410fdf0 Mon Sep 17 00:00:00 2001 From: mhutchin Date: Sun, 27 Mar 2011 23:50:51 -0700 Subject: Minor typo correction. --- railties/guides/source/configuring.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 298335d484..9ca567129b 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -149,7 +149,7 @@ h4. Configuring Middleware Every Rails application comes with a standard set of middleware which it uses in this order in the development environment: -* +Rack::SSL+ Will force every requests to be under HTTPS protocol. Will be available if +config.force_ssl+ is set to _true_. +* +Rack::SSL+ Will force every request to be under HTTPS protocol. Will be available if +config.force_ssl+ is set to _true_. * +ActionDispatch::Static+ is used to serve static assets. Disabled if +config.serve_static_assets+ is _true_. * +Rack::Lock+ Will wrap the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to _false_, which it is by default. * +ActiveSupport::Cache::Strategy::LocalCache+ Serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread. -- cgit v1.2.3