Hi, Just wondering when one would use "include" over "extend"? Both seem to bring in methods to the class no? The context is I''m just trying to understand why both are used with the acts_as_audited plugin: Full extract from plugin (used within here): =================================================# Copyright (c) 2006 Brandon Keepers # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. module CollectiveIdea #:nodoc: module Acts #:nodoc: # Specify this act if you want changes to your model to be saved in an # audit table. This assumes there is an audits table ready. # # class User < ActiveRecord::Base # acts_as_audited # end # # See <tt>CollectiveIdea::Acts::Audited::ClassMethods#acts_as_audited</tt> # for configuration options module Audited CALLBACKS = [:clear_changed_attributes, :audit_create, :audit_update, :audit_destroy] def self.included(base) # :nodoc: base.extend ClassMethods end module ClassMethods # == Configuration options # # * <tt>except</tt> - Excludes fields from being saved in the audit log. # By default, acts_as_audited will audit all but these fields: # # [self.primary_key, inheritance_column, ''lock_version'', ''created_at'', ''updated_at''] # # You can add to those by passing one or an array of fields to skip. # # class User < ActiveRecord::Base # acts_as_audited :except => :password # end # # * <tt>user_class_name</tt> - specifiy the class name of the user class. # This defaults to "User". Set to false to disable user auditing. # # * <tt>user_method</tt> - specify the method to call on <tt>:user_class_name</tt> # that returns the user that is performing the action. This defaults to # <tt>:current_user</tt>. # # == Database Schema # def acts_as_audited(options = {}) # don''t allow multiple calls return if self.included_modules.include?(CollectiveIdea::Acts::Audited::InstanceMethods) include CollectiveIdea::Acts::Audited::InstanceMethods class_eval do extend CollectiveIdea::Acts::Audited::SingletonMethods cattr_accessor :non_audited_columns, :audited_user_class_name, :audited_user_method self.non_audited_columns = [self.primary_key, inheritance_column, ''lock_version'', ''created_at'', ''updated_at''] self.non_audited_columns |= options[:except].is_a?(Array) ? options[:except].collect{|column| column.to_s} : [options[:except].to_s] if options[:except] self.audited_user_class_name options[:user_class_name].nil? ? "User" : options[:user_class_name] self.audited_user_method = options[:user_method] || :current_user has_many :audits, :as => :auditable after_create :audit_create after_update :audit_update before_destroy :audit_destroy after_save :clear_changed_attributes end end end module InstanceMethods # Temporarily turns off auditing while saving. def save_without_auditing without_auditing do save end end # Returns an array of attribute keys that are audited. See non_audited_columns def audited_attributes self.attributes.keys.select { |k| !self.class.non_audited_columns.include?(k) } end # If called with no parameters, gets whether the current model has changed. # If called with a single parameter, gets whether the parameter has changed. def changed?(attr_name = nil) attr_name.nil? ? (@changed_attributes && @changed_attributes.length > 0) : (@changed_attributes && @changed_attributes.include?(attr_name.to_s)) end # Executes the block with the auditing callbacks disabled. # # @foo.without_auditing do # @foo.save # end # def without_auditing(&block) self.class.without_auditing(&block) end private # Creates a new record in the audits table if applicable def audit_create logger.debug "ACTS AS: audit_create" write_audit(:create) end def audit_update write_audit(:update) if changed? end def audit_destroy write_audit(:destroy) end def write_audit(action = :update) logger.debug "ACTS AS: write_audit" logger.debug "ACTS AS: self.audited_user_class_name #{self.audited_user_class_name}" #logger.debug "ACTS AS: Object.const_get(audited_user_class_name) #{Object.const_get(audited_user_class_name)}" logger.debug "ACTS AS: self.audited_user_method #{self.audited_user_method}" user = self.audited_user_class_name ? Object.const_get(audited_user_class_name).send(self.audited_user_method) : nil logger.debug "ACTS AS: user = #{user}" audits.create(:changes => @changed_attributes, :action => action.to_s, :user_id => user ? user.id : nil) end # clears current changed attributes. Called after save. def clear_changed_attributes @changed_attributes = {} end # overload write_attribute to save changes to audited attributes def write_attribute(attr_name, attr_value) attr_name = attr_name.to_s if audited_attributes.include?(attr_name) @changed_attributes ||= {} # get original value old_value = @changed_attributes[attr_name] ? @changed_attributes[attr_name].first : self[attr_name] super(attr_name, attr_value) new_value = self[attr_name] @changed_attributes[attr_name] = [old_value, new_value] if new_value != old_value else super(attr_name, attr_value) end end CALLBACKS.each do |attr_name| alias_method "orig_#{attr_name}".to_sym, attr_name end def empty_callback() end #:nodoc: end # InstanceMethods module SingletonMethods # Returns an array of columns that are audited. See non_audited_columns def audited_columns self.columns.select { |c| !non_audited_columns.include?(c.name) } end # Executes the block with the auditing callbacks disabled. # # Foo.without_auditing do # @foo.save # end # def without_auditing(&block) class_eval do CALLBACKS.each do |attr_name| alias_method attr_name, :empty_callback end end result = block.call class_eval do CALLBACKS.each do |attr_name| alias_method attr_name, "orig_#{attr_name}".to_sym end end result end end end end end ActiveRecord::Base.send :include, CollectiveIdea::Acts::Audited ================================================= -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk -~----------~----~----~----~------~----~------~--~---
dblack-TKXtfPMJ4Ozk1uMJSBkQmQ@public.gmane.org
2006-Sep-04 21:16 UTC
Re: "include" versus "extend" - what''s the difference
Hi -- On Mon, 4 Sep 2006, Greg Hauptmann wrote:> > Hi, > > Just wondering when one would use "include" over "extend"? Both seem to > bring in methods to the class no?Neither of them actually brings methods into a class. They both have the effect of adding a module to a method lookup path. When you do this: class C include M end you''re adding M to the method lookup path of instances of C. When you do this: c = C.new c.extend(M) you''re adding M to the method lookup path of one particular instance of C. In other words, extend is a kind of object-specific version of include.> The context is I''m just trying to understand why both are used with the > acts_as_audited plugin:As with so many Ruby questions, it comes down to: classes are objects too :-) What you''re seeing, among other things, is a class being extended so that the class object itself will respond to a particular set of methods. This technique is used a lot in the Rails source. I''ve also got a detailed discussion of it in my book. David -- David A. Black | dblack-TKXtfPMJ4Ozk1uMJSBkQmQ@public.gmane.org Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3] DABlog (DAB''s Weblog) [2] | Co-director, Ruby Central, Inc. [4] [1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com [2] http://dablog.rubypal.com | [4] http://www.rubycentral.org --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk -~----------~----~----~----~------~----~------~--~---
Thanks David - understand this, but still having a few troubles understanding this in context. Don''t suppose you/someone could clarify how things work in the following situation. Its code from acts_as_audited. Basically the cutback guts of it which I''m having trouble with is as follows. The cutback code is below. Questions I have include: Q1) Is the acts_as_audited function in class Contact run when the "Contact" class is being read into the interpreter OR is it just actually carried out when a Contact class is instantiated? Q2) Whenever acts_as_audited is run from Contact does the "include" line include the methods into the Contact object? (or was the Contact context really a class as opposed to instantiated object?) Q3) I assume "class_eval" is just included to somehow ensure things like the upcoming "extend" method is applied to the object which is calling (e.g. in this case the "contact" object)??? i.e. trying to understand why "class_eval" was required here Q4) When the "extend" line in then hit within "class_eval" does it add these methods directly to the Contact object? (or was the Contact context really a class as opposed to instantiated object?) Q5) I''m not sure if I should even ask about the last line "ActiveRecord:: Base.send :include..." which is outside the modules, and when it is actually run and how it fits into things here :( ... but if you do understand and your on a roll with these questions I''m really trying to understand this stuff :) # contact.rb ======model=======class Contact < ActiveRecord::Base acts_as_audited :user_class_name => ''current_user'', :user_method => ''login'' cattr_accessor :current_user # although I haven''t got the ''current_user'' part working yet I must admit end #acts_as_audited.rb ======================================module CollectiveIdea #:nodoc: module Acts #:nodoc: module Audited CALLBACKS = [:clear_changed_attributes, :audit_create, :audit_update, :audit_destroy] def self.included(base) # :nodoc: base.extend ClassMethods end module ClassMethods def acts_as_audited(options = {}) return if self.included_modules.include? (CollectiveIdea::Acts::Audited::InstanceMethods) include CollectiveIdea::Acts::Audited::InstanceMethods class_eval do extend CollectiveIdea::Acts::Audited::SingletonMethods cattr_accessor :non_audited_columns, :audited_user_class_name, :audited_user_method . . . end end end module InstanceMethods . various def''ed methods . . end # InstanceMethods module SingletonMethods end end end end ActiveRecord::Base.send :include, CollectiveIdea::Acts::Audited #acts_as_audited.rb ====================================== On 9/5/06, dblack-TKXtfPMJ4Ozk1uMJSBkQmQ@public.gmane.org <dblack-TKXtfPMJ4Ozk1uMJSBkQmQ@public.gmane.org> wrote:> > > Hi -- > > On Mon, 4 Sep 2006, Greg Hauptmann wrote: > > > > > Hi, > > > > Just wondering when one would use "include" over "extend"? Both seem to > > bring in methods to the class no? > > Neither of them actually brings methods into a class. They both have > the effect of adding a module to a method lookup path. When you do > this: > > class C > include M > end > > you''re adding M to the method lookup path of instances of C. When you > do this: > > c = C.new > c.extend(M) > > you''re adding M to the method lookup path of one particular instance > of C. In other words, extend is a kind of object-specific version of > include. > > > The context is I''m just trying to understand why both are used with the > > acts_as_audited plugin: > > As with so many Ruby questions, it comes down to: classes are objects > too :-) What you''re seeing, among other things, is a class being > extended so that the class object itself will respond to a particular > set of methods. This technique is used a lot in the Rails source. > I''ve also got a detailed discussion of it in my book. > > > David >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk -~----------~----~----~----~------~----~------~--~---