armin-joellenbeck-rdbc 0.0.4

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