protocol 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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: