armin-joellenbeck-rdbc 0.0.4

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/README ADDED
@@ -0,0 +1,99 @@
1
+ =Design by Contract for Ruby
2
+
3
+ This library supports Design by Contract for Ruby.
4
+
5
+ =Installation
6
+
7
+ Install the *rdbc* gem itself by the following command:
8
+
9
+ $ gem install rdbc
10
+
11
+ =Usage
12
+
13
+ A a standard example for Design by Contract consider the following Stack
14
+ class and its contract StackContract.
15
+
16
+ ===stack.rb
17
+
18
+ :include:examples/stack.rb
19
+
20
+ ===stack_contract.rb
21
+
22
+ :include:examples/stack_contract.rb
23
+
24
+ The mechanics are really simple. There is the class Stack.
25
+ For instances of this class you can define a contract by subclassing
26
+ <b>DesignByContract::Contract</b>, in the example the contract is named
27
+ StackContract.
28
+ Then connect the class Stack to its contract StackContract by using the line
29
+ contract StackContract
30
+ in the class definition of Stack.
31
+
32
+ Now the following happens for an instance method of Stack. Say its name is
33
+ *method*. When it is called the the following calling of methods happens.
34
+
35
+ * *method_pre* of the contract is called with the parameters:
36
+ * the object itself
37
+ * the parameters of the original method *method*
38
+ * the original *method* is called
39
+ * *method_post* of the contract is called with the parameters:
40
+ * the object before the execution of the original method
41
+ * the object after execution the original method
42
+ * the return value of the original method
43
+ * the parameters of the original method
44
+ * *invariant* is called with the parameters:
45
+ * the object before the execution of the original method
46
+ * the object after execution the method
47
+
48
+ The object before the execution of *method* in 3. and 4.
49
+ is in fact a copy of the object before the execution. So you have to implement
50
+ the method *initialize_copy* in the class under contract, i.e. the class
51
+ Stack.
52
+
53
+ In any of these contract methods you can use assertions as in Test::Unit tests.
54
+ When an assertion fails, the exception Test::Unit::AssertionFailedError
55
+ is raised.
56
+
57
+ When the method is an operator the *_pre* and *_post* suffixes are appended
58
+ to the names of the operator according to the constant #Translate::OPERATOR.
59
+
60
+ =Support
61
+
62
+ The project home is at
63
+ GitHub[http://github.com/armin-joellenbeck/rdbc/tree/master]:
64
+ http://github.com/armin-joellenbeck/rdbc/tree/master
65
+
66
+ Feel free to send:
67
+ * bug reports
68
+ * support requests
69
+ * feature requests
70
+ * patches
71
+
72
+ =Copyright
73
+
74
+ Copyright (c) 2007 - 2008 by Armin Jöllenbeck
75
+ <armin@joellenbeck.net[mailto:armin@joellenbeck.net]>
76
+
77
+ All rights reserved.
78
+
79
+ Redistribution and use in source and binary forms, with or without
80
+ modification, are permitted provided that the following conditions
81
+ are met:
82
+ 1. Redistributions of source code must retain the above copyright
83
+ notice, this list of conditions and the following disclaimer.
84
+ 2. Redistributions in binary form must reproduce the above copyright
85
+ notice, this list of conditions and the following disclaimer in the
86
+ documentation and/or other materials provided with the distribution.
87
+ 3. The names of the contributors may not be used to endorse or promote products
88
+ derived from this software without specific prior written permission.
89
+
90
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
91
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
92
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
93
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
94
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
95
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
96
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
97
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
98
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
99
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'spec/rake/spectask'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
4
+
5
+
6
+ task :default => [:spec]
7
+
8
+
9
+ task :all => [:spec, :doc, :package]
10
+
11
+
12
+ Spec::Rake::SpecTask.new do |t|
13
+ t.libs << 'spec' << 'examples' << 'lib'
14
+ end
15
+
16
+
17
+ Rake::RDocTask.new(:doc) do |t|
18
+ t.rdoc_dir = 'doc'
19
+ t.rdoc_files.include('README', 'lib/**/*.rb')
20
+ t.options = [
21
+ '--all',
22
+ '--charset', 'utf8',
23
+ '--inline-source',
24
+ '--main', 'README',
25
+ '--title', 'Design by Contract for Ruby'
26
+ ]
27
+ end
28
+
29
+
30
+ Rake::GemPackageTask.new(eval(File.read('rdbc.gemspec'))) do |pkg|
31
+ end
data/examples/stack.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'stack_contract'
2
+
3
+ class Stack
4
+
5
+ contract StackContract
6
+
7
+ class NoPopForEmptyStack < RuntimeError
8
+ end
9
+
10
+ def initialize
11
+ @elements = []
12
+ end
13
+
14
+ def initialize_copy(orig)
15
+ @elements = orig.elements
16
+ end
17
+
18
+ def elements
19
+ @elements.dup
20
+ end
21
+
22
+ def size
23
+ @elements.size
24
+ end
25
+
26
+ def empty?
27
+ size == 0
28
+ end
29
+
30
+ def top
31
+ @elements.last
32
+ end
33
+
34
+ def push(element)
35
+ @elements.push(element)
36
+ nil
37
+ end
38
+
39
+ def pop
40
+ raise NoPopForEmptyStack if empty?
41
+ @elements.pop
42
+ end
43
+
44
+ end
@@ -0,0 +1,36 @@
1
+ require 'contract'
2
+
3
+ class StackContract < Contract
4
+
5
+ def push_post(stack_pre, stack, exception, result, element)
6
+ assert_nil(result)
7
+ assert_equal(element, stack.top)
8
+ assert_equal(stack.size, stack_pre.size + 1)
9
+ end
10
+
11
+ def pop_pre(stack)
12
+ assert_operator(stack.size, :>=, 0)
13
+ end
14
+
15
+ def pop_post(stack_pre, stack, exception, result)
16
+ if stack_pre.empty?
17
+ assert_kind_of(Stack::NoPopForEmptyStack, exception)
18
+ else
19
+ assert_equal(stack_pre.top, result)
20
+ assert_equal(stack_pre.size - 1, stack.size)
21
+ end
22
+ end
23
+
24
+ def top_pre(stack)
25
+ assert_operator(stack.size, :>=, 1)
26
+ end
27
+
28
+ def top_post(stack_pre, stack, exception, result)
29
+ assert_equal(stack_pre.size, stack.size)
30
+ end
31
+
32
+ def invariant(stack)
33
+ assert_operator(stack.size, :>=, 0)
34
+ end
35
+
36
+ end
data/lib/contract.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'test/unit/assertions'
2
+
3
+ class Contract
4
+ include Test::Unit::Assertions
5
+ end
6
+
7
+
8
+ require 'object'
9
+ require 'proxy'
10
+
11
+ class Class
12
+
13
+ def contract(klass)
14
+ old_new = self.method(:new)
15
+ self.define_singleton_method(:new) do |*args|
16
+ object = old_new.call(*args)
17
+ contract = klass.new
18
+ @proxy = Proxy.new(object, contract)
19
+ end
20
+ end
21
+
22
+ end
data/lib/object.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Object
2
+
3
+ def singleton_class
4
+ class << self
5
+ self
6
+ end
7
+ end
8
+
9
+ def define_singleton_method(method, &block)
10
+ singleton_class.send(:define_method, method, &block)
11
+ end
12
+
13
+ end
data/lib/proxy.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'translate'
2
+
3
+ class Proxy
4
+
5
+ def initialize(object, contract)
6
+ @object = object
7
+ @contract = contract
8
+ end
9
+
10
+ def method_missing(method, *args)
11
+ # clone the object under contract for use in the pre condition
12
+ object_pre = @object.clone
13
+
14
+ # call pre condition
15
+ method_pre = Translate.method_pre(method)
16
+ if @contract.respond_to?(method_pre)
17
+ @contract.send(method_pre, @object, *args)
18
+ end
19
+
20
+ # call the wrapped method
21
+ exception = nil
22
+ begin
23
+ result = @object.send(method, *args)
24
+ rescue => exception
25
+ end
26
+
27
+ # call the invariant condition
28
+ if @contract.respond_to?(:invariant)
29
+ @contract.invariant(@object)
30
+ end
31
+
32
+ # call the post condition
33
+ method_post = Translate.method_post(method)
34
+ if @contract.respond_to?(method_post)
35
+ @contract.send(method_post, object_pre, @object, exception, result, *args)
36
+ end
37
+
38
+ # return the return value of the wrapped method call or raise the exception
39
+ if exception.nil?
40
+ result
41
+ else
42
+ raise exception
43
+ end
44
+ end
45
+
46
+ end
data/lib/translate.rb ADDED
@@ -0,0 +1,56 @@
1
+ module Translate
2
+
3
+ OPERATOR = {
4
+ :[] => :element_read,
5
+ :[]= => :element_write,
6
+ :** => :power,
7
+ :~ => :not,
8
+ :+@ => :unary_plus,
9
+ :-@ => :unary_minus,
10
+ :* => :product,
11
+ :/ => :quotient,
12
+ :% => :modulo,
13
+ :+ => :plus,
14
+ :- => :minus,
15
+ :>> => :right_shift,
16
+ :<< => :left_shift,
17
+ :& => :and,
18
+ :^ => :xor,
19
+ :| => :or,
20
+ :<= => :less_or_equal,
21
+ :< => :less,
22
+ :> => :greater,
23
+ :>= => :greater_or_equal,
24
+ :<=> => :comparison,
25
+ :== => :eql,
26
+ :=== => :case_comparison,
27
+ :=~ => :match
28
+ }
29
+
30
+ def self.method_pre(method)
31
+ method_with_suffix(method, :pre)
32
+ end
33
+
34
+ def self.method_post(method)
35
+ method_with_suffix(method, :post)
36
+ end
37
+
38
+ def self.method_with_suffix(method, type)
39
+ operator = OPERATOR[method]
40
+ suffix = '_' + type.to_s
41
+ if operator.nil?
42
+ method_string = method.to_s
43
+ length = method_string.length
44
+ head = method_string[0...length-1]
45
+ tail = method_string[length-1...length]
46
+ if ['?', '!', '='].include?(tail)
47
+ (head + suffix + tail).to_sym
48
+ else
49
+ (method_string + suffix).to_sym
50
+ end
51
+ else
52
+ ('op_' + operator.to_s + suffix).to_sym
53
+ end
54
+ end
55
+
56
+ end
data/rdbc.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'rdbc'
3
+ s.version = '0.0.4'
4
+ s.summary = 'Design by Contract for Ruby.'
5
+ s.author = 'Armin Joellenbeck'
6
+ s.email = 'armin@joellenbeck.net'
7
+ s.homepage = 'http://github.com/armin-joellenbeck/rdbc/tree/master'
8
+ s.description = <<-EOF
9
+ EOF
10
+ s.files = Dir.glob([
11
+ 'README',
12
+ 'Rakefile',
13
+ '*.gemspec',
14
+ 'examples/**/*',
15
+ 'lib/**/*',
16
+ 'spec/**/*'
17
+ ])
18
+ s.has_rdoc = true
19
+ s.extra_rdoc_files << 'README'
20
+ s.rdoc_options = [
21
+ '--all',
22
+ '--charset', 'utf8',
23
+ '--main', 'README',
24
+ '--title', 'Design by Contract for Ruby'
25
+ ]
26
+ end
@@ -0,0 +1,4 @@
1
+ require 'contract'
2
+
3
+ describe Contract do
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'object'
2
+
3
+ describe Object do
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'proxy'
2
+
3
+ describe Proxy do
4
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format
3
+ specdoc
@@ -0,0 +1,4 @@
1
+ require 'stack_contract'
2
+
3
+ describe StackContract do
4
+ end
@@ -0,0 +1,30 @@
1
+ require 'stack'
2
+
3
+ describe Stack do
4
+
5
+ before do
6
+ @stack = Stack.new
7
+ end
8
+
9
+ it 'should be empty' do
10
+ @stack.should be_empty
11
+ end
12
+
13
+ it 'should have no element on top' do
14
+ lambda { @stack.top }.should raise_error(Test::Unit::AssertionFailedError)
15
+ end
16
+
17
+ it 'should not be possible to pop an element' do
18
+ lambda { @stack.pop }.should raise_error(Stack::NoPopForEmptyStack)
19
+ end
20
+
21
+ it 'a pushed element should be on the top' do
22
+ element = 0
23
+ @stack.push(element)
24
+ @stack.should_not be_empty
25
+ @stack.top.should be_equal(element)
26
+ @stack.pop.should be_equal(element)
27
+ @stack.should be_empty
28
+ end
29
+
30
+ end
@@ -0,0 +1,45 @@
1
+ require 'translate'
2
+
3
+ describe Translate do
4
+
5
+ it 'should give the pre method for a normal method' do
6
+ Translate.method_pre(:method).should == :method_pre
7
+ end
8
+
9
+ it 'should give the pre method for a question method' do
10
+ Translate.method_pre(:method?).should == :method_pre?
11
+ end
12
+
13
+ it 'should give the pre method for an exclamation method' do
14
+ Translate.method_pre(:method!).should == :method_pre!
15
+ end
16
+
17
+ it 'should give the pre method for an assignment method' do
18
+ Translate.method_pre(:method=).should == :method_pre=
19
+ end
20
+
21
+ it 'should give the pre method for an operator' do
22
+ Translate.method_pre(:+).should == :op_plus_pre
23
+ end
24
+
25
+ it 'should give the post method for a normal method' do
26
+ Translate.method_post(:method).should == :method_post
27
+ end
28
+
29
+ it 'should give the post method for a question method' do
30
+ Translate.method_post(:method?).should == :method_post?
31
+ end
32
+
33
+ it 'should give the post method for an exclamation method' do
34
+ Translate.method_post(:method!).should == :method_post!
35
+ end
36
+
37
+ it 'should give the post method for an assignment method' do
38
+ Translate.method_post(:method=).should == :method_post=
39
+ end
40
+
41
+ it 'should give the post method for an operator' do
42
+ Translate.method_post(:+).should == :op_plus_post
43
+ end
44
+
45
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: armin-joellenbeck-rdbc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Armin Joellenbeck
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-08-13 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: ""
17
+ email: armin@joellenbeck.net
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - README
26
+ - Rakefile
27
+ - rdbc.gemspec
28
+ - examples/stack_contract.rb
29
+ - examples/stack.rb
30
+ - lib/contract.rb
31
+ - lib/translate.rb
32
+ - lib/object.rb
33
+ - lib/proxy.rb
34
+ - spec/translate_spec.rb
35
+ - spec/object_spec.rb
36
+ - spec/proxy_spec.rb
37
+ - spec/spec.opts
38
+ - spec/contract_spec.rb
39
+ - spec/stack_contract_spec.rb
40
+ - spec/stack_spec.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/armin-joellenbeck/rdbc/tree/master
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --all
46
+ - --charset
47
+ - utf8
48
+ - --main
49
+ - README
50
+ - --title
51
+ - Design by Contract for Ruby
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.2.0
70
+ signing_key:
71
+ specification_version: 2
72
+ summary: Design by Contract for Ruby.
73
+ test_files: []
74
+