protocol 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES CHANGED
@@ -1,3 +1,5 @@
1
+ 0.9.0 - 2009-08-05
2
+ * Supporting Ruby >= 1.9.2 via ruby_parser
1
3
  0.8.2 - 2008-07-20
2
4
  * Some cleanup.
3
5
  * Bumped up dependency on ParseTree to 3.0.
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # vim: set et sw=2 ts=2:
2
-
3
1
  begin
4
2
  require 'rake/gempackagetask'
5
3
  rescue LoadError
@@ -11,7 +9,7 @@ include Config
11
9
  PKG_NAME = 'protocol'
12
10
  PKG_VERSION = File.read('VERSION').chomp
13
11
  PKG_FILES = FileList['**/*'].exclude(/(CVS|\.svn|pkg|coverage)/)
14
- CLEAN.include 'coverage', 'doc'
12
+ CLEAN.include 'coverage', 'doc', Dir['benchmarks/data/*.*']
15
13
 
16
14
  desc "Installing library"
17
15
  task :install do
@@ -51,6 +49,7 @@ EOT
51
49
 
52
50
  s.require_path = 'lib'
53
51
  s.add_dependency 'ParseTree', '~> 3.0'
52
+ s.add_dependency 'ruby_parser', '~> 2.0'
54
53
 
55
54
  s.has_rdoc = true
56
55
  s.rdoc_options << '--main' << 'doc-main.txt'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.2
1
+ 0.9.0
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bullshit'
4
+ require 'protocol'
5
+
6
+ class MethodParserBenchmark < Bullshit::RepeatCase
7
+ include Protocol
8
+
9
+ class Foo
10
+ def foo(a, b = nil, *c)
11
+ yield
12
+ end
13
+ end
14
+
15
+ warmup yes
16
+
17
+ iterations 500
18
+
19
+ truncate_data do
20
+ alpha_level 0.05
21
+ window_size 10
22
+ slope_angle 0.003
23
+ end
24
+
25
+ output_dir File.join(File.dirname(__FILE__), 'data')
26
+ data_file yes
27
+ histogram yes
28
+
29
+ autocorrelation do
30
+ alpha_level 0.05
31
+ max_lags 50
32
+ file yes
33
+ end
34
+
35
+ def benchmark_standard
36
+ @mp = MethodParser.new(Foo, :foo)
37
+ end
38
+
39
+ def after_standard
40
+ @mp.args == [ :a, :b, :'*c', :'&block' ] or raise "wrong result"
41
+ end
42
+
43
+ def benchmark_nocache
44
+ @mp = MethodParser.new(Foo, :foo)
45
+ end
46
+
47
+ def after_nocache
48
+ @mp.args == [ :a, :b, :'*c', :'&block' ] or raise "wrong result"
49
+ MethodParser.flush_source_cache
50
+ end
51
+ end
@@ -18,9 +18,9 @@ goliath = Person.new 'Goliath', 2_60
18
18
  david = Person.new 'David', 1_40
19
19
  symbol = Person.new 'The artist formerly known as Prince', 1_40
20
20
 
21
- david < goliath # => true
22
- david >= goliath # => false
23
- david == symbol # => true
21
+ puts "david < goliath = #{david < goliath} (true)"
22
+ puts "david >= goliath = #{david >= goliath} (false)"
23
+ puts "david == symbol = #{david == symbol} (true)"
24
24
 
25
25
  begin
26
26
  class NoPerson
@@ -32,6 +32,7 @@ begin
32
32
 
33
33
  conform_to Comparing
34
34
  end
35
+ puts "Should have thrown Protocol::CheckFailed!"
35
36
  rescue Protocol::CheckFailed => e
36
- e.to_s # => "Comparing#<=>(1): method '<=>' not implemented in NoPerson"
37
+ p e
37
38
  end
@@ -1,4 +1,12 @@
1
- require 'protocol/core'
1
+ require 'protocol'
2
+
3
+ Enumerating = Protocol do
4
+ # Iterate over each element of this Enumerating class and pass it to the
5
+ # _block_.
6
+ def each(&block) end
7
+
8
+ include Enumerable
9
+ end
2
10
 
3
11
  begin
4
12
  class FailAry
@@ -11,8 +19,9 @@ begin
11
19
 
12
20
  conform_to Enumerating
13
21
  end
22
+ puts "Should have thrown Protocol::CheckFailed!"
14
23
  rescue Protocol::CheckFailed => e
15
- e.to_s # => "Enumerating#each(0&): expected a block argument for FailAry"
24
+ p e # => "Enumerating#each(0&): expected a block argument for FailAry"
16
25
  end
17
26
 
18
27
  class Ary
@@ -27,9 +36,9 @@ class Ary
27
36
  conform_to Enumerating
28
37
  end
29
38
 
30
- Ary.new.map { |x| x * x } # => [1, 4, 9]
31
- Ary.conform_to?(Enumerating) # => true
32
- Ary.new.conform_to?(Enumerating) # => true
39
+ puts Ary.new.map { |x| x * x }.inspect + " ([1, 4, 9])"
40
+ puts Ary.conform_to?(Enumerating).to_s + " (true)"
41
+ puts Ary.new.conform_to?(Enumerating).to_s + " (true)"
33
42
 
34
43
  Enumerating.check_failure :none
35
44
 
@@ -43,5 +52,5 @@ class FailAry2
43
52
  conform_to Enumerating
44
53
  end
45
54
 
46
- FailAry2.conform_to?(Enumerating) # => false
47
- FailAry2.new.conform_to?(Enumerating) # => false
55
+ puts FailAry2.conform_to?(Enumerating).to_s + " (false)"
56
+ puts FailAry2.new.conform_to?(Enumerating).to_s + " (false)"
data/examples/indexing.rb CHANGED
@@ -21,7 +21,8 @@ if $0 == __FILE__
21
21
  class Proc
22
22
  conform_to Indexing
23
23
  end
24
+ puts "Should have thrown Protocol::CheckFailed!"
24
25
  rescue Protocol::CheckFailed => e
25
- e.to_s # => "Indexing#[]=(): method '[]=' not implemented in Proc"
26
+ p e
26
27
  end
27
28
  end
data/examples/locking.rb CHANGED
@@ -53,7 +53,7 @@ if $0 == __FILE__
53
53
  # Locking '...'.
54
54
  # Synchronized with '...'..
55
55
  # Unlocking '...'.
56
- mutex = FileMutex.new
56
+ p mutex = FileMutex.new
57
57
  mutex.synchronize do
58
58
  puts "Synchronized with '#{mutex.path}'."
59
59
  end
@@ -74,13 +74,13 @@ if $0 == __FILE__
74
74
  conform_to Locking # actually Mutex itself would conform as well ;)
75
75
  end
76
76
 
77
- mutex = MemoryMutex.new
77
+ p mutex = MemoryMutex.new
78
78
  mutex.synchronize do
79
79
  puts "Synchronized in memory."
80
80
  end
81
81
 
82
- MemoryMutex.conform_to? Locking # => true
83
- MemoryMutex.new.conform_to? Locking # => true
82
+ puts MemoryMutex.conform_to?(Locking).to_s + ' (true)'
83
+ puts MemoryMutex.new.conform_to?(Locking).to_s + ' (true)'
84
84
 
85
85
  class MyClass
86
86
  def initialize
@@ -90,22 +90,22 @@ if $0 == __FILE__
90
90
  attr_reader :mutex
91
91
 
92
92
  def mutex=(mutex)
93
- Locking.check mutex
93
+ Locking =~ mutex
94
94
  @mutex = mutex
95
95
  end
96
96
  end
97
97
 
98
98
  obj = MyClass.new
99
- obj.mutex # => #<FileMutex:0xb788f9ac @tempfile=#<File:/tmp/file-mutex.26553.2>>
99
+ p obj.mutex # => #<FileMutex:0xb788f9ac @tempfile=#<File:/tmp/file-mutex.26553.2>>
100
100
  begin
101
101
  obj.mutex = Object.new
102
+ puts "Should have thrown Protocol::CheckFailed!"
102
103
  rescue Protocol::CheckFailed => e
103
- e # => #<Protocol::CheckFailed: #<Protocol::NotImplementedErrorCheckError: Locking#lock(0): method 'lock' not responding in #<Object:0xb788f59c>>|#<Protocol::NotImplementedErrorCheckError: Locking#unlock(0): method 'unlock' not responding in #<Object:0xb788f59c>>
104
+ p e
104
105
  end
105
- obj.mutex = MemoryMutex.new # => #<MemoryMutex:0xb788f038 @mutex=#<Mutex:0xb788eea8>>
106
+ p obj.mutex = MemoryMutex.new # => #<MemoryMutex:0xb788f038 @mutex=#<Mutex:0xb788eea8>>
106
107
  # This works as well:
107
- obj.mutex = Mutex.new # => #<Mutex:0xb788ecc8>
108
- Locking.check Mutex # => true
109
- Mutex.conform_to? Locking # => true
108
+ obj.mutex = Mutex.new
109
+ puts Locking.check(Mutex).to_s + ' (true)'
110
+ puts Mutex.conform_to?(Locking).to_s + ' (true)'
110
111
  end
111
- # >> "Locking '/tmp/file-mutex.26553.1'.\nSynchronized with '/tmp/file-mutex.26553.1'.\nUnlocking '/tmp/file-mutex.26553.1'.\nSynchronized in memory.\n"
data/examples/queue.rb CHANGED
@@ -100,55 +100,58 @@ class Q
100
100
  conform_to QueueProtocol
101
101
  end
102
102
 
