protocol 0.8.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/Rakefile ADDED
@@ -0,0 +1,75 @@
1
+ # vim: set et sw=2 ts=2:
2
+
3
+ begin
4
+ require 'rake/gempackagetask'
5
+ rescue LoadError
6
+ end
7
+ require 'rbconfig'
8
+
9
+ include Config
10
+
11
+ PKG_NAME = 'protocol'
12
+ PKG_VERSION = File.read('VERSION').chomp
13
+ PKG_FILES = FileList['**/*'].exclude(/(CVS|\.svn|pkg|coverage)/)
14
+
15
+ desc "Installing library"
16
+ task :install do
17
+ ruby 'install.rb'
18
+ end
19
+
20
+ desc "Creating documentation"
21
+ task :doc do
22
+ ruby 'make_doc.rb'
23
+ end
24
+
25
+ desc "Testing library"
26
+ task :test do
27
+ ruby '-Ilib tests/test_protocol.rb'
28
+ end
29
+
30
+ desc "Testing library (coverage)"
31
+ task :coverage do
32
+ sh 'rcov -Ilib tests/test_protocol.rb'
33
+ end
34
+
35
+ desc "Clean created files"
36
+ task :clean do
37
+ rm_rf %w[doc coverage]
38
+ end
39
+
40
+ desc "Prepare a release"
41
+ task :release => [ :test, :clean, :package ]
42
+
43
+ if defined? Gem
44
+ spec = Gem::Specification.new do |s|
45
+ s.name = PKG_NAME
46
+ s.version = PKG_VERSION
47
+ s.files = PKG_FILES
48
+ s.summary = 'Method Protocols for Ruby Classes'
49
+ s.description = <<EOT
50
+ This library offers an implementation of protocols against which you can check
51
+ the conformity of your classes or instances of your classes. They are a bit
52
+ like Java Interfaces, but as mixin modules they can also contain already
53
+ implemented methods. Additionaly you can define preconditions/postconditions
54
+ for methods specified in a protocol.
55
+ EOT
56
+
57
+ s.require_path = 'lib'
58
+ s.add_dependency 'ParseTree', '>= 2.0.2'
59
+
60
+ s.has_rdoc = true
61
+ s.rdoc_options << '--title' << s.summary <<
62
+ '-S'
63
+ s.test_files << 'tests/test_protocol.rb'
64
+
65
+ s.author = "Florian Frank"
66
+ s.email = "flori@ping.de"
67
+ s.homepage = "http://protocol.rubyforge.org"
68
+ s.rubyforge_project = "protocol"
69
+ end
70
+
71
+ Rake::GemPackageTask.new(spec) do |pkg|
72
+ pkg.need_tar = true
73
+ pkg.package_files += PKG_FILES
74
+ end
75
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.0
@@ -0,0 +1,37 @@
1
+ require 'protocol/core'
2
+
3
+ class Person
4
+ def initialize(name, size)
5
+ @name, @size = name, size
6
+ end
7
+
8
+ attr_reader :name, :size
9
+
10
+ def <=>(other)
11
+ size <=> other.size
12
+ end
13
+
14
+ conform_to Comparing
15
+ end
16
+
17
+ goliath = Person.new 'Goliath', 2_60
18
+ david = Person.new 'David', 1_40
19
+ symbol = Person.new 'The artist formerly known as Prince', 1_40
20
+
21
+ david < goliath # => true
22
+ david >= goliath # => false
23
+ david == symbol # => true
24
+
25
+ begin
26
+ class NoPerson
27
+ def initialize(name, size)
28
+ @name, @size = name, size
29
+ end
30
+
31
+ attr_reader :name, :size
32
+
33
+ conform_to Comparing
34
+ end
35
+ rescue Protocol::CheckFailed => e
36
+ e.to_s # => "Comparing#<=>(1): method '<=>' not implemented in NoPerson"
37
+ end
@@ -0,0 +1,47 @@
1
+ require 'protocol/core'
2
+
3
+ begin
4
+ class FailAry
5
+ def initialize
6
+ @ary = [1, 2, 3]
7
+ end
8
+
9
+ def each()
10
+ end
11
+
12
+ conform_to Enumerating
13
+ end
14
+ rescue Protocol::CheckFailed => e
15
+ e.to_s # => "Enumerating#each(0&): expected a block argument for FailAry"
16
+ end
17
+
18
+ class Ary
19
+ def initialize
20
+ @ary = [1, 2, 3]
21
+ end
22
+
23
+ def each(&block)
24
+ @ary.each(&block)
25
+ end
26
+
27
+ conform_to Enumerating
28
+ end
29
+
30
+ Ary.new.map { |x| x * x } # => [1, 4, 9]
31
+ Ary.conform_to?(Enumerating) # => true
32
+ Ary.new.conform_to?(Enumerating) # => true
33
+
34
+ Enumerating.check_failure :none
35
+
36
+ class FailAry2
37
+ def initialize
38
+ @ary = [1, 2, 3]
39
+ end
40
+
41
+ def each() end
42
+
43
+ conform_to Enumerating
44
+ end
45
+
46
+ FailAry2.conform_to?(Enumerating) # => false
47
+ FailAry2.new.conform_to?(Enumerating) # => false
data/examples/game.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'protocol'
2
+
3
+ # Small example of the template method pattern with Protocol. (I think, it was
4
+ # inspired by this wikipedia article:
5
+ # http://en.wikipedia.org/wiki/Template_method_pattern
6
+ Gaming = Protocol do
7
+ # defaults to specification mode
8
+
9
+ # Initialize the game before entering the game loop.
10
+ def initialize_game()
11
+ end
12
+
13
+ implementation # switch to implemetation mode
14
+
15
+ attr_accessor :player_count
16
+
17
+ attr_accessor :current_player
18
+
19
+ specification # switch back to specification mode
20
+
21
+ # Make the next move with player +player+.
22
+ def make_move(player)
23
+ player.is_a? Fixnum and player >= 0 or
24
+ raise TypeError, "player #{player.class} is not a Fixnum >= 0"
25
+ end
26
+
27
+ # Return true if the game is over.
28
+ def game_over?()
29
+ end
30
+
31
+ # Output the winner of the game after +game_over?+ returned true.
32
+ def output_winner()
33
+ end
34
+
35
+ implementation # switch to implemetation mode again
36
+
37
+ # Play the game with +player_count+ players.
38
+ def play(player_count)
39
+ self.player_count = player_count
40
+ self.current_player = 0
41
+ initialize_game
42
+ loop do
43
+ make_move current_player
44
+ game_over? and break
45
+ self.current_player = (current_player + 1) % player_count
46
+ end
47
+ output_winner
48
+ end
49
+ end
50
+
51
+ class GuessGame
52
+ def initialize_game
53
+ @winner = nil
54
+ @move = 0
55
+ @secret_number = 1 + rand(10)
56
+ end
57
+
58
+ def make_move(player)
59
+ @move += 1
60
+ @guess = 1 + rand(10)
61
+ puts "#@move. Player ##{player}'s move: number = #{@guess}?"
62
+ end
63
+
64
+ def game_over?
65
+ if @guess == @secret_number
66
+ @winner = current_player
67
+ true
68
+ else
69
+ false
70
+ end
71
+ end
72
+
73
+ def output_winner
74
+ puts "The winner is player ##@winner, the secret number was #@secret_number!"
75
+ end
76
+
77
+ conform_to Gaming
78
+ end
79
+ game = GuessGame.new
80
+ game.play 2
81
+ game.play 3
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env ruby
2
+ # This example is loosely based on a joke posted by MillionthMonkey on
3
+ # slashdot: http://ask.slashdot.org/comments.pl?sid=250311&cid=19862863
4
+ # Nonetheless, it demonstrates some features of the protocol library pretty
5
+ # nicely.
6
+
7
+ require 'protocol'
8
+ require 'singleton'
9
+
10
+ MessageStrategyProtocol = Protocol do
11
+ def send_message() end
12
+ end
13
+
14
+ MessageBodyProtocol = Protocol do
15
+ attr_reader :payload
16
+
17
+ def configure(obj)
18
+ precondition { obj.respond_to? :to_str }
19
+ end
20
+
21
+ def send(message_strategy)
22
+ MessageStrategyProtocol =~ message_strategy
23
+ precondition { payload.respond_to? :to_str }
24
+ postcondition { result == :done }
25
+ end
26
+ end
27
+
28
+ class MessageBody
29
+ attr_reader :payload
30
+
31
+ def configure(obj)
32
+ @payload = obj
33
+ end
34
+
35
+ def send(message_strategy)
36
+ message_strategy.send_message
37
+ :done
38
+ end
39
+
40
+ conform_to MessageBodyProtocol
41
+ end
42
+
43
+ StrategyFactoryProtocol = Protocol do
44
+ def create_strategy(message_body)
45
+ MessageBodyProtocol =~ message_body
46
+ end
47
+ end
48
+
49
+ class DefaultFactory
50
+ include Singleton
51
+
52
+ def create_strategy(message_body)
53
+ Class.new do
54
+ define_method(:send_message) do ||
55
+ puts message_body.payload
56
+ end
57
+
58
+ conform_to MessageStrategyProtocol
59
+ end.new
60
+ end
61
+
62
+ conform_to StrategyFactoryProtocol
63
+ end
64
+
65
+ class HelloWorld
66
+ def self.main(*args)
67
+ message_body = MessageBody.new
68
+ message_body.configure "Hello World!"
69
+ factory = DefaultFactory.instance
70
+ strategy = factory.create_strategy message_body
71
+ message_body.send strategy
72
+ end
73
+ end
74
+
75
+ if $0 == __FILE__
76
+ HelloWorld.main(*ARGV)
77
+ end
@@ -0,0 +1,27 @@
1
+ require 'protocol'
2
+
3
+ Indexing = Protocol {
4
+ check_failure :error
5
+
6
+ understand :[]
7
+
8
+ understand :[]=
9
+ }
10
+
11
+ if $0 == __FILE__
12
+ class Array
13
+ conform_to Indexing
14
+ end
15
+
16
+ class Hash
17
+ conform_to Indexing
18
+ end
19
+
20
+ begin
21
+ class Proc
22
+ conform_to Indexing
23
+ end
24
+ rescue Protocol::CheckFailed => e
25
+ e.to_s # => "Indexing#[]=(): method '[]=' not implemented in Proc"
26
+ end
27
+ end
@@ -0,0 +1,111 @@
1
+ require 'protocol'
2
+
3
+ Locking = Protocol do
4
+ specification # not necessary, because Protocol defaults to specification
5
+ # mode already
6
+
7
+ def lock() end
8
+
9
+ def unlock() end
10
+
11
+ implementation
12
+
13
+ def synchronize
14
+ lock
15
+ begin
16
+ yield
17
+ ensure
18
+ unlock
19
+ end
20
+ end
21
+ end
22
+
23
+ if $0 == __FILE__
24
+ require 'thread'
25
+ require 'tempfile'
26
+
27
+ class FileMutex
28
+ def initialize
29
+ @tempfile = Tempfile.new 'file-mutex'
30
+ end
31
+
32
+ def path
33
+ @tempfile.path
34
+ end
35
+
36
+ def lock
37
+ puts "Locking '#{path}'."
38
+ @tempfile.flock File::LOCK_EX
39
+ end
40
+
41
+ def unlock
42
+ puts "Unlocking '#{path}'."
43
+ @tempfile.flock File::LOCK_UN
44
+ end
45
+
46
+ conform_to Locking
47
+ end
48
+
49
+ FileMutex.conform_to? Locking # => true
50
+ FileMutex.new.conform_to? Locking # => true
51
+
52
+ # Outputs something like:
53
+ # Locking '...'.
54
+ # Synchronized with '...'..
55
+ # Unlocking '...'.
56
+ mutex = FileMutex.new
57
+ mutex.synchronize do
58
+ puts "Synchronized with '#{mutex.path}'."
59
+ end
60
+
61
+ class MemoryMutex
62
+ def initialize
63
+ @mutex = Mutex.new
64
+ end
65
+
66
+ def lock
67
+ @mutex.lock
68
+ end
69
+
70
+ def unlock
71
+ @mutex.unlock
72
+ end
73
+
74
+ conform_to Locking # actually Mutex itself would conform as well ;)
75
+ end
76
+
77
+ mutex = MemoryMutex.new
78
+ mutex.synchronize do
79
+ puts "Synchronized in memory."
80
+ end
81
+
82
+ MemoryMutex.conform_to? Locking # => true
83
+ MemoryMutex.new.conform_to? Locking # => true
84
+
85
+ class MyClass
86
+ def initialize
87
+ @mutex = FileMutex.new
88
+ end
89
+
90
+ attr_reader :mutex
91
+
92
+ def mutex=(mutex)
93
+ Locking.check mutex
94
+ @mutex = mutex
95
+ end
96
+ end
97
+
98
+ obj = MyClass.new
99
+ obj.mutex # => #<FileMutex:0xb788f9ac @tempfile=#<File:/tmp/file-mutex.26553.2>>
100
+ begin
101
+ obj.mutex = Object.new
102
+ 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
+ end
105
+ obj.mutex = MemoryMutex.new # => #<MemoryMutex:0xb788f038 @mutex=#<Mutex:0xb788eea8>>
106
+ # This works as well:
107
+ obj.mutex = Mutex.new # => #<Mutex:0xb788ecc8>
108
+ Locking.check Mutex # => true
109
+ Mutex.conform_to? Locking # => true
110
+ 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"