meta_programming 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -27,6 +27,14 @@ I consider this Gem in personal beta and suspect it will be in this state for
27
27
  a while. If you use it, expect changes that may break your code until the
28
28
  lexigraph has a chance to mature.
29
29
 
30
+ If you include metaprogramming in your projects, I suggest you limit the version
31
+ dependency to 0.2.x, something like
32
+
33
+ s.add_dependency('meta_programming', '>= 0.2.0', '< 0.3.0')
34
+
35
+ Any 0.2.x version will be backward compatible with any earlier 0.2.x version. A
36
+ bump to 0.3.0 cannot be guaranteed backward compatible.
37
+
30
38
  I welcome comments and collaborators who
31
39
  would like to develop a library for simplifying common patterns and limit
32
40
  the pitfalls of Ruby meta-programming.
@@ -94,7 +102,7 @@ The matcher may be any of a symbol, string, regular expression or Proc/lambda.
94
102
  ==== String, symbols and regular expression matchers
95
103
 
96
104
  define_ghost_method('my_method) {|sym, *args| ... }
97
- define_ghost_method(':my_method) {|sym, *args| ... }
105
+ define_ghost_method(:my_method) {|sym, *args| ... }
98
106
  define_ghost_method(/^my_method$/) {|sym, *args| ... }
99
107
 
100
108
  String and symbol matchers will only process the method if its name matches the string
@@ -110,14 +118,14 @@ method block. This is not the case with lambda matchers.
110
118
  Lambda matchers provide an opportunity to greatly scrutinize the method call. The method is
111
119
  invoked for any lambda not evaluating to nil or false.
112
120
 
113
- proc = lambda{|method_name, *args| ...matching code... }
121
+ proc = lambda{|method_name| ...matching code... }
114
122
  define_ghost_method(proc) {|proc_result, *args| ... }
115
123
 
116
124
  Proc and lambda matchers differ from the other matcher in that the second parameter passes
117
125
  the result of the Proc/lambda matcher to the method block. This feature lets the matcher
118
126
  pre-process the name of the method passed to the body.
119
127
 
120
- proc = lambda{|sym, *args| sym =~ /^not_(.+\?)$/ && self.class.method_defined?($1.to_sym) && $1.to_sym }
128
+ proc = lambda{|sym| sym =~ /^not_(.+\?)$/ && self.class.method_defined?($1.to_sym) && $1.to_sym }
121
129
 
122
130
  The lambda matcher is evaluated in the context of the object and can
123
131
  call methods of the object as in the above example. The lambda matcher is also called
@@ -127,12 +135,26 @@ matcher to avoid a stack overflow error. Instead call method_defined? on the cl
127
135
  The extra flexibility comes at greater responsibility. If you only want to pass the
128
136
  method name symbol to the method block, don't forget to return it from the lambda.
129
137
 
130
- proc = lambda{|sym, *args| sym =~ /^not_.+\?$/ && sym }
138
+ proc = lambda{|sym| sym =~ /^not_.+\?$/ && sym }
131
139
 
132
140
 
133
141
  ==== Critique
134
142
 
135
- I'm not completely satisfied with the design of the implementation.
143
+ 2010.04.30
144
+
145
+ I'm not satisfied with the ghost method implementation.
146
+
147
+ 2010.05.02
148
+
149
+ I like the fact that define_ghost_method blocks and lambda matchers are now evaluated
150
+ in the context of the object and that the object is no longer passed into the method block
151
+ and lambda matcher. The method now behaves similarly to its cousin, +define_method+.
152
+
153
+ == Testing
154
+
155
+ Passes for Ruby
156
+ * 1.8.7
157
+ * 1.9.1
136
158
 
137
159
  == Dependencies
138
160
 
@@ -4,6 +4,16 @@ module MetaProgramming
4
4
  raise 'This module may only be included in class Class' unless base.name == 'Class'
5
5
  end
6
6
 
7
+ #opts :matcher=>/xx/, :except=>[:method_name, //, ''], :only=>[:method_name, //, '']
8
+ # def cast_proxy(target_klass, opts={}, &block)
9
+ # matcher = lambda{|sym| target_klass.method_defined?(sym) && sym}
10
+ # define_ghost_method(matcher) {|sym, *args| block.call }
11
+ # end
12
+
13
+ def dynamic_proxy(target, opts={}, &block)
14
+
15
+ end
16
+
7
17
  def blank_slate(opts={})
8
18
  opts[:except] = opts[:except] ? (opts[:except].is_a?(Array) ? opts[:except] : [opts[:except]]) : []
9
19
  exceptions = opts[:except].map(&:to_s)
@@ -39,10 +39,10 @@ module MetaProgramming
39
39
  class_eval do
40
40
  method_name_with_ext, method_name_without_ext = Helpers.compose_chaining_symbols(method_name, ext)
41
41
  instance_variable = Helpers.escape_method_name(method_name_with_ext)
42
- if method_access_level(method_name_with_ext)
43
- raise "#{method_name_with_ext} already chained. Rechaining not permitted" if eigenclass.instance_variable_defined?("@#{instance_variable}")
44
- if method_access_level(method_name.to_sym)
45
- #alias_method_chain(method_name.to_sym, ext.to_sym)
42
+ if (method_defined?(method_name_with_ext) || private_method_defined?(method_name_with_ext))
43
+ raise(MetaProgramming::AliasMethodChainError, "#{method_name_without_ext} already exists. Circular references not permitted.") if (method_defined?(method_name_without_ext) || private_method_defined?(method_name_without_ext))
44
+ raise(MetaProgramming::AliasMethodChainError, "#{method_name_with_ext} already chained. Rechaining not permitted") if eigenclass.instance_variable_defined?("@#{instance_variable}")
45
+ if (method_defined?(method_name.to_sym) || private_method_defined?(method_name.to_sym))
46
46
  alias_method method_name_without_ext, method_name.to_sym
47
47
  alias_method method_name.to_sym, method_name_with_ext
48
48
  case method_access_level(method_name_without_ext)
@@ -74,16 +74,16 @@ module MetaProgramming
74
74
  def define_ghost_method(matcher, &block)
75
75
  raise "Must have a block" unless block_given?
76
76
  raise ArgumentError, "Matcher argument must be either a 'string', :symbol, /regexp/ or proc" unless (matcher.nil? || [String, Symbol, Regexp, Proc].any?{|c| matcher.is_a?(c)})
77
- ext = "#{self.name.gsub(/^.+>::/,'')}_#{matcher.class.name.gsub(/^.+>::/,'')}#{matcher.hash.abs.to_s}"
78
- _ghost_method_handler = "_ghost_method_handler_#{ext}".to_sym
79
- _ghost_method_matcher = "_ghost_method_matcher_#{ext}".to_sym
77
+ uniq_ext = "#{self.name.gsub(/^.+>::/,'')}_#{matcher.class.name.gsub(/^.+>::/,'')}#{matcher.hash.abs.to_s}"
78
+ _ghost_method_handler = "_ghost_method_handler_#{uniq_ext}".to_sym
79
+ _ghost_method_matcher = "_ghost_method_matcher_#{uniq_ext}".to_sym
80
80
  define_method(_ghost_method_handler, block)
81
81
  private _ghost_method_handler
82
82
  if matcher.is_a?(Proc)
83
83
  define_method(_ghost_method_matcher, matcher)
84
84
  private _ghost_method_matcher
85
85
  end
86
- define_chained_method(:method_missing, ext.to_sym) do |symbol, *args|
86
+ define_chained_method(:method_missing, uniq_ext.to_sym) do |symbol, *args|
87
87
  handled = case matcher
88
88
  when Regexp then !(symbol.to_s =~ matcher).nil?
89
89
  when String, Symbol then (symbol == matcher.to_sym)
@@ -102,11 +102,11 @@ module MetaProgramming
102
102
  raise handler_error, "#{handler_error.message} in a ghost method block called with symbol :#{symbol}."
103
103
  end
104
104
  else
105
- __send__("method_missing_without_#{ext}".to_sym, symbol, *args)
105
+ __send__("method_missing_without_#{uniq_ext}".to_sym, symbol, *args)
106
106
  end
107
107
  end
108
108
  #cripple respond_to? in deference of 1.8 -- the include_private no longer works
109
- define_chained_method(:respond_to?, ext.to_sym) do |method_name| #1.9 only, include_private=nil|
109
+ define_chained_method(:respond_to?, uniq_ext.to_sym) do |method_name| #1.9 only, include_private=nil|
110
110
  responds = case matcher
111
111
  when Regexp then !(method_name.to_s =~ matcher).nil?
112
112
  when String, Symbol then method_name == matcher.to_sym
@@ -117,7 +117,7 @@ module MetaProgramming
117
117
  raise matcher_error, "#{matcher_error.message} in a ghost method matcher called in respond_to? for symbol :#{method_name}. Be sure to use self.class.method_defined? instead of respond_to? in a lambda matcher."
118
118
  end
119
119
  end
120
- responds || __send__("respond_to_without_#{ext}?".to_sym, method_name) #1.9 only, include_private)
120
+ responds || __send__("respond_to_without_#{uniq_ext}?".to_sym, method_name) #1.9 only, include_private)
121
121
  end
122
122
  end
123
123
  end
@@ -2,6 +2,7 @@ require 'meta_programming/object'
2
2
  require 'meta_programming/class'
3
3
 
4
4
  module MetaProgramming
5
+ class AliasMethodChainError < Exception; end
5
6
  end
6
7
 
7
8
  Object.send :include, MetaProgramming::Object
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe "MetaProgramming" do
4
+
5
+ describe "dynamic_proxy method" do
6
+ # it "should result in NoMethodError when target object is nil"
7
+ # it "should take the symbol of an instance variable as the target"
8
+ # it "should take the string representation of an instance variable as the target"
9
+ # it "should take the symbol of a method as the target"
10
+ # it "should accept and obey a :matcher option"
11
+ # it "should accept and obey an :except option"
12
+ # it "should accept and obey an :only option"
13
+ # it "should delegate method calls when no block given"
14
+ # it "should pass the method name to the block"
15
+ # it "should pass a block to forward "
16
+ end
17
+
18
+ end
19
+
20
+
@@ -72,6 +72,23 @@ describe "MetaProgramming" do
72
72
  end
73
73
  end
74
74
  describe "safe_alias_method_chain method" do
75
+ it "should warn against circular references" do
76
+ class L1
77
+ def method_call
78
+ 'the real one'
79
+ end
80
+ def method_call_with_alias
81
+ method_call_without_alias
82
+ end
83
+ safe_alias_method_chain :method_call, :alias
84
+ end
85
+ class L2 < L1
86
+ def method_call_with_alias
87
+ method_call_without_alias
88
+ end
89
+ end
90
+ lambda { L2.class_eval { safe_alias_method_chain :method_call, :alias } }.should raise_exception(MetaProgramming::AliasMethodChainError, /Circular references/)
91
+ end
75
92
  it "should chain for a primary protected method" do
76
93
  class D1
77
94
  def primary(array); array << 'primary'; end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 0
9
- version: 0.2.0
8
+ - 1
9
+ version: 0.2.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jeff Patmon
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-02 00:00:00 -07:00
17
+ date: 2010-05-21 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -35,6 +35,7 @@ files:
35
35
  - spec/spec_helper.rb
36
36
  - spec/blank_slate_spec.rb
37
37
  - spec/spec.opts
38
+ - spec/dynamic_proxy_spec.rb
38
39
  - init.rb
39
40
  - LICENSE
40
41
  - Rakefile
@@ -76,3 +77,4 @@ test_files:
76
77
  - spec/meta_programming_spec.rb
77
78
  - spec/ghost_methods_spec.rb
78
79
  - spec/blank_slate_spec.rb
80
+ - spec/dynamic_proxy_spec.rb