103
- if $0 == __FILE__
104
- q = Q.new
105
- q.observer = O.new
106
- q.empty? # => true
107
- begin
108
- q.deq
109
- rescue Protocol::CheckError => e
110
- e # => #<Protocol::PreconditionCheckError: QueueProtocol#deq(0): precondition failed for Q>
111
- end
112
- q.empty? # => true
113
- q.size # => 0
114
- q.first # => nil
115
- q.enq 2
116
- q.empty? # => false
117
- q.size # => 1
118
- q.first # => 2
119
- q.enq 2
120
- q.size # => 2
121
- q.deq # => 2
122
- q.first # => 2
123
- q.size # => 1
124
-
125
- q = Q.new
126
- q.observer = O.new
127
- q.empty? # => true
128
- begin
129
- q.deq
130
- rescue Protocol::CheckError => e
131
- e # => #<Protocol::PreconditionCheckError: QueueProtocol#deq(0): precondition failed for Q>
132
- end
133
- q.empty? # => true
134
- q.size # => 0
135
- q.first # => nil
136
- q.enq 2
137
- q.empty? # => false
138
- q.size # => 1
139
- q.first # => 2
140
- q.enq 2
141
- q.size # => 2
142
- q.deq # => 2
143
- q.first # => 2
144
- q.size # => 1
145
- q.observer = SneakyO.new
146
- q.deq # => 2
147
- q.empty? # => true
148
- begin
149
- q.enq 7
150
- rescue Protocol::CheckError => e
151
- e # => #<Protocol::PostconditionCheckError: ObserverProtocol#after_enq(1): postcondition failed for SneakyO, result = 7>
152
- end
103
+ def should_be(a1, a2)
104
+ puts "#{a1.inspect} (#{a2.inspect})"
105
+ end
106
+
107
+ q = Q.new
108
+ q.observer = O.new
109
+ should_be q.empty?, true
110
+ begin
111
+ q.deq
112
+ rescue Protocol::CheckError => e
113
+ p e
114
+ end
115
+ should_be q.empty?, true
116
+ should_be q.size, 0
117
+ should_be q.first, nil
118
+ q.enq 2
119
+ should_be q.empty?, false
120
+ should_be q.size, 1
121
+ should_be q.first, 2
122
+ q.enq 2
123
+ should_be q.size, 2
124
+ should_be q.deq, 2
125
+ should_be q.first, 2
126
+ should_be q.size, 1
127
+
128
+ q = Q.new
129
+ q.observer = O.new
130
+ should_be q.empty?, true
131
+ begin
132
+ q.deq
133
+ puts "Should have thrown Protocol::CheckFailed!"
134
+ rescue Protocol::CheckError => e
135
+ p e
136
+ end
137
+ should_be q.empty?, true
138
+ should_be q.size, 0
139
+ should_be q.first, nil
140
+ q.enq 2
141
+ should_be q.empty?, false
142
+ should_be q.size, 1
143
+ should_be q.first, 2
144
+ q.enq 2
145
+ should_be q.size, 2
146
+ should_be q.deq, 2
147
+ should_be q.first, 2
148
+ should_be q.size, 1
149
+ q.observer = SneakyO.new
150
+ should_be q.deq, 2
151
+ should_be q.empty?, true
152
+ begin
153
+ q.enq 7
154
+ puts "Should have thrown Protocol::CheckFailed!"
155
+ rescue Protocol::CheckError => e
156
+ p e
153
157
  end
154
- # >> "Enqueued.\nEnqueued.\nDequeued.\nEnqueued.\nEnqueued.\nDequeued.\nDequeued.\nEnqueued.\nDequeued.\n"
data/examples/stack.rb CHANGED
@@ -53,23 +53,23 @@ end
53
53
 
54
54
  if $0 == __FILE__
55
55
  s = S.new
56
- s.top # => nil
57
- s.empty? # => true
58
- s.size # => 0
56
+ puts s.top.inspect + " (nil)"
57
+ puts s.empty?.to_s + " (true)"
58
+ puts s.size.to_s + " (0)"
59
59
  begin
60
60
  s.pop
61
61
  rescue Protocol::CheckError => e
62
- e # => #<Protocol::PreconditionCheckError: StackProtocol#empty?(0): precondition failed for S>
62
+ p e # => #<Protocol::PreconditionCheckError: StackProtocol#empty?(0): precondition failed for S>
63
63
  end
64
- s.empty? # => true
64
+ puts s.empty?.to_s + " (true)"
65
65
  s.push 2
66
- s.empty? # => false
67
- s.size # => 1
68
- s.top # => 2
66
+ puts s.empty?.to_s + " (false)"
67
+ puts s.size.to_s + " (1)"
68
+ puts s.top.to_s + " (2)"
69
69
  s.push 4
70
- s.top # => 4
71
- s.size # => 2
72
- s.pop # => 4
73
- s.top # => 2
74
- s.size # => 1
70
+ puts s.top.to_s + " (4)"
71
+ puts s.size.to_s + " (2)"
72
+ puts s.pop.to_s + " (4)"
73
+ puts s.top.to_s + " (2)"
74
+ puts s.size.to_s + " (1)"
75
75
  end
data/install.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- # vim: set et sw=2 ts=2:
3
2
 
4
3
  require 'rbconfig'
5
4
  require 'fileutils'
@@ -16,3 +15,9 @@ mkdir_p dest
16
15
  for file in Dir['lib/protocol/*.rb']
17
16
  install(file, dest)
18
17
  end
