<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>test/table_lock.rb</filename>
    </added>
    <added>
      <filename>test/unlocked.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,6 +1,6 @@
 == AttrLocked
 
-+AttrLocked+ is a tiny little Rails plugin that provides extra security for your +ActiveRecord+ models. It lets you specify, at the model level, that certain model attributes should never be allowed to change. Perfect for usernames or financial record-keeping.
++AttrLocked+ is a little Rails plugin that provides extra security for your +ActiveRecord+ models. It lets you specify, at the model level, that certain model attributes should never be allowed to change. Perfect for usernames or financial record-keeping.
 
 
 === In the model
@@ -37,6 +37,17 @@ So, you are unable to assign using &lt;tt&gt;username=&lt;/tt&gt; (or indeed using &lt;tt&gt;attri
 To make sure people don't waste their time filling out forms, any form input that's linked to a locked attribute will be disabled automatically, unless you're creating a new record. So, you don't need to go around putting &lt;tt&gt;:disabled =&gt; !object.new_record?&lt;/tt&gt; everywhere. Helpful, no?
 
 
+=== Locking whole tables
+
+You might have some database tables that are read-only. For example, there might be a table that stores a set of third-party geographic data that's used by your application. You can safeguard such tabled against changes by using:
+
+  class Location &lt; ActiveRecord::Base
+    table_locked
+  end
+
+With that in place, you cannot modify the properties of any +Location+ instances, even new ones not yet saved to the database. You cannot save, create, update or destroy (or +delete_all+) any records from the table.
+
+
 === License
 
 Copyright (c) 2007 James Coglan</diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -2,21 +2,22 @@ module ActionView #:nodoc:
   module Helpers #:nodoc:
     class InstanceTag
     
-      def attribute_locked
+      def attribute_locked?
         object.is_a?(ActiveRecord::Base) and
-            object.class.has_locked_attribute?(method_name) and
-            !object.new_record?
+            (object.class.table_read_only? or 
+            (object.class.has_locked_attribute?(method_name) and
+            !object.new_record?))
       end
       
       def tag_with_attribute_locking(name, options = nil)
-        options = (options || {}).update(&quot;disabled&quot; =&gt; attribute_locked)
+        options = (options || {}).update(&quot;disabled&quot; =&gt; attribute_locked?)
         tag_without_attribute_locking(name, options)
       end
       alias_method(:tag_without_attribute_locking, :tag)
       alias_method(:tag, :tag_with_attribute_locking)
       
       def content_tag_with_attribute_locking(name, content_or_options_with_block = nil, options = nil, &amp;block)
-        options = (options || {}).update(&quot;disabled&quot; =&gt; attribute_locked)
+        options = (options || {}).update(&quot;disabled&quot; =&gt; attribute_locked?)
         content_tag_without_attribute_locking(name, content_or_options_with_block, options, &amp;block)
       end
       alias_method(:content_tag_without_attribute_locking, :content_tag)
@@ -25,7 +26,7 @@ module ActionView #:nodoc:
     private
       
       def date_or_time_select_with_attribute_locking(options)
-        options[:disabled] = attribute_locked
+        options[:disabled] = attribute_locked?
         date_or_time_select_without_attribute_locking(options)
       end
       alias_method(:date_or_time_select_without_attribute_locking, :date_or_time_select)</diff>
      <filename>lib/action_view.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,24 +7,36 @@ module ActiveRecord #:nodoc:
       end
       
       def locked_attributes
-        read_inheritable_attribute(&quot;attr_locked&quot;)
+        (table_read_only? ? self.new.attribute_names :
+            read_inheritable_attribute(&quot;attr_locked&quot;)).to_a.collect(&amp;:to_s)
       end
       
       def define_locked_write_methods
-        locked_attributes.to_a.collect(&amp;:to_s).each do |attr|
+        locked_attributes.each do |attr|
           define_method(&quot;#{attr}=&quot;) do |value|
-            write_attribute(attr, value) if new_record?
+            write_attribute(attr, value) if new_record? and !self.class.table_read_only?
           end
         end
       end
       
       def has_locked_attribute?(attr_name)
-        locked_attributes.to_a.collect(&amp;:to_s).include?(attr_name.to_s)
+        locked_attributes.include?(attr_name.to_s)
+      end
+      
+      def table_locked
+        write_inheritable_attribute(&quot;read_only_table&quot;, true)
+        define_locked_write_methods
+        def self.delete_all; return false; end
+      end
+      
+      def table_read_only?
+        read_inheritable_attribute(&quot;read_only_table&quot;) ? true : false
       end
     end
     
     define_method('[]=_with_attribute_locking') do |attr_name, value|
-      if !self.class.has_locked_attribute?(attr_name) or new_record?
+      if !self.class.table_read_only? and
+          (!self.class.has_locked_attribute?(attr_name) or new_record?)
         write_attribute(attr_name, value)
       end
     end
@@ -40,5 +52,12 @@ module ActiveRecord #:nodoc:
     end
     alias_method(:update_attribute_without_attribute_locking, :update_attribute)
     alias_method(:update_attribute, :update_attribute_with_attribute_locking)
+    
+    before_save :check_table_lock
+    before_destroy :check_table_lock
+    def check_table_lock
+      return false if self.class.table_read_only?
+    end
+    
   end
 end</diff>
      <filename>lib/active_record.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,8 @@ require 'test/unit'
 require File.dirname(__FILE__) + '/../../../../config/environment'
 require File.dirname(__FILE__) + '/../init'
 require File.dirname(__FILE__) + '/lock'
+require File.dirname(__FILE__) + '/table_lock'
+require File.dirname(__FILE__) + '/unlocked'
 
 class AttrLockedTest &lt; Test::Unit::TestCase
   
@@ -17,6 +19,8 @@ class AttrLockedTest &lt; Test::Unit::TestCase
     Lock.delete_all
     Lock.create(AttrLockedTest::Data)
     @lock = Lock.find(:first)
+    @table_lock = TableLock.find(:first)
+    @unlocked = Unlocked.new
   end
   
   def check_values
@@ -91,8 +95,66 @@ class AttrLockedTest &lt; Test::Unit::TestCase
   end
   
   def test_update_attribute
-    assert_equal false, @lock.update_attribute(:name, &quot;Mike&quot;)
+    assert !@lock.update_attribute(:name, &quot;Mike&quot;)
     @lock.reload
     check_values
   end
+  
+  def test_assignment_on_new_locked_table_record
+    @table_lock = TableLock.new
+    @table_lock.unlocked = &quot;Mike&quot;
+    assert_equal nil, @table_lock.unlocked
+    @table_lock[:unlocked] = &quot;Mike&quot;
+    assert_equal nil, @table_lock.unlocked
+    @table_lock.attributes = {:unlocked =&gt; &quot;Mike&quot;}
+    assert_equal nil, @table_lock.unlocked
+  end
+  
+  def test_saving_on_locked_table
+    @table_lock = TableLock.new(:name =&gt; &quot;locked up&quot;)
+    assert @table_lock.valid?
+    assert !@table_lock.save
+    @found_table_lock = TableLock.find_by_name(&quot;locked up&quot;)
+    assert_equal nil, @found_table_lock
+    assert_equal 1, TableLock.count
+  end
+  
+  def test_creation_on_locked_table
+    TableLock.create(:name =&gt; &quot;locked up&quot;)
+    @found_table_lock = TableLock.find_by_name(&quot;locked up&quot;)
+    assert_equal nil, @found_table_lock
+    assert_equal 1, TableLock.count
+  end
+  
+  def test_updating_on_locked_table
+    assert_equal AttrLockedTest::Data[:name], @table_lock.name
+    assert !@table_lock.update_attribute(:unlocked, &quot;anything&quot;)
+    assert !@table_lock.update_attributes(:description =&gt; &quot;a locked table&quot;)
+    @found_table_lock = TableLock.find_by_unlocked(&quot;anything&quot;)
+    assert_equal nil, @found_table_lock
+    @found_table_lock = TableLock.find_by_description(&quot;a locked table&quot;)
+    assert_equal nil, @found_table_lock
+  end
+  
+  def test_destruction_on_locked_table
+    assert !@table_lock.destroy
+    assert_equal 1, TableLock.count
+    TableLock.destroy_all
+    assert_equal 1, TableLock.count
+    assert !TableLock.delete_all
+    assert_equal 1, TableLock.count
+  end
+  
+  def test_operations_on_unlocked_model
+    Unlocked.delete_all
+    @unlocked.attributes = AttrLockedTest::Data
+    assert @unlocked.save
+    @unlocked = Unlocked.find(:first)
+    @unlocked.update_attributes(:name =&gt; &quot;Joe&quot;, :date =&gt; &quot;2012-06-21&quot;)
+    @unlocked.reload
+    assert_equal &quot;Joe&quot;, @unlocked.name
+    assert_equal Date.new(2012, 6, 21), @unlocked.date
+    @unlocked.destroy
+    assert_equal 0, Unlocked.count
+  end
 end</diff>
      <filename>test/attr_locked_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,5 @@
+&lt;h1&gt;Lock instance&lt;/h1&gt;
+
 &lt;% form_for(:lock) do |form| %&gt;
   &lt;div&gt;&lt;%= form.text_field(:name) %&gt;&lt;/div&gt;
   &lt;div&gt;&lt;%= form.text_area(:description, :rows =&gt; 6) %&gt;&lt;/div&gt;
@@ -10,3 +12,35 @@
   &lt;div&gt;&lt;%= form.select(:name, [[1,1], [2,2], [3,3]]) %&gt;&lt;/div&gt;
   &lt;div&gt;&lt;%= form.country_select(:description) %&gt;
 &lt;% end %&gt;
+
+
+&lt;h1&gt;TableLock instance&lt;/h1&gt;
+
+&lt;% form_for(:table_lock) do |form| %&gt;
+  &lt;div&gt;&lt;%= form.text_field(:name) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.text_area(:description, :rows =&gt; 6) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.date_select(:date) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.check_box(:flag) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.radio_button(:flag, 'foo') %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.radio_button(:flag, 'bar') %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.text_field(:unlocked) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.collection_select(:name, Lock.find(:all), :name, :date) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.select(:name, [[1,1], [2,2], [3,3]]) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.country_select(:description) %&gt;
+&lt;% end %&gt;
+
+
+&lt;h1&gt;Unlocked instance&lt;/h1&gt;
+
+&lt;% form_for(:unlocked) do |form| %&gt;
+  &lt;div&gt;&lt;%= form.text_field(:name) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.text_area(:description, :rows =&gt; 6) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.date_select(:date) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.check_box(:flag) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.radio_button(:flag, 'foo') %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.radio_button(:flag, 'bar') %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.text_field(:unlocked) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.collection_select(:name, Lock.find(:all), :name, :date) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.select(:name, [[1,1], [2,2], [3,3]]) %&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;%= form.country_select(:description) %&gt;
+&lt;% end %&gt;</diff>
      <filename>test/index.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,9 +1,13 @@
 require RAILS_ROOT + '/vendor/plugins/attr_locked/test/lock'
+require RAILS_ROOT + '/vendor/plugins/attr_locked/test/table_lock'
+require RAILS_ROOT + '/vendor/plugins/attr_locked/test/unlocked'
 
 class LockController &lt; ApplicationController
   def index
     Lock.delete_all
     Lock.create(:name =&gt; &quot;Bob&quot;, :description =&gt; &quot;The testing guy&quot;, :date =&gt; &quot;2007-06-01&quot;, :flag =&gt; true, :unlocked =&gt; &quot;blah&quot;)
     @lock = Lock.find(:first)
+    @table_lock = TableLock.new
+    @unlocked = Unlocked.find(:first)
   end
 end</diff>
      <filename>test/lock_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,11 +3,11 @@ require RAILS_ROOT + '/config/environment'
 class AttrLockedMigration &lt; ActiveRecord::Migration
   def self.up
     create_table :attr_locked_test do |t|
-      t.column :name, :string
-      t.column :description, :text
-      t.column :date, :date
-      t.column :flag, :boolean
-      t.column :unlocked, :string
+      t.column :name, :string, :null =&gt; true
+      t.column :description, :text, :null =&gt; true
+      t.column :date, :date, :null =&gt; true
+      t.column :flag, :boolean, :null =&gt; true
+      t.column :unlocked, :string, :null =&gt; true
     end
   end
   </diff>
      <filename>test/migration.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>3dbe244e918e1e54c01351724fb373d86dfeff46</id>
    </parent>
  </parents>
  <author>
    <name>James Coglan</name>
    <email>jcoglan@googlemail.com</email>
  </author>
  <url>http://github.com/jcoglan/attr_locked/commit/cb359dabde882f32e761475d7413974d5c4b2c8c</url>
  <id>cb359dabde882f32e761475d7413974d5c4b2c8c</id>
  <committed-date>2007-06-11T05:34:49-07:00</committed-date>
  <authored-date>2007-06-11T05:34:49-07:00</authored-date>
  <message>Added ability to lock tables from saving, updating and deleting records. You cannot modify the attributes on any instance of a locked table model.</message>
  <tree>38b7c41bb54192f4ba0950a3b954cb4cab6e6bf2</tree>
  <committer>
    <name>James Coglan</name>
    <email>jcoglan@googlemail.com</email>
  </committer>
</commit>
