blankslate.rb 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. #!/usr/bin/env ruby
  2. #--
  3. # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
  4. # All rights reserved.
  5. # Permission is granted for use, copying, modification, distribution,
  6. # and distribution of modified versions of this work as long as the
  7. # above copyright notice is included.
  8. #++
  9. ######################################################################
  10. # BlankSlate provides an abstract base class with no predefined
  11. # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
  12. # BlankSlate is useful as a base class when writing classes that
  13. # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
  14. #
  15. class BlankSlate
  16. class << self
  17. # Hide the method named +name+ in the BlankSlate class. Don't
  18. # hide +instance_eval+ or any method beginning with "__".
  19. def hide(name)
  20. if instance_methods.include?(name.to_s) and
  21. name !~ /^(__|instance_eval)/
  22. @hidden_methods ||= {}
  23. @hidden_methods[name.to_sym] = instance_method(name)
  24. undef_method name
  25. end
  26. end
  27. def find_hidden_method(name)
  28. @hidden_methods ||= {}
  29. @hidden_methods[name] || superclass.find_hidden_method(name)
  30. end
  31. # Redefine a previously hidden method so that it may be called on a blank
  32. # slate object.
  33. def reveal(name)
  34. hidden_method = find_hidden_method(name)
  35. fail "Don't know how to reveal method '#{name}'" unless hidden_method
  36. define_method(name, hidden_method)
  37. end
  38. end
  39. instance_methods.each { |m| hide(m) }
  40. end
  41. ######################################################################
  42. # Since Ruby is very dynamic, methods added to the ancestors of
  43. # BlankSlate <em>after BlankSlate is defined</em> will show up in the
  44. # list of available BlankSlate methods. We handle this by defining a
  45. # hook in the Object and Kernel classes that will hide any method
  46. # defined after BlankSlate has been loaded.
  47. #
  48. module Kernel
  49. class << self
  50. alias_method :blank_slate_method_added, :method_added
  51. # Detect method additions to Kernel and remove them in the
  52. # BlankSlate class.
  53. def method_added(name)
  54. result = blank_slate_method_added(name)
  55. return result if self != Kernel
  56. BlankSlate.hide(name)
  57. result
  58. end
  59. end
  60. end
  61. ######################################################################
  62. # Same as above, except in Object.
  63. #
  64. class Object
  65. class << self
  66. alias_method :blank_slate_method_added, :method_added
  67. # Detect method additions to Object and remove them in the
  68. # BlankSlate class.
  69. def method_added(name)
  70. result = blank_slate_method_added(name)
  71. return result if self != Object
  72. BlankSlate.hide(name)
  73. result
  74. end
  75. def find_hidden_method(name)
  76. nil
  77. end
  78. end
  79. end
  80. ######################################################################
  81. # Also, modules included into Object need to be scanned and have their
  82. # instance methods removed from blank slate. In theory, modules
  83. # included into Kernel would have to be removed as well, but a
  84. # "feature" of Ruby prevents late includes into modules from being
  85. # exposed in the first place.
  86. #
  87. class Module
  88. alias blankslate_original_append_features append_features
  89. def append_features(mod)
  90. result = blankslate_original_append_features(mod)
  91. return result if mod != Object
  92. instance_methods.each do |name|
  93. BlankSlate.hide(name)
  94. end
  95. result
  96. end
  97. end