File: method_space.rb

package info (click to toggle)
ruby-facets 2.9.2-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 9,824 kB
  • sloc: ruby: 25,483; xml: 90; makefile: 20
file content (99 lines) | stat: -rw-r--r-- 2,331 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
require 'facets/functor'
require 'facets/module/basename'

class Module

  # Create method namespaces, allowing for method
  # chains but still accessing the object's instance.
  #
  #   class A
  #     attr_writer :x
  #     method_space :inside do
  #       def x; @x; end
  #     end
  #   end
  #
  #   a = A.new
  #   a.x = 10
  #   a.inside.x  #=> 10
  #
  #   expect NoMethodError do
  #     a.x
  #   end
  #
  # NOTE: This is not (presently) a common core extension and is not
  # loaded automatically when using <code>require 'facets'</code>.
  #
  # CREDIT: Pit Captain
  def method_space(name, mod=nil, &blk)

    ## If block is given then create a module, otherwise
    ## get the name of the module.
    if block_given?
      name = name.to_s
      raise ArgumentError if mod
      mod  = Module.new(&blk)
    else
      if Module === name
        mod = name
        name = mod.basename.downcase
      end
      mod  = mod.dup
    end

    ## Include the module. This is neccessary, otherwise
    ## Ruby won't let us bind the instance methods.
    include mod

    ## Save the instance methods of the module and
    ## replace them with a "transparent" version.
    methods = {}
    mod.instance_methods(false).each do |m|
      methods[m.to_sym] = mod.instance_method(m)
      mod.module_eval %{
        def #{m}(*a,&b)
          super(*a,&b)
        end
      }
      ##mod.instance_eval do
      ##  define_method(m)
      ##    super
      ##  end
      ##end
    end

    ## Add a method for the namespace that delegates
    ## via the Functor to the saved instance methods.
    define_method(name) do
      mtab = methods
      Functor.new do |op, *args|
        if meth = mtab[op.to_sym]
          meth.bind(self).call(*args)
        else
          #self.__send__(op, *args)
          raise NoMethodError, "undefined method `#{m}'"
        end
      end
    end
  end

  # Include a module via a specified space.
  #
  #   module T
  #     def t ; "HERE" ; end
  #   end
  #
  #   class X
  #     include_as :test => T
  #     def t ; test.t ; end
  #   end
  #
  #   X.new.t  #=> "HERE"
  #
  # NOTE: This is not (presently) a common core extension and is not
  # loaded automatically when using <code>require 'facets'</code>.
  def include_as(h)
    h.each{ |name, mod| method_space(name, mod) }
  end

end