dci 0.1.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/.ruby +43 -0
- data/.yardopts +7 -0
- data/COPYING.md +36 -0
- data/HISTORY.md +10 -0
- data/README.md +32 -0
- data/demo/account_example.md +74 -0
- data/demo/applique/dci.rb +1 -0
- data/lib/dci.rb +38 -0
- data/lib/dci/context.rb +89 -0
- data/lib/dci/object.rb +47 -0
- data/lib/dci/role.rb +53 -0
- metadata +82 -0
data/.ruby
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
source:
|
3
|
+
- Profile
|
4
|
+
authors:
|
5
|
+
- name: Thomas Sawyer
|
6
|
+
email: transfire@gmail.com
|
7
|
+
copyrights:
|
8
|
+
- holder: Thomas Sawyer
|
9
|
+
year: '2012'
|
10
|
+
license: BSD-2-Clause
|
11
|
+
replacements: []
|
12
|
+
alternatives: []
|
13
|
+
requirements:
|
14
|
+
- name: detroit
|
15
|
+
groups:
|
16
|
+
- build
|
17
|
+
development: true
|
18
|
+
- name: qed
|
19
|
+
groups:
|
20
|
+
- test
|
21
|
+
development: true
|
22
|
+
dependencies: []
|
23
|
+
conflicts: []
|
24
|
+
repositories:
|
25
|
+
- uri: git@github.com:rubyworks/dci.git
|
26
|
+
scm: git
|
27
|
+
name: upstream
|
28
|
+
resources:
|
29
|
+
home: https://rubyworks.github.com/dci
|
30
|
+
code: https://github.com/rubyworks/dci
|
31
|
+
mail: http://groups.google.com/groups/rubyworks-mailinglist
|
32
|
+
extra: {}
|
33
|
+
load_path:
|
34
|
+
- lib
|
35
|
+
revision: 0
|
36
|
+
version: 0.1.0
|
37
|
+
name: dci
|
38
|
+
title: DCI
|
39
|
+
summary: DCI for Ruby
|
40
|
+
created: '2011-03-04'
|
41
|
+
description: Faithful DCI framework for Ruby application development.
|
42
|
+
organization: Rubyworks
|
43
|
+
date: '2012-02-07'
|
data/.yardopts
ADDED
data/COPYING.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# COPYRIGHT
|
2
|
+
|
3
|
+
## NOTICES
|
4
|
+
|
5
|
+
### Assay
|
6
|
+
|
7
|
+
| Project | Assay |
|
8
|
+
|-----------|-----------------------------------|
|
9
|
+
| Copyright | (c) 2012 Rubyworks |
|
10
|
+
| License | (r) BSD-2-Clause |
|
11
|
+
| Website | http://rubyworks.github.com/assay |
|
12
|
+
|
13
|
+
## LICENSES
|
14
|
+
|
15
|
+
### BSD-2-Clause License
|
16
|
+
|
17
|
+
Redistribution and use in source and binary forms, with or without
|
18
|
+
modification, are permitted provided that the following conditions are met:
|
19
|
+
|
20
|
+
1. Redistributions of source code must retain the above copyright notice,
|
21
|
+
this list of conditions and the following disclaimer.
|
22
|
+
|
23
|
+
2. Redistributions in binary form must reproduce the above copyright
|
24
|
+
notice, this list of conditions and the following disclaimer in the
|
25
|
+
documentation and/or other materials provided with the distribution.
|
26
|
+
|
27
|
+
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
28
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
29
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
30
|
+
COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
31
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
32
|
+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
33
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
34
|
+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
35
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
36
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/HISTORY.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# DCI for Ruby
|
2
|
+
|
3
|
+
[Home](http://rubyworks.github.com/dci) /
|
4
|
+
[Code](http://github.com/rubyworks/dci) /
|
5
|
+
[Bugs](http://github.com/rubyworks/dci/issues) /
|
6
|
+
[Mail](http://groups.google.com/groups/rubyworks-mailinglist)
|
7
|
+
|
8
|
+
|
9
|
+
## Description
|
10
|
+
|
11
|
+
The DCI library for Ruby is a fairly faithful implementation of the DCI
|
12
|
+
concpets developed by Trygve Reenskaug, Reenskaug and James O. Coplien.
|
13
|
+
|
14
|
+
It define two reusable base classes, the Role and Context. The best way
|
15
|
+
to understand their usage is to look at the QED documentation provided
|
16
|
+
([for example](https://github.com/rubyworks/dci/blob/master/demo/account_example.md)).
|
17
|
+
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
The ususal RubyGems install procedure:
|
22
|
+
|
23
|
+
$ gem install dci
|
24
|
+
|
25
|
+
|
26
|
+
## Copyrights
|
27
|
+
|
28
|
+
Copyright (c) 2012 Rubyworks. All rights reserved.
|
29
|
+
|
30
|
+
DCI for Ruby is distributable under the terms of **BSD-2-Clause** license.
|
31
|
+
|
32
|
+
See COPYING.md file for license details.
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Account Balance Transfer
|
2
|
+
|
3
|
+
The Account Balance Transfre is the classic example of using DCI.
|
4
|
+
|
5
|
+
First we need our Data model. In the example that is the Account class.
|
6
|
+
To keep our example simple we will initialize new accounts with
|
7
|
+
a balance of $100. (We're generous like that.)
|
8
|
+
|
9
|
+
class Account
|
10
|
+
def initialize(account_id)
|
11
|
+
@account_id = account_id
|
12
|
+
@balance = 100
|
13
|
+
end
|
14
|
+
def account_id
|
15
|
+
@account_id
|
16
|
+
end
|
17
|
+
def available_balance
|
18
|
+
@balance
|
19
|
+
end
|
20
|
+
def increase_balance(amount)
|
21
|
+
@balance += amount
|
22
|
+
end
|
23
|
+
def decrease_balance(amount)
|
24
|
+
@balance -= amount
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
We set up two Roles, one role for withdrawing money from an account,
|
29
|
+
and one for depositing money into an account.
|
30
|
+
|
31
|
+
class Account::TransferWithdraw < Role
|
32
|
+
def transfer(amount)
|
33
|
+
decrease_balance(amount)
|
34
|
+
#log "Tranfered $#{amount} from account ##{account_id}."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Account::TransferDeposit < Role
|
39
|
+
def transfer(amount)
|
40
|
+
increase_balance(amount)
|
41
|
+
#log "Tranfered $#{amount} into account ##{account_id}."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Now we create a Context which will assign accounts to the roles
|
46
|
+
and used to perfomr the transfer.
|
47
|
+
|
48
|
+
# We can think of a context as setting a scene.
|
49
|
+
class Account::Transfer < Context
|
50
|
+
role :source_account => Account::TransferWithdraw
|
51
|
+
role :destination_account => Account::TransferDeposit
|
52
|
+
|
53
|
+
def initialize(source_account, destination_account)
|
54
|
+
self.source_account = source_account
|
55
|
+
self.destination_account = destination_account
|
56
|
+
end
|
57
|
+
|
58
|
+
def transfer(amount)
|
59
|
+
#log "Begin transfer."
|
60
|
+
roles.each{ |role| role.transfer(amount) }
|
61
|
+
#log "Transfer complete."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
Let's give it a try.
|
66
|
+
|
67
|
+
acct1 = Account.new(000100)
|
68
|
+
acct2 = Account.new(000200)
|
69
|
+
|
70
|
+
Account::Transfer.new(acct1, acct2).transfer(50)
|
71
|
+
|
72
|
+
acct1.available_balance #=> 50
|
73
|
+
acct2.available_balance #=> 150
|
74
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'dci'
|
data/lib/dci.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Data, Context and Interaction (DCI) is a paradigm used in computer software to
|
2
|
+
# program systems of communicating objects. Its goals are:
|
3
|
+
#
|
4
|
+
# * To improve the readability of object-oriented code by giving system behavior
|
5
|
+
# first-class status;
|
6
|
+
#
|
7
|
+
# * To cleanly separate code for rapidly changing system behavior (what the system does)
|
8
|
+
# from code for slowly changing domain knowledge (what the system is), instead
|
9
|
+
# of combining both in one class interface;
|
10
|
+
#
|
11
|
+
# * To help software developers reason about system-level state and behavior
|
12
|
+
# instead of only object state and behavior;
|
13
|
+
#
|
14
|
+
# * To support an object style of thinking that is close to peoples' mental
|
15
|
+
# models, rather than the class style of thinking that overshadowed object
|
16
|
+
# thinking early in the history of object-oriented programming languages.
|
17
|
+
#
|
18
|
+
# The paradigm separates the domain model (data) from use cases (context) and roles
|
19
|
+
# that objects play (interaction). DCI is complementary to model–view–controller (MVC).
|
20
|
+
# MVC as a pattern language is still used to separate the data and its processing from
|
21
|
+
# presentation.
|
22
|
+
#
|
23
|
+
# DCI was invented by Trygve Reenskaug, also the inventor of MVC. The current formulation
|
24
|
+
# of DCI is mostly the work of Reenskaug and James O. Coplien.
|
25
|
+
#
|
26
|
+
# acct1 = Account.new(10500)
|
27
|
+
# acct2 = Account.new(10010)
|
28
|
+
#
|
29
|
+
# Balance::Transfer.new(acct1, acct2).transfer(50)
|
30
|
+
#
|
31
|
+
module DCI
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'dci/object' # data
|
35
|
+
require 'dci/context' # context
|
36
|
+
require 'dci/role' # interation
|
37
|
+
|
38
|
+
|
data/lib/dci/context.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Context - The Context is the class (or its instance) whose code includes the roles
|
2
|
+
# for a given algorithm, scenario, or use case, as well as the code to map these
|
3
|
+
# roles into objects at run time and to enact the use case. Each role is bound
|
4
|
+
# to exactly one object during any given use case enactment; however, a single
|
5
|
+
# object may simultaneously play several roles. A context is instantiated at the
|
6
|
+
# beginning of the enactment of an algorithm, scenario, or use case. In summary,
|
7
|
+
# a Context comprises use cases and algorithms in which data objects are used
|
8
|
+
# through specific Roles.
|
9
|
+
#
|
10
|
+
# Each context represents one or more use cases. A context object is instantiated
|
11
|
+
# for each enactment of a use case for which it is responsible. Its main job
|
12
|
+
# is to identify the objects that will participate in the use case and to
|
13
|
+
# assign them to play the Roles which carry out the use case through their
|
14
|
+
# responsibilities. A role may comprise methods, and each method is some small
|
15
|
+
# part of the logic of an algorithm implementing a use case. Role methods run
|
16
|
+
# in the context of an object that is selected by the context to play that role
|
17
|
+
# for the current use case enactment. The role-to-object bindings that take place
|
18
|
+
# in a context can be contrasted with the polymorphism of vernacular object-oriented
|
19
|
+
# programming. The overall business functionality is the sum of complex, dynamic
|
20
|
+
# networks of methods decentralized in multiple contexts and their roles.
|
21
|
+
#
|
22
|
+
# Each context is a scope that includes identifiers that correspond to its roles.
|
23
|
+
# Any role executing within that context can refer to the other roles in that
|
24
|
+
# context through these identifiers. These identifiers have come to be called
|
25
|
+
# methodless roles. At use case enactment time, each and every one of these
|
26
|
+
# identifiers becomes bound to an object playing the corresponding Role for
|
27
|
+
# this Context.
|
28
|
+
#
|
29
|
+
# An example of a context could be a wire transfer between two accounts,
|
30
|
+
# where data models (the banking accounts) are used through roles named
|
31
|
+
# SourceAccount and # DestinationAccount.
|
32
|
+
#
|
33
|
+
# class Account::Transfer < Context
|
34
|
+
# role :source_account => Account::TransferWithdraw
|
35
|
+
# role :destination_account => Account::TransferDeposit
|
36
|
+
#
|
37
|
+
# def initialize(source_account, destination_account)
|
38
|
+
# self.source_account = source_account
|
39
|
+
# self.destination_account = destination_account
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# def transfer(amount)
|
43
|
+
# roles.each{ |role| role.transfer(amount) }
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
class Context
|
48
|
+
|
49
|
+
# Define a role given the name the role will use in this context,
|
50
|
+
# and the role class that is to be played.
|
51
|
+
#
|
52
|
+
def self.role(name_to_role)
|
53
|
+
name_to_role.each do |name, role|
|
54
|
+
define_method("role_#{name}"){ role }
|
55
|
+
|
56
|
+
module_eval %{
|
57
|
+
def #{name}=(data)
|
58
|
+
@#{name} = role_#{name}.new(data)
|
59
|
+
end
|
60
|
+
|
61
|
+
def #{name}
|
62
|
+
@#{name}
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# The default contructor can be used to assign roles via
|
69
|
+
# a settings hash.
|
70
|
+
#
|
71
|
+
def initialize(settings={})
|
72
|
+
settings.each do |k,v|
|
73
|
+
__send__("#{k}=", v)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns a list of all roles in the context.
|
78
|
+
#
|
79
|
+
# @todo Return value should probably be cached.
|
80
|
+
def roles
|
81
|
+
list = []
|
82
|
+
methods.each do |name|
|
83
|
+
next unless name.to_s.start_with?('role_')
|
84
|
+
list << __send__(name.to_s.sub('role_',''))
|
85
|
+
end
|
86
|
+
list
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
data/lib/dci/object.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Data - The data are "what the system is." The data part of the DCI architecture
|
2
|
+
# is its (relatively) static data model with relations. The data design is usually
|
3
|
+
# coded up as conventional classes that represent the basic domain structure of
|
4
|
+
# the system. These classes are barely smart data, and they explicitly lack the
|
5
|
+
# functionality that is peculiar to support of any particular use case. These
|
6
|
+
# classes commonly encapsulate the physical storage of the data. These data
|
7
|
+
# implement an information structure that comes from the mental model of end
|
8
|
+
# users, domain experts, programmers, and other people in the system. They may
|
9
|
+
# correspond closely to the model objects of MVC.
|
10
|
+
#
|
11
|
+
# An example of a data object could be a bank account. Its interface would have
|
12
|
+
# basic operations for increasing and decreasing the balance and for inquiring
|
13
|
+
# about the current balance. The interface would likely not offer operations that
|
14
|
+
# involve transactions, or which in any way involve other objects or any user
|
15
|
+
# interaction. So, for example, while a bank account may offer a primitive for
|
16
|
+
# increasing the balance, it would have no method called deposit. Such operations
|
17
|
+
# belong instead in the interaction part of DCI.
|
18
|
+
#
|
19
|
+
# Data objects are instances of classes that might come from domain-driven design,
|
20
|
+
# and such classes might use subtyping relationships to organize domain data.
|
21
|
+
# Though it reduces to classes in the end, DCI reflects a computational model
|
22
|
+
# dominated by object thinking rather than class thinking. Therefore, when
|
23
|
+
# thinking "data" in DCI, it means thinking more about the instances at run time
|
24
|
+
# than about the classes from which they were instantiated.
|
25
|
+
#
|
26
|
+
# class Account
|
27
|
+
# def initialize(accountId)
|
28
|
+
# @account_id = accountId
|
29
|
+
# @balance = 0
|
30
|
+
# end
|
31
|
+
# def account_id
|
32
|
+
# @account_id
|
33
|
+
# end
|
34
|
+
# def available_balance
|
35
|
+
# @balance
|
36
|
+
# end
|
37
|
+
# def increase_balance(amount)
|
38
|
+
# @balance += amount
|
39
|
+
# end
|
40
|
+
# def decrease_balance(amount)
|
41
|
+
# @balance -= amount
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# @todo Should objects track the roles in which they are presently particiapting?
|
46
|
+
class Object
|
47
|
+
end
|
data/lib/dci/role.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Interaction - The Interaction is "what the system does." The Interaction
|
2
|
+
# is implemented as Roles which are played by objects at run time. These objects
|
3
|
+
# combine the state and methods of a Data (domain) object with methods (but no
|
4
|
+
# state, as Roles are stateless) from one or more Roles. In good DCI style,
|
5
|
+
# a Role addresses another object only in terms of its (methodless) Role. There
|
6
|
+
# is a special Role called `@self` which binds to the object playing the current
|
7
|
+
# Role. Code within a Role method may invoke a method on `@self` and thereby
|
8
|
+
# invoke a method of the Data part of the current object. One curious aspect
|
9
|
+
# of DCI is that these bindings are guaranteed to be in place only at run time
|
10
|
+
# (using a variety of approaches and conventions; C++ templates can be used to
|
11
|
+
# guarantee that the bindings will succeed). This means that Interactions—the
|
12
|
+
# Role methods—are generic. In fact, some DCI implementations use generics or
|
13
|
+
# templates for Roles.
|
14
|
+
#
|
15
|
+
# A Role is a stateless programming construct that corresponds to the end user's
|
16
|
+
# mental model of some entity in the system. A Role represents a collection of
|
17
|
+
# responsibilities. Whereas vernacular object-oriented programming speaks of
|
18
|
+
# objects or classes as the loci of responsibilities, DCI ascribes them to Roles.
|
19
|
+
# An object participating in a use case has responsibilities: those that it takes
|
20
|
+
# on as a result of playing a particular Role.
|
21
|
+
#
|
22
|
+
# In the money transfer use case, for example, the role methods in the
|
23
|
+
# SourceAccount and DestinationAccount enact the actual transfer.
|
24
|
+
#
|
25
|
+
# class Account::TransferWithdraw < Role
|
26
|
+
# def transfer(amount)
|
27
|
+
# decrease_balance(amount)
|
28
|
+
# log "Tranfered from account #{account_id} $#{amount}"
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# class Account::TransferDepoit < Role
|
33
|
+
# def transfer(amount)
|
34
|
+
# increase_balance(amount)
|
35
|
+
# log "Tranfered into account #{account_id} $#{amount}"
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
class Role
|
40
|
+
|
41
|
+
#
|
42
|
+
def initialize(player)
|
43
|
+
@self = player
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# @todo Should use #public_send?
|
48
|
+
def method_missing(s, *a, &b)
|
49
|
+
@self.__send__(s, *a, &b)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dci
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Thomas Sawyer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: detroit
|
16
|
+
requirement: &12149120 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *12149120
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: qed
|
27
|
+
requirement: &12148360 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *12148360
|
36
|
+
description: Faithful DCI framework for Ruby application development.
|
37
|
+
email:
|
38
|
+
- transfire@gmail.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files:
|
42
|
+
- COPYING.md
|
43
|
+
- HISTORY.md
|
44
|
+
- README.md
|
45
|
+
files:
|
46
|
+
- .ruby
|
47
|
+
- .yardopts
|
48
|
+
- demo/account_example.md
|
49
|
+
- demo/applique/dci.rb
|
50
|
+
- lib/dci/context.rb
|
51
|
+
- lib/dci/object.rb
|
52
|
+
- lib/dci/role.rb
|
53
|
+
- lib/dci.rb
|
54
|
+
- COPYING.md
|
55
|
+
- HISTORY.md
|
56
|
+
- README.md
|
57
|
+
homepage: https://rubyworks.github.com/dci
|
58
|
+
licenses:
|
59
|
+
- BSD-2-Clause
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.8.11
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: DCI for Ruby
|
82
|
+
test_files: []
|