18
+
19
+ dest = File.join(CONFIG["sitelibdir"], 'protocol', 'method_parser')
20
+ mkdir_p dest
21
+ for file in Dir['lib/protocol/method_parser/*.rb']
22
+ install(file, dest)
23
+ end
@@ -0,0 +1,124 @@
1
+ require 'parse_tree'
2
+ require 'sexp_processor'
3
+
4
+ module Protocol
5
+ # Parse protocol method definition to derive a Message specification.
6
+ class MethodParser < SexpProcessor
7
+ # Create a new MethodParser instance for method +methodname+ of module
8
+ # +modul+. For eigenmethods set +eigenclass+ to true, otherwise bad things
9
+ # will happen.
10
+ def initialize(modul, methodname, eigenclass = false)
11
+ super()
12
+ @method = Module === modul ?
13
+ modul.instance_method(methodname) :
14
+ modul.method(methodname)
15
+ self.strict = false
16
+ self.auto_shift_type = true
17
+ @complex = false
18
+ @first_defn = true
19
+ @first_block = true
20
+ @args = []
21
+ @arg_kinds = []
22
+ @arity = @method.arity
23
+ parsed = ParseTree.new.parse_tree_for_method(modul, methodname, eigenclass)
24
+ process parsed
25
+ end
26
+
27
+ # Process +exp+, but catch UnsupportedNodeError exceptions and ignore them.
28
+ def process(exp)
29
+ super
30
+ rescue UnsupportedNodeError => ignore
31
+ end
32
+
33
+ # Returns the names of the arguments of the parsed method.
34
+ attr_reader :args
35
+
36
+ def arg(i)
37
+ @args[i]
38
+ end
39
+
40
+ # Returns the names of the arguments of the parsed method.
41
+ attr_reader :arg_kinds
42
+
43
+ def arg_kind(i)
44
+ @arg_kinds[i]
45
+ end
46
+
47
+ # Returns the arity of the parsed method.
48
+ attr_reader :arity
49
+
50
+ # Return true if this protocol method is a complex method, which ought to
51
+ # be called for checking conformance to the protocol.
52
+ def complex?
53
+ @complex
54
+ end
55
+
56
+ # Return true if a block argument was detected.
57
+ def block_arg?
58
+ @arg_kinds.last == :block
59
+ end
60
+
61
+ # Only consider first the first defn, skip inner method definitions.
62
+ def process_defn(exp)
63
+ if @first_defn
64
+ @first_defn = false
65
+ _name, scope = exp
66
+ process scope
67
+ end
68
+ exp.clear
69
+ s :dummy
70
+ end
71
+
72
+ # Remember the argument names in +exp+ in the args attribute.
73
+ def process_args(exp)
74
+ @args.replace exp.select { |x| x.is_a? Symbol }
75
+ @arg_kinds = @args.map { |a| a.to_s[0] == ?* ? :rest : :req }
76
+ if block = exp.find { |x| x.is_a?(Array) and x.first == :block }
77
+ lasgns = block[1..-1].transpose[1]
78
+ i = args.size - 1
79
+ @args.reverse_each do |a|
80
+ exp.first
81
+ l = lasgns.last
82
+ if a == l
83
+ @arg_kinds[i] = :opt
84
+ lasgns.pop
85
+ end
86
+ i -= 1
87
+ end
88
+ end
89
+ exp.clear
90
+ s :dummy
91
+ end
92
+
93
+ # Remember if we encounter a block argument.
94
+ def process_block_arg(exp)
95
+ @args.push :"&#{exp.first}"
96
+ @arg_kinds.push :block
97
+ exp.clear
98
+ s :dummy
99
+ end
100
+
101
+ # Remember if we encounter a yield keyword.
102
+ def process_yield(exp)
103
+ if @arg_kinds.last != :block
104
+ @args.push :'&block'
105
+ @arg_kinds.push :block
106
+ end
107
+ exp.clear
108
+ s :dummy
109
+ end
110
+
111
+ # We only consider the first block in +exp+ (can there be more than one?),
112
+ # and then try to figure out, if this is a complex method or not. Continue
113
+ # processing the +exp+ tree after that.
114
+ def process_block(exp)
115
+ if @first_block
116
+ @first_block = false
117
+ @complex = exp.flatten.any? { |e| [ :call, :fcall, :vcall ].include?(e) }
118
+ exp.each { |e| process e }
119
+ end
120
+ exp.clear
121
+ s :dummy
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,123 @@
1
+ require 'ruby_parser'
2
+
3
+ module Protocol
4
+ # Parse protocol method definition to derive a Message specification.
5
+ class MethodParser
6
+ class << self
7
+ attr_accessor :__source_cache__
8
+
9
+ # Flushes the source cache.
10
+ def flush_source_cache
11
+ __source_cache__.clear
12
+ self
13
+ end
14
+ end
15
+ self.__source_cache__ = {}
16
+
17
+ # Create a new MethodParser instance for method +methodname+ of module
18
+ # +modul+. For eigenmethods set +eigenclass+ to true, otherwise bad things
19
+ # will happen.
20
+ def initialize(modul, methodname, eigenclass = false)
21
+ @method = Module === modul ?
22
+ modul.instance_method(methodname) :
23
+ modul.method(methodname)
24
+ compute_args
25
+ parse_method
26
+ end
27
+
28
+ # Returns the names of the arguments of the parsed method.
29
+ attr_reader :args
30
+
31
+ # Returns the i-th argument (beginning with 0).
32
+ def arg(i)
33
+ @args[i]
34
+ end
35
+
36
+ # Returns the kinds of the arguments of the parsed method.
37
+ attr_reader :arg_kinds
38
+
39
+ # Returns the i-th kind of an argument (beginning with 0).
40
+ def arg_kind(i)
41
+ @arg_kinds[i]
42
+ end
43
+
44
+ # Returns the arity of the parsed method.
45
+ attr_reader :arity
46
+
47
+ # Return true if this protocol method is a complex method, which ought to
48
+ # be called for checking conformance to the protocol.
49
+ def complex?
50
+ @complex
51
+ end
52
+
53
+ # Return true if a block argument was detected.
54
+ def block_arg?
55
+ @arg_kinds.last == :block
56
+ end
57
+
58
+ private
59
+
60
+ def compute_args
61
+ @arity = @method.arity
62
+ if @method.respond_to?(:parameters)
63
+ parameters = @method.parameters
64
+ @args, @arg_kinds = parameters.map do |kind, name|
65
+ case kind
66
+ when :req
67
+ [ name, kind ]
68
+ when :opt
69
+ [ name, kind ]
70
+ when :rest
71
+ [ :"*#{name}", kind ]
72
+ when :block
73
+ [ :"&#{name}", kind ]
74
+ end
75
+ end.compact.transpose
76
+ else
77
+ raise NotImplementedError,
78
+ "#{@method.class}#parameters as in ruby version >=1.9.2 is required"
79
+ end
80
+ @args ||= []
81
+ @arg_kinds ||= []
82
+ end
83
+
84
+ def cached_source(filename)
85
+ cache = self.class.__source_cache__
86
+ unless source = cache[filename]
87
+ begin
88
+ source = IO.readlines(filename)
89
+ cache[filename] = source
90
+ rescue SystemCallError => e
91
+ $DEBUG and warn "Caught #{e.class}: #{e}"
92
+ nil
93
+ end
94
+ end
95
+ source
96
+ end
97
+
98
+ def parse_method
99
+ @complex = false
100
+ filename, lineno = @method.source_location
101
+ if filename
102
+ source = cached_source(filename) or return
103
+ source = source[(lineno - 1)..-1].join
104
+ current = 0
105
+ tree = nil
106
+ while current = source.index('end', current)
107
+ current += 3
108
+ begin
109
+ tree = RubyParser.new.parse(source[0, current], filename)
110
+ break
111
+ rescue SyntaxError, Racc::ParseError
112
+ end
113
+ end
114
+ ary = tree.to_a.flatten
115
+ @complex = ary.flatten.any? { |node| [ :call, :fcall, :vcall ].include?(node) }
116
+ if ary.index(:yield) and @arg_kinds.last != :block
117
+ @args.push :'&block'
118
+ @arg_kinds.push :block
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,6 +1,6 @@
1
1
  module Protocol
2
2
  # Protocol version
3
- VERSION = '0.8.2'
3
+ VERSION = '0.9.0'
4
4
  VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/lib/protocol.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  require 'protocol/version'
2
- require 'parse_tree'
3
- require 'sexp_processor'
4
2
 
5
3
  module Protocol
4
+ if RUBY_VERSION[/\A1\.8\./]
5
+ require 'protocol/method_parser/parse_tree'
6
+ else
7
+ require 'protocol/method_parser/ruby_parser'
8
+ end
9
+
6
10
  class ::Object
7
11
  # Returns true if this object conforms to +protocol+, otherwise false.
8
12
  #
@@ -144,7 +148,7 @@ module Protocol
144
148
  # after the result of the wrapped method was determined.
145
149
  class Postcondition
146
150
  instance_methods.each do |m|
147
- m =~ /\A(__|instance_eval\Z|inspect\Z)/ or undef_method m
151
+ m.to_s =~ /\A(__|object_id|instance_eval\Z|inspect\Z)/ or undef_method m
148
152
  end
149
153
 
150
154
  def initialize(object)
@@ -204,8 +208,9 @@ module Protocol
204
208
  # Creates a Message instance named +name+, with the arity +arity+.
205
209
  # If +arity+ is nil, the arity isn't checked during conformity tests.
206
210
  def initialize(protocol, name, arity = nil, block_expected = false)
211
+ name = name.to_s
207
212
  @protocol, @name, @arity, @block_expected =
208
- protocol, name.to_s, arity, !!block_expected
213
+ protocol, name, arity, !!block_expected
209
214
  end
210
215
 
211
216
  # The protocol this message was defined in.
@@ -320,7 +325,7 @@ module Protocol
320
325
  (1..arity).map { |i| "x#{i}," }
321
326
  else
322
327
  (1..~arity).map { |i| "x#{i}," } << '*rest,'
323
- end
328
+ end.join
324
329
  wrapped_call = %{
325
330
  alias_method :'#{inner_name}', :'#{name}'
326
331
 
@@ -395,7 +400,7 @@ module Protocol
395
400
  " in method '#{name}' (#{check_arity} for #{arity}) of #{object}")
396
401
  end
397
402
  if block_expected?
398
- if object.singleton_methods(false).include?(name)
403
+ if object.singleton_methods(false).map { |m| m.to_s } .include?(name)
399
404
  parser = MethodParser.new(object, name, true)
400
405
  else
401
406
  ancestors = object.class.ancestors
@@ -446,90 +451,6 @@ module Protocol
446
451
  end
447
452
  end
448
453
 
449
- # Parse protocol method definition to derive a Message specification.
450
- class MethodParser < SexpProcessor
451
- # Create a new MethodParser instance for method +methodname+ of module
452
- # +modul+. For eigenmethods set +eigenclass+ to true, otherwise bad things
453
- # will happen.
454
- def initialize(modul, methodname, eigenclass = false)
455
- super()
456
- self.strict = false
457
- self.auto_shift_type = true
458
- @complex = false
459
- @block_arg = false
460
- @first_defn = true
461
- @first_block = true
462
- @args = []
463
- parsed = ParseTree.new.parse_tree_for_method(modul, methodname, eigenclass)
464
- process parsed
465
- end
466
-
467
- # Process +exp+, but catch UnsupportedNodeError exceptions and ignore them.
468
- def process(exp)
469
- super
470
- rescue UnsupportedNodeError => ignore
471
- end
472
-
473
- # Returns the names of the arguments of the parsed method.
474
- attr_reader :args
475
-
476
- # Returns the arity of the parsed method.
477
- def arity
478
- @args.size
479
- end
480
-
481
- # Return true if this protocol method is a complex method, which ought to
482
- # be called for checking conformance to the protocol.
483
- def complex?
484
- @complex
485
- end
486
-
487
- # Return true if a block argument was detected.
488
- def block_arg?
489
- @block_arg
490
- end
491
-
492
- # Only consider first the first defn, skip inner method definitions.
493
- def process_defn(exp)
494
- if @first_defn
495
- @first_defn = false
496
- _name, scope = exp
497
- process scope
498
- end
499
- exp.clear
500
- s :dummy
501
- end
502
-
503
- # Remember the argument names in +exp+ in the args attribute.
504
- def process_args(exp)
505
- @args.replace exp
506
- exp.clear
507
- s :dummy
508
- end
509
-
510
- # Remember if we encounter a block argument or a yield keyword.
511
- def process_block_arg(exp)
512
- @block_arg = true
513
- exp.clear
514
- s :dummy
515
- end
516
-
517
- alias process_yield process_block_arg
518
-
519
- # We only consider the first block in +exp+ (can there be more than one?),
520
- # and then try to figure out, if this is a complex method or not. Continue
521
- # processing the +exp+ tree after that.
522
- def process_block(exp)
523
- if @first_block
524
- @first_block = false
525
- @complex = exp[-1][0] != :nil rescue false
526
- exp.each { |e| process e }
527
- end
528
- exp.clear
529
- s :dummy
530
- end
531
- end
532
-
533
454
  # A ProtocolModule object
534
455
  class ProtocolModule < Module
535
456
  # Creates an new ProtocolModule instance.
@@ -546,7 +467,7 @@ module Protocol
546
467
  def descriptors
547
468
  descriptors = []
548
469
  protocols.each do |a|
549
- descriptors << a.instance_variable_get('@descriptor')
470
+ descriptors << a.instance_variable_get(:@descriptor)
550
471
  end
551
472
  descriptors
552
473
  end
@@ -578,8 +499,7 @@ module Protocol
578
499
  # have to call #reset_messages, if you want to recompute the array in the
579
500
  # next call to #messages.
580
501
  def messages
581
- @messages and return @messages
582
- @messages = []
502
+ result = []
583
503
  seen = {}
584
504
  descriptors.each do |d|
585
505
  dm = d.messages
@@ -588,10 +508,9 @@ module Protocol
588
508
  seen[m.name] = true
589
509
  delete
590
510
  end
591
- @messages.concat dm
511
+ result.concat dm
592
512
  end
593
- @messages.sort!
594
- @messages
513
+ result.sort!
595
514
  end
596
515
 
597
516
  alias to_a messages
@@ -820,8 +739,14 @@ module Protocol
820
739
  # This Method tries to find the first module that implements the method
821
740
  # named +methodname+ in the array of +ancestors+. If this fails nil is
822
741
  # returned.
823
- def find_method_module(methodname, ancestors) ancestors.each do |a|
824
- return a if a.instance_methods(false).include? methodname
742
+ def find_method_module(methodname, ancestors)
743
+ methodname = methodname.to_s
744
+ ancestors.each do |a|
745
+ begin
746
+ a.instance_method(methodname)
747
+ return a
748
+ rescue NameError
749
+ end
825
750
  end
826
751
  nil
827
752
  end
data/make_doc.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # vim: set et sw=2 ts=2:
3
2
 
4
3
  $outdir = 'doc/'
5
4
  puts "Creating documentation in '#$outdir'."
6
- system "rdoc --main=doc-main.txt -o #$outdir doc-main.txt lib/protocol.rb"
5
+ system "rdoc --main=doc-main.txt -o #$outdir doc-main.txt #{Dir['lib/**/*.rb'] * ' '}"
data/protocol.gemspec CHANGED
@@ -1,8 +1,8 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |s|
3
3
  s.name = 'protocol'
4
- s.version = '0.8.2'
5
- s.files = ["CHANGES", "COPYING", "Rakefile", "VERSION", "doc-main.txt", "examples", "examples/comparing.rb", "examples/enumerating.rb", "examples/game.rb", "examples/hello_world_patternitis.rb", "examples/indexing.rb", "examples/locking.rb", "examples/queue.rb", "examples/stack.rb", "install.rb", "lib", "lib/protocol", "lib/protocol.rb", "lib/protocol/core.rb", "lib/protocol/version.rb", "make_doc.rb", "protocol.gemspec", "tests", "tests/test_protocol.rb"]
4
+ s.version = '0.9.0'
5
+ s.files = ["CHANGES", "COPYING", "Rakefile", "VERSION", "benchmarks", "benchmarks/data", "benchmarks/method_parser.rb", "doc-main.txt", "examples", "examples/comparing.rb", "examples/enumerating.rb", "examples/game.rb", "examples/hello_world_patternitis.rb", "examples/indexing.rb", "examples/locking.rb", "examples/queue.rb", "examples/stack.rb", "install.rb", "lib", "lib/protocol", "lib/protocol.rb", "lib/protocol/core.rb", "lib/protocol/method_parser", "lib/protocol/method_parser/parse_tree.rb", "lib/protocol/method_parser/ruby_parser.rb", "lib/protocol/version.rb", "make_doc.rb", "protocol.gemspec", "tests", "tests/test_protocol.rb", "tests/test_protocol_method_parser.rb"]
6
6
  s.summary = 'Method Protocols for Ruby Classes'
7
7
  s.description = <<EOT
8
8
  This library offers an implementation of protocols against which you can check
@@ -14,6 +14,7 @@ EOT
14
14
 
15
15
  s.require_path = 'lib'
16
16
  s.add_dependency 'ParseTree', '~> 3.0'
17
+ s.add_dependency 'ruby_parser', '~> 2.0'
17
18
 
18
19
  s.has_rdoc = true
19
20
  s.rdoc_options << '--main' << 'doc-main.txt'
@@ -19,25 +19,25 @@ class TestProtocol < Test::Unit::TestCase
19
19
  end
20
20
 
21
21
  TestProtocol_foo_bar_1 = Protocol do
22
- include TestProtocol_foo
22
+ include TestProtocol::TestProtocol_foo
23
23
  understand :bar
