mirror of
https://github.com/github/rails.git
synced 2026-01-29 08:18:03 -05:00
Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [dcmanges]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7693 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
@@ -1,5 +1,16 @@
|
||||
*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.14.2 - 1.15.3]
|
||||
|
||||
* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [dcmanges]
|
||||
|
||||
class Comment < ActiveRecord::Base
|
||||
# Automatically sets Article#comments_count as readonly.
|
||||
belongs_to :article, :counter_cache => :comments_count
|
||||
end
|
||||
|
||||
class Article < ActiveRecord::Base
|
||||
attr_readonly :approved_comments_count
|
||||
end
|
||||
|
||||
* Make size for has_many :through use counter cache if it exists. Closes #9734 [xaviershay]
|
||||
|
||||
* Remove DB2 adapter since IBM chooses to maintain their own adapter instead. [Jeremy Kemper]
|
||||
|
||||
@@ -841,7 +841,11 @@ module ActiveRecord
|
||||
module_eval(
|
||||
"before_destroy '#{reflection.name}.class.decrement_counter(\"#{cache_column}\", #{reflection.primary_key_name})" +
|
||||
" unless #{reflection.name}.nil?'"
|
||||
)
|
||||
)
|
||||
|
||||
module_eval(
|
||||
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name})"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -636,6 +636,15 @@ module ActiveRecord #:nodoc:
|
||||
read_inheritable_attribute("attr_accessible")
|
||||
end
|
||||
|
||||
# Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.
|
||||
def attr_readonly(*attributes)
|
||||
write_inheritable_array("attr_readonly", attributes - (readonly_attributes || []))
|
||||
end
|
||||
|
||||
# Returns an array of all the attributes that have been specified as readonly.
|
||||
def readonly_attributes
|
||||
read_inheritable_attribute("attr_readonly")
|
||||
end
|
||||
|
||||
# If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
|
||||
# then specify the name of that attribute using this method and it will be handled automatically.
|
||||
@@ -1953,7 +1962,7 @@ module ActiveRecord #:nodoc:
|
||||
def update
|
||||
connection.update(
|
||||
"UPDATE #{self.class.table_name} " +
|
||||
"SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " +
|
||||
"SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false, false))} " +
|
||||
"WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}",
|
||||
"#{self.class.name} Update"
|
||||
)
|
||||
@@ -2008,6 +2017,15 @@ module ActiveRecord #:nodoc:
|
||||
raise "Declare either attr_protected or attr_accessible for #{self.class}, but not both."
|
||||
end
|
||||
end
|
||||
|
||||
# Removes attributes which have been marked as readonly.
|
||||
def remove_readonly_attributes(attributes)
|
||||
unless self.class.readonly_attributes.nil?
|
||||
attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,"").intern) }
|
||||
else
|
||||
attributes
|
||||
end
|
||||
end
|
||||
|
||||
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
|
||||
def attributes_protected_by_default
|
||||
@@ -2018,13 +2036,14 @@ module ActiveRecord #:nodoc:
|
||||
|
||||
# Returns copy of the attributes hash where all the values have been safely quoted for use in
|
||||
# an SQL statement.
|
||||
def attributes_with_quotes(include_primary_key = true)
|
||||
attributes.inject({}) do |quoted, (name, value)|
|
||||
def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true)
|
||||
quoted = attributes.inject({}) do |quoted, (name, value)|
|
||||
if column = column_for_attribute(name)
|
||||
quoted[name] = quote_value(value, column) unless !include_primary_key && column.primary
|
||||
end
|
||||
quoted
|
||||
end
|
||||
include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
|
||||
end
|
||||
|
||||
# Quote strings appropriately for SQL statements.
|
||||
|
||||
@@ -1175,6 +1175,24 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
|
||||
topic.update_attributes(:title => "37signals")
|
||||
assert_equal 1, Topic.find(topic.id)[:replies_count]
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_after_save
|
||||
topic = Topic.create("title" => "monday night")
|
||||
topic.replies.create("title" => "re: monday night", "content" => "football")
|
||||
assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
|
||||
|
||||
topic.save
|
||||
assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_after_update_attributes
|
||||
topic = Topic.create("title" => "37s")
|
||||
topic.replies.create("title" => "re: 37s", "content" => "rails")
|
||||
assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
|
||||
|
||||
topic.update_attributes("title" => "37signals")
|
||||
assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
|
||||
end
|
||||
|
||||
def test_assignment_before_parent_saved
|
||||
client = Client.find(:first)
|
||||
|
||||
@@ -47,6 +47,10 @@ class TightDescendant < TightPerson
|
||||
attr_accessible :phone_number
|
||||
end
|
||||
|
||||
class ReadonlyTitlePost < Post
|
||||
attr_readonly :title
|
||||
end
|
||||
|
||||
class Booleantest < ActiveRecord::Base; end
|
||||
|
||||
class Task < ActiveRecord::Base
|
||||
@@ -840,6 +844,19 @@ class BasicsTest < Test::Unit::TestCase
|
||||
assert_nil TightDescendant.protected_attributes
|
||||
assert_equal [ :name, :address, :phone_number ], TightDescendant.accessible_attributes
|
||||
end
|
||||
|
||||
def test_readonly_attributes
|
||||
assert_equal [ :title ], ReadonlyTitlePost.readonly_attributes
|
||||
|
||||
post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
|
||||
post.reload
|
||||
assert_equal "cannot change this", post.title
|
||||
|
||||
post.update_attributes(:title => "try to change", :body => "changed")
|
||||
post.reload
|
||||
assert_equal "cannot change this", post.title
|
||||
assert_equal "changed", post.body
|
||||
end
|
||||
|
||||
def test_multiparameter_attributes_on_date
|
||||
attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
|
||||
@@ -1222,12 +1239,12 @@ class BasicsTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_increment_attribute
|
||||
assert_equal 1, topics(:first).replies_count
|
||||
topics(:first).increment! :replies_count
|
||||
assert_equal 2, topics(:first, :reload).replies_count
|
||||
|
||||
topics(:first).increment(:replies_count).increment!(:replies_count)
|
||||
assert_equal 4, topics(:first, :reload).replies_count
|
||||
assert_equal 50, accounts(:signals37).credit_limit
|
||||
accounts(:signals37).increment! :credit_limit
|
||||
assert_equal 51, accounts(:signals37, :reload).credit_limit
|
||||
|
||||
accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
|
||||
assert_equal 53, accounts(:signals37, :reload).credit_limit
|
||||
end
|
||||
|
||||
def test_increment_nil_attribute
|
||||
@@ -1237,14 +1254,13 @@ class BasicsTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_decrement_attribute
|
||||
topics(:first).increment(:replies_count).increment!(:replies_count)
|
||||
assert_equal 3, topics(:first).replies_count
|
||||
|
||||
topics(:first).decrement!(:replies_count)
|
||||
assert_equal 2, topics(:first, :reload).replies_count
|
||||
assert_equal 50, accounts(:signals37).credit_limit
|
||||
|
||||
topics(:first).decrement(:replies_count).decrement!(:replies_count)
|
||||
assert_equal 0, topics(:first, :reload).replies_count
|
||||
accounts(:signals37).decrement!(:credit_limit)
|
||||
assert_equal 49, accounts(:signals37, :reload).credit_limit
|
||||
|
||||
accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
|
||||
assert_equal 47, accounts(:signals37, :reload).credit_limit
|
||||
end
|
||||
|
||||
def test_toggle_attribute
|
||||
|
||||
Reference in New Issue
Block a user