protocol 0.8.0

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