24
24
  end
25
25
 
26
26
  TestProtocol_foo_bar_1_fail = Protocol do
27
- include TestProtocol_foo
27
+ include TestProtocol::TestProtocol_foo
28
28
  understand :bar
29
29
  end
30
30
 
31
31
  TestProtocol_foo_bar_2 = Protocol do
32
- include TestProtocol_foo
33
- include TestProtocol_bar
32
+ include TestProtocol::TestProtocol_foo
33
+ include TestProtocol::TestProtocol_bar
34
34
  end
35
35
 
36
36
  TestProtocol_foo_bar_2_fail = Protocol do
37
37
  check_failure :error
38
38
 
39
- include TestProtocol_foo
40
- include TestProtocol_bar
39
+ include TestProtocol::TestProtocol_foo
40
+ include TestProtocol::TestProtocol_bar
41
41
  end
42
42
 
43
43
  TestProtocolArgs = Protocol do
@@ -47,7 +47,7 @@ class TestProtocol < Test::Unit::TestCase
47
47
  end
48
48
 
49
49
  TestProtocolArgsOverwritten = Protocol do
50
- include TestProtocolArgs
50
+ include TestProtocol::TestProtocolArgs
51
51
 
52
52
  def bar(a, b, c)
53
53
  end
@@ -101,8 +101,8 @@ class TestProtocol < Test::Unit::TestCase
101
101
 
102
102
  TestProtocolWrapMethod = Protocol do
103
103
  def foo_bar(foo, bar)
104
- TestProtocolWrapMethodPassedFoo =~ foo
105
- TestProtocolWrapMethodPassedBar =~ bar
104
+ ::TestProtocol::TestProtocolWrapMethodPassedFoo =~ foo
105
+ ::TestProtocol::TestProtocolWrapMethodPassedBar =~ bar
106
106
  end
107
107
  end
108
108
 
@@ -128,7 +128,7 @@ class TestProtocol < Test::Unit::TestCase
128
128
  end
129
129
 
130
130
  TestProtocolInheritance = Protocol do
131
- inherit MyClass, :one_with_block
131
+ inherit TestProtocol::MyClass, :one_with_block
132
132
  end
133
133
 
134
134
  TestProtocolInheritanceC = Protocol do
@@ -617,4 +617,3 @@ class TestProtocol < Test::Unit::TestCase
617
617
  end
618
618
  end
619
619
  end
