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

Changeset 8864

Show
Ignore:
Timestamp:
02/13/08 06:32:50 (3 months ago)
Author:
bitsweat
Message:

Introduce the :readonly option to all associations. Records from the association cannot be saved. Closes #11084.

Files:

Legend:

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

    r8855 r8864  
    11*SVN* 
     2 
     3* Introduce the :readonly option to all associations. Records from the association cannot be saved.  #11084 [miloops] 
    24 
    35* Multiparameter attributes for time columns fail over to DateTime when out of range of Time [Geoff Buesing] 
  • trunk/activerecord/lib/active_record/associations.rb

    r8856 r8864  
    670670      #   association is a polymorphic +belongs_to+. 
    671671      # * <tt>:uniq</tt> - if set to +true+, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>. 
     672      # * <tt>:readonly</tt> - if set to +true+, all the associated objects are readonly through the association. 
    672673      # 
    673674      # Option examples: 
     
    678679      #   has_many :comments, :dependent => :nullify 
    679680      #   has_many :tags, :as => :taggable 
     681      #   has_many :reports, :readonly => true 
    680682      #   has_many :subscribers, :through => :subscriptions, :source => :user 
    681683      #   has_many :subscribers, :class_name => "Person", :finder_sql => 
     
    736738      # * <tt>:include</tt>  - specify second-order associations that should be eager loaded when this object is loaded. 
    737739      # * <tt>:as</tt>: Specifies a polymorphic interface (See <tt>#belongs_to</tt>). 
    738             # 
     740      # * <tt>:readonly</tt> - if set to +true+, the associated object is readonly through the association. 
     741      # 
    739742      # Option examples: 
    740743      #   has_one :credit_card, :dependent => :destroy  # destroys the associated credit card 
     
    743746      #   has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'" 
    744747      #   has_one :attachment, :as => :attachable 
     748      #   has_one :boss, :readonly => :true 
    745749      def has_one(association_id, options = {}) 
    746750        reflection = create_has_one_reflection(association_id, options) 
     
    812816      #   Note: If you've enabled the counter cache, then you may want to add the counter cache attribute 
    813817      #   to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end). 
     818      # * <tt>:readonly</tt> - if set to +true+, the associated object is readonly through the association. 
    814819      # 
    815820      # Option examples: 
     
    819824      #              :conditions => 'discounts > #{payments_count}' 
    820825      #   belongs_to :attachable, :polymorphic => true 
     826      #   belongs_to :project, :readonly => true 
    821827      def belongs_to(association_id, options = {}) 
    822828        reflection = create_belongs_to_reflection(association_id, options) 
     
    971977      # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join 
    972978      #   but not include the joined columns. 
     979      # * <tt>:readonly</tt> - if set to +true+, all the associated objects are readonly through the association. 
    973980      # 
    974981      # Option examples: 
     
    977984      #   has_and_belongs_to_many :nations, :class_name => "Country" 
    978985      #   has_and_belongs_to_many :categories, :join_table => "prods_cats" 
     986      #   has_and_belongs_to_many :categories, :readonly => true 
    979987      #   has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql => 
    980988      #   'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}' 
     
    12351243            :finder_sql, :counter_sql, 
    12361244            :before_add, :after_add, :before_remove, :after_remove, 
    1237             :extend 
     1245            :extend, :readonly 
    12381246          ) 
    12391247 
     
    12451253        def create_has_one_reflection(association_id, options) 
    12461254          options.assert_valid_keys( 
    1247             :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as 
     1255            :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly 
    12481256          ) 
    12491257 
     
    12541262          options.assert_valid_keys( 
    12551263            :class_name, :foreign_key, :foreign_type, :remote, :conditions, :order, :include, :dependent, 
    1256             :counter_cache, :extend, :polymorphic 
     1264            :counter_cache, :extend, :polymorphic, :readonly 
    12571265          ) 
    12581266 
     
    12731281            :finder_sql, :delete_sql, :insert_sql, 
    12741282            :before_add, :after_add, :before_remove, :after_remove, 
    1275             :extend 
     1283            :extend, :readonly 
    12761284          ) 
    12771285 
  • trunk/activerecord/lib/active_record/associations/association_proxy.rb

    r8737 r8864  
    115115            :joins   => @reflection.options[:joins], 
    116116            :include => @reflection.options[:include], 
    117             :select  => @reflection.options[:select] 
     117            :select  => @reflection.options[:select], 
     118            :readonly  => @reflection.options[:readonly] 
    118119          ) 
    119120        end 
  • trunk/activerecord/lib/active_record/associations/belongs_to_association.rb

    r8735 r8864  
    4545            @owner[@reflection.primary_key_name],  
    4646            :conditions => conditions, 
    47             :include    => @reflection.options[:include] 
     47            :include    => @reflection.options[:include], 
     48            :readonly => @reflection.options[:readonly] 
    4849          ) 
    4950        end 
  • trunk/activerecord/lib/active_record/associations/has_one_association.rb

    r8571 r8864  
    5454            :conditions => @finder_sql,  
    5555            :order      => @reflection.options[:order],  
    56             :include    => @reflection.options[:include] 
     56            :include    => @reflection.options[:include], 
     57            :readonly    => @reflection.options[:readonly] 
    5758          ) 
    5859        end 
  • trunk/activerecord/test/cases/associations_test.rb

    r8776 r8864  
    470470  end 
    471471 
     472  def test_cant_save_readonly_association 
     473    assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_firm).readonly_account.save!  } 
     474    assert companies(:first_firm).readonly_account.readonly? 
     475  end 
     476 
    472477end 
    473478 
     
    543548    assert_equal 2, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'", :limit => 9_000).length 
    544549    assert_equal 2, companies(:first_firm).limited_clients.find_all_by_type('Client', :limit => 9_000).length 
     550  end 
     551 
     552  def test_dynamic_find_all_should_respect_readonly_access 
     553    companies(:first_firm).readonly_clients.find(:all).each { |c| assert_raise(ActiveRecord::ReadOnlyRecord) { c.save!  } } 
     554    companies(:first_firm).readonly_clients.find(:all).each { |c| assert c.readonly? } 
    545555  end 
    546556 
     
    15801590    # the author id of the post should be the id we set 
    15811591    assert_equal post.author_id, author2.id 
     1592  end 
     1593 
     1594  def test_cant_save_readonly_association 
     1595    assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_client).readonly_firm.save! } 
     1596    assert companies(:first_client).readonly_firm.readonly? 
    15821597  end 
    15831598 
     
    19882003  end 
    19892004 
     2005  def test_dynamic_find_all_should_respect_readonly_access 
     2006    projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save!  } if d.valid?} 
     2007    projects(:active_record).readonly_developers.each { |d| d.readonly? } 
     2008  end 
     2009 
    19902010  def test_new_with_values_in_collection 
    19912011    jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis') 
  • trunk/activerecord/test/cases/reflection_test.rb

    r8681 r8864  
    160160 
    161161  def test_reflection_of_all_associations 
    162     assert_equal 17, Firm.reflect_on_all_associations.size 
    163     assert_equal 15, Firm.reflect_on_all_associations(:has_many).size 
    164     assert_equal 2, Firm.reflect_on_all_associations(:has_one).size 
     162    assert_equal 19, Firm.reflect_on_all_associations.size 
     163    assert_equal 16, Firm.reflect_on_all_associations(:has_many).size 
     164    assert_equal 3, Firm.reflect_on_all_associations(:has_one).size 
    165165    assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size 
    166166  end 
  • trunk/activerecord/test/models/company.rb

    r8657 r8864  
    4141  has_many :clients_using_finder_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE 1=1' 
    4242  has_many :plain_clients, :class_name => 'Client' 
     43  has_many :readonly_clients, :class_name => 'Client', :readonly => true 
    4344 
    4445  has_one :account, :foreign_key => "firm_id", :dependent => :destroy 
     46  has_one :readonly_account, :foreign_key => "firm_id", :class_name => "Account", :readonly => true 
    4547end 
    4648 
     
    6163  belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of" 
    6264  belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => ["1 = ?", 1] 
     65  belongs_to :readonly_firm, :class_name => "Firm", :foreign_key => "firm_id", :readonly => true 
    6366 
    6467  # Record destruction so we can test whether firm.clients.clear has 
  • trunk/activerecord/test/models/project.rb

    r8657 r8864  
    11class Project < ActiveRecord::Base 
    22  has_and_belongs_to_many :developers, :uniq => true, :order => 'developers.name desc, developers.id desc' 
     3  has_and_belongs_to_many :readonly_developers, :class_name => "Developer", :readonly => true 
    34  has_and_belongs_to_many :selected_developers, :class_name => "Developer", :select => "developers.*", :uniq => true 
    45  has_and_belongs_to_many :non_unique_developers, :order => 'developers.name desc, developers.id desc', :class_name => 'Developer'