# gmosx: replaces gems/facet_more-1.0.1/lib/facet/paramix.rb # to make Nitro work, till we come up with a better solution. # # This is a temporary solution until the Facets version is # fixed. #-- # Parametric Mixins # # Copyright (c) 2005 Thomas Sawyer and George Moschovitis # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. # # ========================================================================== # Revision History # -------------------------------------------------------------------------- # 2005-12-02 trans * Simplified into fine elegance. # 2005-09-19 trans * Cleaned up and renamed. # 2005-04-28 trans * Added to Calibre. # ========================================================================== # #++ require 'facet/module/basename' #:title: Parametric Mixins # # Parametric Mixins provides parameters for mixin modules. # Module parameters can be set at the time of inclusion or extension, # then accessed via an instance method of the same name as the included # module. # # == Synopsis # # module Mixin # def hello # puts "Hello from #{Mixin(:name)}" # end # end # # class MyClass # include Mixin, :name => 'Ruby' # end # # m = MyClass.new # m.hello -> 'Hello from Ruby' # # You can view the full set of parameters via the #mixin_parameters # class method, which returns a hash keyed on the included modules. # # MyClass.mixin_parameters #=> {Mixin=>{:name=>'Ruby'}} # MyClass.mixin_parameters[Mixin] #=> {:name=>'Ruby'} # # To create _dynamic mixins_ you can use the #included callback # method along with mixin_parameters method like so: # # module Mixin # def included( base ) # parms = base.mixin_parameters[self] # base.class_eval { # def hello # puts "Hello from #{parms(:name)}" # end # } # end # end # # More conveniently a new callback has been added, #included_with_parameters, # which passes in the parameters in addition to the base class/module. # # module Mixin # def included_with_parameters( base, parms ) # base.class_eval { # def hello # puts "Hello from #{parms(:name)}" # end # } # end # end # # We would prefer to have passed the parameters through the #included callback # method itself, but implementation of such a feature is much more complicated. # If a reasonable solution presents itself in the future however, we will fix. # # == Author(s) # # * George Moschovitis # * Thomas Sawyer # class Module # Store for module parameters. This is local per module # and indexed on module/class included-into. def mixin_parameters ; @mixin_parameters ||= {} ; end alias_method :include_without_parameters, :include def include(*args) params = args.last.is_a?(Hash) ? args.pop : {} =begin for mod in args mixin_parameters[mod] = params define_method( mod.basename ) do |key| if params.key?(key) params[key] else super if defined?( super ) end end end =end r = include_without_parameters(*args) for mod in args mod.included_with_parameters( self, params ) if mod.respond_to?(:included_with_parameters) end r end alias_method :extend_without_parameters, :extend def extend(*args) params = args.last.is_a?(Hash) ? args.pop : nil =begin for mod in args (class << self; self; end).class_eval do mixin_parameters[mod] = params define_method( mod.basename ) do |key| if params.key?(key) params[key] else super if defined?( super ) end end end end =end r = extend_without_parameters(*args) extended_with_parameters( self, params ) if method_defined?(:extended_with_parameters) r end end # _____ _ # |_ _|__ ___| |_ # | |/ _ \/ __| __| # | | __/\__ \ |_ # |_|\___||___/\__| # =begin test require 'test/unit' require 'facet/kernel/adhoc' class TC01 < Test::Unit::TestCase module M def f M(:p) end def self.included_with_parameters( base, parms ) end end class C include M, :p => "check" end class D include M, :p => "steak" end def test_01_001 c = C.new assert_equal( "check", c.f ) end def test_01_002 d = D.new assert_equal( "steak", d.f ) end def test_01_003 assert_equal( {M=>{:p => "check"}}, C.mixin_parameters ) assert_equal( {M=>{:p => "steak"}}, D.mixin_parameters ) end end class TC02 < Test::Unit::TestCase module M def f M(:p) end end class C extend M, :p => "mosh" end class D extend M, :p => "many" end def test_02_001 assert_equal( "mosh", C.f ) end def test_02_002 assert_equal( "many", D.f ) end def test_02_003 assert_equal( {M=>{:p => "mosh"}}, C.adhoc.mixin_parameters ) assert_equal( {M=>{:p => "many"}}, D.adhoc.mixin_parameters ) end end =end