620
- # vim: set et sw=2 ts=2:
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'protocol'
5
+
6
+ class TestProtocolMethodParser < Test::Unit::TestCase
7
+ include Protocol
8
+
9
+ class A
10
+ def empty
11
+ end
12
+
13
+ def none()
14
+ end
15
+
16
+ def one_req(a)
17
+ end
18
+
19
+ def two_req(a, b)
20
+ end
21
+
22
+ def one_req_one_opt(a, b = nil)
23
+ end
24
+
25
+ def one_opt(a = nil)
26
+ end
27
+
28
+ def two_opt(a = nil, b = nil)
29
+ end
30
+
31
+ def one_req_rest(a, *b)
32
+ end
33
+
34
+ def one_opt_rest(a = nil, *b)
35
+ end
36
+
37
+ def block(&b)
38
+ end
39
+
40
+ def one_req_block(a, &b)
41
+ end
42
+
43
+ def one_opt_block(a = nil, &b)
44
+ end
45
+
46
+ def yield
47
+ yield
48
+ end
49
+
50
+ def yield_block(&b)
51
+ yield
52
+ end
53
+
54
+ def complex_end
55
+ a = :end
56
+ foo { }
57
+ end
58
+
59
+ def complex
60
+ foo { }
61
+ end
62
+ end
63
+
64
+ def test_args
65
+ m = :empty; mp = MethodParser.new(A, m)
66
+ assert_equal 0, mp.arity, "arity failed for A##{m}"
67
+ assert_equal [ ], mp.args, "args failed for A##{m}"
68
+ assert_equal [ ], mp.arg_kinds, "args failed for A##{m}"
69
+ assert !mp.complex?
70
+ m = :none; mp = MethodParser.new(A, m)
71
+ assert_equal 0, mp.arity, "arity failed for A##{m}"
72
+ assert_equal [ ], mp.args, "args failed for A##{m}"
73
+ assert_equal [ ], mp.arg_kinds, "args failed for A##{m}"
74
+ assert !mp.complex?
75
+ m = :one_req; mp = MethodParser.new(A, m)
76
+ assert_equal 1, mp.arity, "arity failed for A##{m}"
77
+ assert_equal [ :a ], mp.args, "args failed for A##{m}"
78
+ assert_equal [ :req ], mp.arg_kinds, "args failed for A##{m}"
79
+ assert !mp.complex?
80
+ m = :two_req; mp = MethodParser.new(A, m)
81
+ assert_equal 2, mp.arity, "arity failed for A##{m}"
82
+ assert_equal [ :req, :req ], mp.arg_kinds, "args failed for A##{m}"
83
+ assert_equal [ :a, :b ], mp.args, "args failed for A##{m}"
84
+ assert !mp.complex?
85
+ m = :one_req_one_opt; mp = MethodParser.new(A, m)
86
+ assert_equal -2, mp.arity, "arity failed for A##{m}"
87
+ assert_equal [ :a, :b ], mp.args, "args failed for A##{m}"
88
+ assert_equal [ :req, :opt ], mp.arg_kinds, "args failed for A##{m}"
89
+ assert !mp.complex?
90
+ m = :one_opt; mp = MethodParser.new(A, m)
91
+ assert_equal -1, mp.arity, "arity failed for A##{m}"
92
+ assert_equal [ :a ], mp.args, "args failed for A##{m}"
93
+ assert_equal [ :opt ], mp.arg_kinds, "args failed for A##{m}"
94
+ assert !mp.complex?
95
+ m = :two_opt; mp = MethodParser.new(A, m)
96
+ assert_equal -1, mp.arity, "arity failed for A##{m}"
97
+ assert_equal [ :a, :b ], mp.args, "args failed for A##{m}"
98
+ assert_equal [ :opt, :opt ], mp.arg_kinds, "args failed for A##{m}"
99
+ assert !mp.complex?
100
+ m = :one_req_rest; mp = MethodParser.new(A, m)
101
+ assert_equal -2, mp.arity, "arity failed for A##{m}"
102
+ assert_equal [ :a, :'*b' ], mp.args, "args failed for A##{m}"
103
+ assert_equal [ :req, :rest ], mp.arg_kinds, "args failed for A##{m}"
104
+ assert !mp.complex?
105
+ m = :one_opt_rest; mp = MethodParser.new(A, m)
106
+ assert_equal -1, mp.arity, "arity failed for A##{m}"
107
+ assert_equal [ :a, :'*b' ], mp.args, "args failed for A##{m}"
108
+ assert_equal [ :opt, :rest ], mp.arg_kinds, "args failed for A##{m}"
109
+ assert !mp.complex?
110
+ m = :block; mp = MethodParser.new(A, m)
111
+ assert_equal 0, mp.arity, "arity failed for A##{m}"
112
+ assert_equal [ :'&b' ], mp.args, "args failed for A##{m}"
113
+ assert_equal [ :block ], mp.arg_kinds, "args failed for A##{m}"
114
+ assert !mp.complex?
115
+ m = :one_req_block; mp = MethodParser.new(A, m)
116
+ assert_equal 1, mp.arity, "arity failed for A##{m}"
117
+ assert_equal [ :a, :'&b' ], mp.args, "args failed for A##{m}"
118
+ assert_equal [ :req, :block ], mp.arg_kinds, "args failed for A##{m}"
119
+ assert !mp.complex?
120
+ m = :one_opt_block; mp = MethodParser.new(A, m)
121
+ assert_equal -1, mp.arity, "arity failed for A##{m}"
122
+ assert_equal [ :a, :'&b' ], mp.args, "args failed for A##{m}"
123
+ assert_equal [ :opt, :block ], mp.arg_kinds, "args failed for A##{m}"
124
+ assert !mp.complex?
125
+ m = :yield_block; mp = MethodParser.new(A, m)
126
+ assert_equal 0, mp.arity, "arity failed for A##{m}"
127
+ assert_equal [ :'&b' ], mp.args, "args failed for A##{m}"
128
+ assert_equal [ :block ], mp.arg_kinds, "args failed for A##{m}"
129
+ assert !mp.complex?
130
+ m = :yield; mp = MethodParser.new(A, m)
131
+ assert_equal 0, mp.arity, "arity failed for A##{m}"
132
+ assert_equal [ :'&block' ], mp.args, "args failed for A##{m}"
133
+ assert_equal [ :block ], mp.arg_kinds, "args failed for A##{m}"
134
+ assert !mp.complex?
135
+ m = :complex_end; mp = MethodParser.new(A, m)
136
+ assert_equal 0, mp.arity, "arity failed for A##{m}"
137
+ assert_equal [ ], mp.args, "args failed for A##{m}"
138
+ assert_equal [ ], mp.arg_kinds, "args failed for A##{m}"
139
+ assert mp.complex?
140
+ m = :complex; mp = MethodParser.new(A, m)
141
+ assert_equal 0, mp.arity, "arity failed for A##{m}"
142
+ assert_equal [ ], mp.args, "args failed for A##{m}"
143
+ assert_equal [ ], mp.arg_kinds, "args failed for A##{m}"
144
+ assert mp.complex?
145
+ end
146
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protocol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-28 00:00:00 +02:00
12
+ date: 2009-08-11 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,6 +22,16 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: "3.0"
24
24
  version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: ruby_parser
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: "2.0"
34
+ version:
25
35
  description: This library offers an implementation of protocols against which you can check the conformity of your classes or instances of your classes. They are a bit like Java Interfaces, but as mixin modules they can also contain already implemented methods. Additionaly you can define preconditions/postconditions for methods specified in a protocol.
26
36
  email: flori@ping.de
27
37
  executables: []
@@ -35,6 +45,9 @@ files:
35
45
  - COPYING
36
46
  - Rakefile
37
47
  - VERSION
48
+ - benchmarks
49
+ - benchmarks/data
50
+ - benchmarks/method_parser.rb
38
51
  - doc-main.txt
39
52
  - examples
40
53
  - examples/comparing.rb
@@ -50,11 +63,15 @@ files:
50
63
  - lib/protocol
51
64
  - lib/protocol.rb
52
65
  - lib/protocol/core.rb
66
+ - lib/protocol/method_parser
67
+ - lib/protocol/method_parser/parse_tree.rb
68
+ - lib/protocol/method_parser/ruby_parser.rb
53
69
  - lib/protocol/version.rb
54
70
  - make_doc.rb
55
71
  - protocol.gemspec
56
72
  - tests
57
73
  - tests/test_protocol.rb
74
+ - tests/test_protocol_method_parser.rb
58
75
  has_rdoc: true
59
76
  homepage: http://protocol.rubyforge.org
60
77
  post_install_message: