Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 7693

Show
Ignore:
Timestamp:
09/30/07 07:09:44 (9 months ago)
Author:
rick
Message:

Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [dcmanges]

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activerecord/CHANGELOG

    r7692 r7693  
    11*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.14.2 - 1.15.3] 
     2 
     3* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [dcmanges] 
     4 
     5  class Comment < ActiveRecord::Base 
     6    # Automatically sets Article#comments_count as readonly. 
     7    belongs_to :article, :counter_cache => :comments_count 
     8  end 
     9 
     10  class Article < ActiveRecord::Base 
     11    attr_readonly :approved_comments_count 
     12  end 
    213 
    314* Make size for has_many :through use counter cache if it exists.  Closes #9734 [xaviershay] 
  • trunk/activerecord/lib/active_record/associations.rb

    r7666 r7693  
    842842            "before_destroy '#{reflection.name}.class.decrement_counter(\"#{cache_column}\", #{reflection.primary_key_name})" + 
    843843            " unless #{reflection.name}.nil?'" 
    844           )           
     844          ) 
     845           
     846          module_eval( 
     847            "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name})" 
     848          ) 
    845849        end 
    846850      end 
  • trunk/activerecord/lib/active_record/base.rb

    r7666 r7693  
    637637      end 
    638638 
     639       # Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards. 
     640       def attr_readonly(*attributes) 
     641         write_inheritable_array("attr_readonly", attributes - (readonly_attributes || [])) 
     642       end 
     643 
     644       # Returns an array of all the attributes that have been specified as readonly. 
     645       def readonly_attributes 
     646         read_inheritable_attribute("attr_readonly") 
     647       end 
    639648 
    640649      # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,  
     
    19541963        connection.update( 
    19551964          "UPDATE #{self.class.table_name} " + 
    1956           "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " + 
     1965          "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false, false))} " + 
    19571966          "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}", 
    19581967          "#{self.class.name} Update" 
     
    20092018        end 
    20102019      end 
     2020       
     2021      # Removes attributes which have been marked as readonly. 
     2022      def remove_readonly_attributes(attributes) 
     2023        unless self.class.readonly_attributes.nil? 
     2024          attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,"").intern) } 
     2025        else 
     2026          attributes 
     2027        end 
     2028      end 
    20112029 
    20122030      # The primary key and inheritance column can never be set by mass-assignment for security reasons. 
     
    20192037      # Returns copy of the attributes hash where all the values have been safely quoted for use in 
    20202038      # an SQL statement. 
    2021       def attributes_with_quotes(include_primary_key = true
    2022         attributes.inject({}) do |quoted, (name, value)| 
     2039      def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true
     2040        quoted = attributes.inject({}) do |quoted, (name, value)| 
    20232041          if column = column_for_attribute(name) 
    20242042            quoted[name] = quote_value(value, column) unless !include_primary_key && column.primary 
     
    20262044          quoted 
    20272045        end 
     2046        include_readonly_attributes ? quoted : remove_readonly_attributes(quoted) 
    20282047      end 
    20292048 
  • trunk/activerecord/test/associations_test.rb

    r7675 r7693  
    11761176    assert_equal 1, Topic.find(topic.id)[:replies_count] 
    11771177  end 
     1178   
     1179  def test_belongs_to_counter_after_save 
     1180    topic = Topic.create("title" => "monday night") 
     1181    topic.replies.create("title" => "re: monday night", "content" => "football") 
     1182    assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count") 
     1183 
     1184    topic.save 
     1185    assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count") 
     1186  end 
     1187 
     1188  def test_belongs_to_counter_after_update_attributes 
     1189    topic = Topic.create("title" => "37s") 
     1190    topic.replies.create("title" => "re: 37s", "content" => "rails") 
     1191    assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count") 
     1192 
     1193    topic.update_attributes("title" => "37signals") 
     1194    assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count") 
     1195  end 
    11781196 
    11791197  def test_assignment_before_parent_saved 
  • trunk/activerecord/test/base_test.rb

    r7666 r7693  
    4646class TightDescendant < TightPerson 
    4747  attr_accessible :phone_number 
     48end 
     49 
     50class ReadonlyTitlePost < Post 
     51  attr_readonly :title 
    4852end 
    4953 
     
    841845    assert_equal [ :name, :address, :phone_number  ], TightDescendant.accessible_attributes 
    842846  end 
     847   
     848  def test_readonly_attributes 
     849    assert_equal [ :title ], ReadonlyTitlePost.readonly_attributes 
     850     
     851    post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable") 
     852    post.reload 
     853    assert_equal "cannot change this", post.title 
     854     
     855    post.update_attributes(:title => "try to change", :body => "changed") 
     856    post.reload 
     857    assert_equal "cannot change this", post.title 
     858    assert_equal "changed", post.body 
     859  end 
    843860 
    844861  def test_multiparameter_attributes_on_date 
     
    12231240 
    12241241  def test_increment_attribute 
    1225     assert_equal 1, topics(:first).replies_coun
    1226     topics(:first).increment! :replies_coun
    1227     assert_equal 2, topics(:first, :reload).replies_count 
    1228      
    1229     topics(:first).increment(:replies_count).increment!(:replies_count) 
    1230     assert_equal 4, topics(:first, :reload).replies_coun
     1242    assert_equal 50, accounts(:signals37).credit_limi
     1243    accounts(:signals37).increment! :credit_limi
     1244    assert_equal 51, accounts(:signals37, :reload).credit_limit     
     1245 
     1246    accounts(:signals37).increment(:credit_limit).increment!(:credit_limit) 
     1247    assert_equal 53, accounts(:signals37, :reload).credit_limi
    12311248  end 
    12321249   
     
    12381255   
    12391256  def test_decrement_attribute 
    1240     topics(:first).increment(:replies_count).increment!(:replies_count) 
    1241     assert_equal 3, topics(:first).replies_count 
    1242      
    1243     topics(:first).decrement!(:replies_count) 
    1244     assert_equal 2, topics(:first, :reload).replies_count 
    1245  
    1246     topics(:first).decrement(:replies_count).decrement!(:replies_count) 
    1247     assert_equal 0, topics(:first, :reload).replies_count 
     1257    assert_equal 50, accounts(:signals37).credit_limit 
     1258 
     1259    accounts(:signals37).decrement!(:credit_limit) 
     1260    assert_equal 49, accounts(:signals37, :reload).credit_limit 
     1261   
     1262    accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit) 
     1263    assert_equal 47, accounts(:signals37, :reload).credit_limit 
    12481264  end 
    12491265