drsi 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +100 -0
- data/Rakefile +2 -0
- data/drsi.gemspec +25 -0
- data/examples/money_transfer.rb +80 -0
- data/lib/drsi/dci/context.rb +170 -0
- data/lib/drsi/dci/role.rb +18 -0
- data/lib/drsi/module.rb +28 -0
- data/lib/drsi/object.rb +6 -0
- data/lib/drsi/rolable.rb +66 -0
- data/lib/drsi/version.rb +3 -0
- data/lib/drsi.rb +6 -0
- data/spec/context_spec.rb +117 -0
- data/spec/interaction_spec.rb +48 -0
- data/spec/players_spec.rb +83 -0
- data/spec/role_spec.rb +33 -0
- data/spec/roleplayers_spec.rb +107 -0
- data/spec/spec_helper.rb +12 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2e2a89f6f00a413fd3ae85e494e75e0f4bcc4aac
|
4
|
+
data.tar.gz: b44a051e6678c13077ae65a55e8c11ba36530128
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: abd2d518e9b8952cfff3d150e323e0c440529aadf51c5dc2d2df388c8e93353d86bdd542faeaaeb276635ca503ce1cfa48b7bc8a43f1a8c94fa3facf18a7fb40
|
7
|
+
data.tar.gz: eae8418a3b0e7949b569cc73a510998d6a7a358ce4836d1548eb5c713b31ee0b79fca8c69cf51b7641c93bfa72c41e547b9edf2bc5393108e7faf01b487ec7aa
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
drsi
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.0
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Lorenzo Tello
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# drsi
|
2
|
+
|
3
|
+
**_Trygve Reenskaug_**, the parent of MVC, proposes an evolution to traditional OO paradigm: [The DCI Architecture: A New Vision of Object-Oriented Programming](http://www.artima.com/articles/dci_vision.html "The DCI Architecture: A New Vision of Object-Oriented Programming").
|
4
|
+
|
5
|
+
This gem makes Data-Context-Interaction paradigm ready to be used in your Ruby application. See also [Data Context Interaction: The Evolution of the Object Oriented Paradigm](http://rubysource.com/dci-the-evolution-of-the-object-oriented-paradigm/ "Data Context Interaction: The Evolution of the Object Oriented Paradigm").
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Install as usual, either with rubygems
|
10
|
+
|
11
|
+
gem install drsi
|
12
|
+
|
13
|
+
or including it in your Gemfile and running bundle install:
|
14
|
+
|
15
|
+
# Gemfile
|
16
|
+
gem "drsi"
|
17
|
+
|
18
|
+
$ bundle install
|
19
|
+
|
20
|
+
_**Note**: only **ruby 2.1+** compatible._
|
21
|
+
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
dci-ruby gives you the class DCI::Context to inherit from to create your own contexts:
|
26
|
+
|
27
|
+
class MoneyTransfer < DCI::Context
|
28
|
+
|
29
|
+
# Roles
|
30
|
+
|
31
|
+
role :source_account do
|
32
|
+
def transfer(amount)
|
33
|
+
self.balance -= amount
|
34
|
+
target_account.get_transfer(amount)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
role :target_account do
|
39
|
+
def get_transfer(amount)
|
40
|
+
self.balance += amount
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Interactions
|
46
|
+
|
47
|
+
def run(amount = settings(:amount))
|
48
|
+
source_account.transfer(amount)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Every context defines some roles to be played by external objects (players) and their interactions. This way
|
53
|
+
you have all the agents and operations in a use case wrapped in just one entity instead of spread out throughout the
|
54
|
+
application code.
|
55
|
+
|
56
|
+
Use the defined contexts, instantiating them, wherever you need in your code:
|
57
|
+
|
58
|
+
MoneyTransfer.new(:source_account => Account.new(1),
|
59
|
+
:target_account => Account.new(2)).run(100)
|
60
|
+
|
61
|
+
or the short preferred way:
|
62
|
+
|
63
|
+
MoneyTransfer[:source_account => Account.new(1),
|
64
|
+
:target_account => Account.new(2),
|
65
|
+
:amount => 100]
|
66
|
+
|
67
|
+
Inside a context instance, every player object incorporates the behaviour (methods) defined by its role while keeping its own.
|
68
|
+
|
69
|
+
The Account instances above are players. They are accesible inside #run through #source_account and #target_account private methods.
|
70
|
+
Also, every role player has private access to the rest of role players in the context.
|
71
|
+
|
72
|
+
Unlike the Presenter approach in dci-ruby (where the object to play a role and the one inside the context playing it are associated but different), this extending/unextending approach preserves unique identity of objects playing roles.
|
73
|
+
|
74
|
+
When instanciating a Context, the extra no-role pairs given as arguments are read-only attributes accessible via #settings:
|
75
|
+
|
76
|
+
MoneyTransfer[:source_account => Account.new(1),
|
77
|
+
:target_account => Account.new(2),
|
78
|
+
:amount => 500]
|
79
|
+
|
80
|
+
here, :amount is not a player (has no associated role) but is still privately accessible both in the interactions and the roles
|
81
|
+
via #settings(:amount).
|
82
|
+
|
83
|
+
|
84
|
+
See the [examples](https://github.com/ltello/drsi/tree/master/examples) folder for examples of use and the [drsi-DCI-Sample](https://github.com/ltello/drsi-DCI-Sample) repository for a sample Rails application using DCI through this gem.
|
85
|
+
|
86
|
+
Notice how your models and controllers are not overloaded anymore. They are thinner and simpler.
|
87
|
+
Also note how now most of the functionality of the system is isolated, totally dry-ied and easily maintainable in the different context classes.
|
88
|
+
|
89
|
+
## Contributing
|
90
|
+
|
91
|
+
1. Fork it
|
92
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
93
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
94
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
95
|
+
5. Create new Pull Request
|
96
|
+
|
97
|
+
|
98
|
+
## Copyright
|
99
|
+
|
100
|
+
Copyright (c) 2012, 2013 Lorenzo Tello. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
data/drsi.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/drsi/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "drsi"
|
6
|
+
gem.version = Drsi::VERSION
|
7
|
+
gem.authors = ["Lorenzo Tello"]
|
8
|
+
gem.email = ["ltello8a@gmail.com"]
|
9
|
+
gem.homepage = "http://github.com/ltello/drsi"
|
10
|
+
gem.description = "Make DCI paradigm available to Ruby applications"
|
11
|
+
gem.summary = "Make DCI paradigm available to Ruby applications by enabling developers defining contexts subclassing the class DCI::Context. You define roles inside the definition. Match roles and player objects in context instantiation. Single Identity approach."
|
12
|
+
gem.licenses = ["MIT"]
|
13
|
+
|
14
|
+
gem.rubyforge_project = "drsi"
|
15
|
+
|
16
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
gem.files = `git ls-files`.split("\n")
|
18
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
gem.add_development_dependency "rspec", "~> 2.0"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'drsi'
|
2
|
+
|
3
|
+
|
4
|
+
class CheckingAccount
|
5
|
+
attr_reader :account_id, :currency
|
6
|
+
attr_accessor :balance
|
7
|
+
|
8
|
+
def initialize(account_id, initial_balance)
|
9
|
+
@account_id = account_id
|
10
|
+
b, @currency = initial_balance.split(' ')
|
11
|
+
@balance = b.to_i
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Amount
|
16
|
+
attr_reader :quantity, :currency
|
17
|
+
|
18
|
+
def initialize(data)
|
19
|
+
q, @currency = data.split(' ')
|
20
|
+
@quantity = q.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"#{quantity}#{currency}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
class MoneyTransferContext < DCI::Context
|
30
|
+
|
31
|
+
# Roles Definitions
|
32
|
+
|
33
|
+
role :source_account do
|
34
|
+
def run_transfer
|
35
|
+
self.balance -= amount.quantity
|
36
|
+
puts "\t\tAccount(\##{account_id}) sent #{amount} to Account(\##{target_account.account_id})."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
role :target_account do
|
41
|
+
def run_transfer
|
42
|
+
self.balance += amount.quantity
|
43
|
+
puts "\t\tAccount(\##{account_id}) received #{amount} from Account(\##{source_account.account_id})."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
role :amount
|
48
|
+
|
49
|
+
|
50
|
+
# Interactions
|
51
|
+
|
52
|
+
def run
|
53
|
+
puts "\nMoney Transfer of #{amount} between Account(\##{source_account.account_id}) and Account(\##{target_account.account_id})"
|
54
|
+
puts "\tBalances Before: #{balances}"
|
55
|
+
source_account.run_transfer
|
56
|
+
target_account.run_transfer
|
57
|
+
puts "\tBalances After: #{balances}"
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def accounts
|
64
|
+
[source_account, target_account]
|
65
|
+
end
|
66
|
+
|
67
|
+
def balances
|
68
|
+
accounts.map {|account| "#{account.balance}#{account.currency}"}.join(' - ')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
acc1 = CheckingAccount.new(1, '1000 €')
|
73
|
+
acc2 = CheckingAccount.new(2, '0 €')
|
74
|
+
amount = Amount.new('200 €')
|
75
|
+
|
76
|
+
5.times do
|
77
|
+
MoneyTransferContext.new(:source_account => acc1,
|
78
|
+
:target_account => acc2,
|
79
|
+
:amount => amount).run
|
80
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'drsi/dci/role'
|
2
|
+
|
3
|
+
module DCI
|
4
|
+
class Context
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# Every subclass of Context has is own class and instance method roles defined.
|
9
|
+
# The instance method delegates value to the class.
|
10
|
+
def inherited(subklass)
|
11
|
+
subklass.class_eval do
|
12
|
+
@roles ||= {}
|
13
|
+
def self.roles; @roles end
|
14
|
+
def roles; self.class.roles end
|
15
|
+
private :roles
|
16
|
+
assign_unplay_roles_within_klass_instance_methods!(subklass)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# A short way for ContextSubclass.new(players_and_extra_args).run(extra_args)
|
21
|
+
def [](*args)
|
22
|
+
new(*args).run
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# The macro role is defined to allow a subclass of Context to define roles in its definition body.
|
29
|
+
# Every new role is added to the hash of roles in that Context subclass.
|
30
|
+
# A reader to access the object playing the new role is also defined and available in every instance of the context subclass.
|
31
|
+
# Also, readers to allow each other role access are defined.
|
32
|
+
def role(rolekey, &block)
|
33
|
+
raise "role name must be a symbol" unless rolekey.is_a?(Symbol)
|
34
|
+
create_role_from(rolekey, &block)
|
35
|
+
define_reader_for_role(rolekey)
|
36
|
+
define_mate_roleplayers_readers_after_newrole(rolekey)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Adds a new entry to the roles accumulator hash.
|
40
|
+
def create_role_from(key, &block)
|
41
|
+
roles.merge!(key => create_role_module_from(key, &block))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Defines and return a new subclass of DCI::Role named after the given rolekey and with body the given block.
|
45
|
+
def create_role_module_from(rolekey, &block)
|
46
|
+
new_mod_name = rolekey.to_s.split(/\_+/).map(&:capitalize).join('')
|
47
|
+
const_set(new_mod_name, Module.new(&block))
|
48
|
+
const_get(new_mod_name).tap {|mod| mod.send(:extend, ::DCI::Role)}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Defines a private reader to allow a context instance access to the roleplayer object associated to the given rolekey.
|
52
|
+
def define_reader_for_role(rolekey)
|
53
|
+
private
|
54
|
+
attr_reader rolekey
|
55
|
+
end
|
56
|
+
|
57
|
+
# After a new role is defined, you've got to create a reader method for this new role in the rest of context
|
58
|
+
# roles, and viceverse: create a reader method in the new role mod for each of the other roles in the context.
|
59
|
+
# This method does exactly this.
|
60
|
+
def define_mate_roleplayers_readers_after_newrole(new_rolekey)
|
61
|
+
new_role_mod = roles[new_rolekey]
|
62
|
+
mate_roles = mate_roles_of(new_rolekey)
|
63
|
+
mate_roles.each do |mate_rolekey, mate_role_mod|
|
64
|
+
mate_role_mod.send(:add_role_reader_for!, new_rolekey)
|
65
|
+
new_role_mod.send(:add_role_reader_for!, mate_rolekey)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# For a give role key, returns a hash with the rest of the roles (pair :rolekey => role_mod) in the context it belongs to.
|
70
|
+
def mate_roles_of(rolekey)
|
71
|
+
roles.dup.tap do |roles|
|
72
|
+
roles.delete(rolekey)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Wraps every existing public/protected instance_method of klass (not superclasses methods) to assign roles to
|
77
|
+
# player objects before the execution and to un-assign roles from player objects at the end of execution just
|
78
|
+
# before returning control.
|
79
|
+
# Also inject code to magically do the same to every new method defined in klass.
|
80
|
+
def assign_unplay_roles_within_klass_instance_methods!(klass)
|
81
|
+
klass.instance_methods(false).each do |existing_methodname|
|
82
|
+
assign_unplay_roles_within_klass_instance_method!(klass, existing_methodname)
|
83
|
+
end
|
84
|
+
def klass.method_added(methodname)
|
85
|
+
if not @context_internals and public_method_defined?(methodname)
|
86
|
+
@context_internals = true
|
87
|
+
assign_unplay_roles_within_klass_instance_method!(self, methodname)
|
88
|
+
@context_internals = false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Wraps the given klass's methodname to assign/un-assign roles to player objects before and after actual method
|
94
|
+
# execution.
|
95
|
+
def assign_unplay_roles_within_klass_instance_method!(klass, methodname)
|
96
|
+
klass.class_eval do
|
97
|
+
method_object = instance_method(methodname)
|
98
|
+
define_method(methodname) do |*args, &block|
|
99
|
+
players_play_role!
|
100
|
+
method_object.bind(self).call(*args, &block).tap {players_unplay_role!}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# Instances of a defined subclass of Context are initialized checking first that all subclass defined roles
|
109
|
+
# are provided in the creation invocation raising an error if any of them is missing.
|
110
|
+
# Once the previous check is met, every object playing in the context instance is associated to the stated role.
|
111
|
+
# Non players args are associated to instance_variables and readers defined.
|
112
|
+
def initialize(args={})
|
113
|
+
check_all_roles_provided_in!(args)
|
114
|
+
players, noplayers = args.partition {|key, *| roles.has_key?(key)}.map {|group| Hash[*group.flatten]}
|
115
|
+
@_players = players
|
116
|
+
@settings = noplayers
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# Private access to the extra args received in the instantiation.
|
123
|
+
# Returns a hash (copy of the instantiation extra args) with only the args included in 'keys' or all of them
|
124
|
+
# when called with no args.
|
125
|
+
def settings(*keys)
|
126
|
+
return @settings.dup if keys.empty?
|
127
|
+
entries = @settings.reject {|k, v| !keys.include?(k)}
|
128
|
+
keys.size == 1 ? entries.values.first : entries
|
129
|
+
end
|
130
|
+
|
131
|
+
# Checks there is a player for each role.
|
132
|
+
# Raises and error message in case of missing roles.
|
133
|
+
def check_all_roles_provided_in!(players={})
|
134
|
+
missing_rolekeys = missing_roles(players)
|
135
|
+
raise "missing roles #{missing_rolekeys}" unless missing_rolekeys.empty?
|
136
|
+
end
|
137
|
+
|
138
|
+
# The list of roles with no player provided
|
139
|
+
def missing_roles(players={})
|
140
|
+
(roles.keys - players.keys)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Associates every role to the intended player.
|
144
|
+
def players_play_role!
|
145
|
+
roles.keys.each do |rolekey|
|
146
|
+
assign_role_to_player!(rolekey, @_players[rolekey])
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Associates a role to an intended player:
|
151
|
+
# - The player object is 'extended' with the methods of the role to play.
|
152
|
+
# - The player get access to the context it is playing.
|
153
|
+
# - The player get access to the rest of players in its context through instance methods named after their role keys.
|
154
|
+
# - This context instance get access to this new role player through an instance method named after the role key.
|
155
|
+
def assign_role_to_player!(rolekey, player)
|
156
|
+
role_mod = roles[rolekey]
|
157
|
+
player.__play_role!(role_mod, self)
|
158
|
+
instance_variable_set(:"@#{rolekey}", player)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Disassociates every role from the playing object.
|
162
|
+
def players_unplay_role!
|
163
|
+
roles.keys.each do |rolekey|
|
164
|
+
@_players[rolekey].__unplay_last_role!
|
165
|
+
# 'instance_variable_set(:"@#{rolekey}", nil)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module DCI
|
2
|
+
module Role
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def context
|
7
|
+
raise 'This method must be redefined in every module including DCI::Role'
|
8
|
+
end
|
9
|
+
|
10
|
+
# Defines a new private reader instance method for a context mate role, delegating it to the context object.
|
11
|
+
def add_role_reader_for!(rolekey)
|
12
|
+
return if private_method_defined?(rolekey)
|
13
|
+
private
|
14
|
+
define_method(rolekey) {context.send(rolekey)}
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
data/lib/drsi/module.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
# Define instance methods delegating execution to the corresponding ones in 'mod'.
|
4
|
+
def __copy_instance_methods_from(mod)
|
5
|
+
[:public_instance_methods, :protected_instance_methods, :private_instance_methods].each do |methods_type|
|
6
|
+
methods = mod.send(methods_type, false).map {|methodname| mod.instance_method(methodname)}
|
7
|
+
type = methods_type.to_s.split('_').first.to_sym
|
8
|
+
__add_instance_methods(methods, type)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Define instance methods binding to self the unbound ones received in 'methods'.
|
16
|
+
# Also, set their visibility from 'type' (:public, :protected, :private).
|
17
|
+
def __add_instance_methods(methods, type)
|
18
|
+
module_exec(methods, type) do |methods, type|
|
19
|
+
methods.each do |method|
|
20
|
+
define_method(method.name) do |*args, &block|
|
21
|
+
method.bind(self).call(*args, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
send(type, *methods.map(&:name))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/lib/drsi/object.rb
ADDED
data/lib/drsi/rolable.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'drsi/module'
|
2
|
+
|
3
|
+
# This module defines a mechanism to extend and 'unextend' modules in an object.
|
4
|
+
#
|
5
|
+
# The idea is to provide an object with a heap of extended modules:
|
6
|
+
# the highest ones filled with the methods associated to the roles the object currently plays,
|
7
|
+
# and the lowest ones, clean (no methods) when the object finishes playing roles.
|
8
|
+
# reusing the empty ones or adding and extending new ones when it is needed.
|
9
|
+
module Rolable
|
10
|
+
|
11
|
+
# Make an object play the role defined as a module in 'mod'
|
12
|
+
def __play_role!(role_klass, context)
|
13
|
+
new_role = __next_empty_role
|
14
|
+
new_role.__copy_instance_methods_from(role_klass)
|
15
|
+
new_role.send(:define_method, :context) {context}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Make an object stop playing the last role it plays, if any.
|
19
|
+
def __unplay_last_role!
|
20
|
+
if role = __last_role
|
21
|
+
methods = role.public_instance_methods(false) + role.protected_instance_methods(false) + role.private_instance_methods(false)
|
22
|
+
methods.each {|name| role.send(:remove_method, name)}
|
23
|
+
@__last_role_index = __last_role_index - 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def __roles
|
31
|
+
@__roles ||= Array.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def __last_role_index
|
35
|
+
@__last_role_index ||= -1
|
36
|
+
end
|
37
|
+
|
38
|
+
def __last_role
|
39
|
+
__roles[__last_role_index]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the highest role module free of methods. If none, creates a new empty module ready to be filled with
|
43
|
+
# role instance methods.
|
44
|
+
def __next_empty_role
|
45
|
+
@__last_role_index = __last_role_index + 1
|
46
|
+
__add_empty_role! unless __last_role
|
47
|
+
__last_role
|
48
|
+
end
|
49
|
+
|
50
|
+
# Creates and extends a new module ready to be filled with role instance methods.
|
51
|
+
def __add_empty_role!
|
52
|
+
role = Module.new
|
53
|
+
extend(role)
|
54
|
+
__roles << role
|
55
|
+
end
|
56
|
+
|
57
|
+
# The context a role is played within. This method must be overidden in every __role definition module.
|
58
|
+
def context
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# The role definition code also have private access to the extra args given in the context instantiation.
|
63
|
+
def settings(*keys)
|
64
|
+
context.send(:settings, *keys) if context
|
65
|
+
end
|
66
|
+
end
|
data/lib/drsi/version.rb
ADDED
data/lib/drsi.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe DCI::Context do
|
4
|
+
|
5
|
+
context "Definition:" do
|
6
|
+
context "When Inheriting from DCI::Context..." do
|
7
|
+
before(:all) do
|
8
|
+
class TestingDefinitionContext < DCI::Context
|
9
|
+
role :role_name do
|
10
|
+
def role_name_method
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def interaction1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it("...a new dci context is ready to be used...") {TestingDefinitionContext.superclass.should be(DCI::Context)}
|
20
|
+
it("...in which the developer can define roles...") do
|
21
|
+
TestingDefinitionContext.private_methods.map(&:to_s).should include("role")
|
22
|
+
end
|
23
|
+
it("...but privately inside the subclass.") {TestingDefinitionContext.should_not respond_to(:role)}
|
24
|
+
|
25
|
+
it("A role is defined calling the private macro #role with a role_key and a block defining the specific methods of the role.") do
|
26
|
+
TestingDefinitionContext.roles.size.should be(1)
|
27
|
+
TestingDefinitionContext.roles.keys.should include(:role_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
it("The #roles public class_and_instance_method will return a hash with pairs (role_key => ContextSubclass::Rolekey)...") do
|
31
|
+
TestingDefinitionContext.roles.should eq({:role_name => TestingDefinitionContext::RoleName})
|
32
|
+
TestingDefinitionContext.new(:role_name => Object.new).send(:roles).should eq(:role_name => TestingDefinitionContext::RoleName)
|
33
|
+
end
|
34
|
+
it("... where every ContextSubclass::Rolekey is a new module created at load time,...") do
|
35
|
+
TestingDefinitionContext.roles[:role_name].should be_a(Module)
|
36
|
+
TestingDefinitionContext.roles[:role_name].should be(TestingDefinitionContext::RoleName)
|
37
|
+
end
|
38
|
+
it("... named after the associated role_key...") do
|
39
|
+
TestingDefinitionContext.const_defined?(:RoleName).should be(true)
|
40
|
+
end
|
41
|
+
it("... and defined after the block given to the associated role in its definition.") do
|
42
|
+
TestingDefinitionContext::RoleName.public_instance_methods.map(&:to_s).should include("role_name_method")
|
43
|
+
end
|
44
|
+
|
45
|
+
it("Inside the context subclass, the developer defines context methods (instance methods) that act as interactions.") do
|
46
|
+
TestingDefinitionContext.public_instance_methods(false).map(&:to_s).should include('interaction1')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "Use:" do
|
52
|
+
context "To use a Context..." do
|
53
|
+
before(:all) do
|
54
|
+
class TestingUseContext < DCI::Context
|
55
|
+
role :role1 do
|
56
|
+
def role1_method
|
57
|
+
:role1_method
|
58
|
+
end
|
59
|
+
end
|
60
|
+
role :role2 do
|
61
|
+
end
|
62
|
+
|
63
|
+
def run
|
64
|
+
role1.role1_method
|
65
|
+
end
|
66
|
+
|
67
|
+
def interaction2
|
68
|
+
role1
|
69
|
+
end
|
70
|
+
|
71
|
+
def interaction3
|
72
|
+
role1.object_id - role2.object_id
|
73
|
+
end
|
74
|
+
end
|
75
|
+
@player1, @player2 = Object.new, Object.new
|
76
|
+
@context_instance_1 = TestingUseContext.new(:role1 => @player1, :role2 => @player2)
|
77
|
+
@context_instance_2 = TestingUseContext.new(:role1 => @player1, :role2 => @player2, :extra_arg => :extra)
|
78
|
+
end
|
79
|
+
|
80
|
+
it("...instanciate it from its correspondig DCI::Context subclass as usual...") do
|
81
|
+
@context_instance_1.should be_a(TestingUseContext)
|
82
|
+
end
|
83
|
+
it("...providing pairs of type :rolekey1 => player1 as arguments...") do
|
84
|
+
expect {TestingUseContext.new(@player1, @player2)}.to raise_error
|
85
|
+
end
|
86
|
+
it("...with ALL the role_keys in the subclass as keys...") do
|
87
|
+
expect {TestingUseContext.new(:role1 => @player1)}.to raise_error(/missing roles(.+)role2/)
|
88
|
+
end
|
89
|
+
it("...and the objects to play those roles as values.") do
|
90
|
+
@context_instance_1.interaction2
|
91
|
+
[@player1, @player2].should include(@context_instance_1.send(:role1), @context_instance_1.send(:role2))
|
92
|
+
end
|
93
|
+
|
94
|
+
it("...You can also include other extra pairs as arguments...") do
|
95
|
+
expect {TestingUseContext.new(:role1 => @player1, :role2 => @player2, :extra_arg => :extra)}.not_to raise_error
|
96
|
+
end
|
97
|
+
|
98
|
+
it("A shorter way to instantiate a context is through the class method []...") do
|
99
|
+
expect {TestingUseContext[:role1 => @player1, :role2 => @player2, :extra_arg => :extra]}.not_to raise_error
|
100
|
+
end
|
101
|
+
it("...which is equivalent to create a new instance and call #run on it.") do
|
102
|
+
TestingUseContext[:role1 => @player1, :role2 => @player2].should be(:role1_method)
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
it("Once instantiated...") {@context_instance_1.should be_a(TestingUseContext)}
|
107
|
+
it("...you call an interaction (instance method) on it") do
|
108
|
+
@context_instance_1.should respond_to(:interaction2)
|
109
|
+
end
|
110
|
+
it("...to start interaction among roleplayers inside the context.") do
|
111
|
+
@context_instance_1.interaction3.should be_instance_of(Fixnum)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe 'Interaction:' do
|
4
|
+
|
5
|
+
context "Inside a Context instance method(interaction)..." do
|
6
|
+
before(:all) do
|
7
|
+
class TestingInteractionsContext < DCI::Context
|
8
|
+
role :role1 do
|
9
|
+
end
|
10
|
+
role :role2 do
|
11
|
+
end
|
12
|
+
|
13
|
+
def interaction1
|
14
|
+
role1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
@player1, @player2 = Object.new, Object.new
|
18
|
+
@test_interactions_context = TestingInteractionsContext.new(:role1 => @player1,
|
19
|
+
:role2 => @player2,
|
20
|
+
:setting1 => :one,
|
21
|
+
:setting2 => :two,
|
22
|
+
:setting3 => :three)
|
23
|
+
end
|
24
|
+
|
25
|
+
it("...the developer has access to all the roleplayers...") do
|
26
|
+
@test_interactions_context.interaction1.should be(@player1)
|
27
|
+
@test_interactions_context.send(:role2).should be(@player2)
|
28
|
+
end
|
29
|
+
it("...via private instance methods named after their role keys.") do
|
30
|
+
@test_interactions_context.private_methods(false).map(&:to_s).should include('role1', 'role2')
|
31
|
+
end
|
32
|
+
|
33
|
+
it("He also have private access to extra args received in the instantiation of its context...") do
|
34
|
+
@test_interactions_context.private_methods.map(&:to_s).should include('settings')
|
35
|
+
@test_interactions_context.public_methods.map(&:to_s).should_not include('settings')
|
36
|
+
end
|
37
|
+
it("...calling #settings that returns a hash with all the extra args...") do
|
38
|
+
@test_interactions_context.send(:settings).should eq({:setting1 => :one, :setting2 => :two, :setting3 => :three})
|
39
|
+
end
|
40
|
+
it("...or #settings(key) that returns the value of the given extra arg...") do
|
41
|
+
@test_interactions_context.send(:settings, :setting2).should be(:two)
|
42
|
+
end
|
43
|
+
it("...or #settings(key1, key2, ...) that returns a hash with the given extra args.") do
|
44
|
+
@test_interactions_context.send(:settings, :setting1, :setting3).should eq({:setting1 => :one, :setting3 => :three})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
# require 'ostruct'
|
3
|
+
|
4
|
+
|
5
|
+
describe 'Players:' do
|
6
|
+
|
7
|
+
class CheckingAccount
|
8
|
+
attr_reader :account_id
|
9
|
+
attr_accessor :balance
|
10
|
+
|
11
|
+
def initialize(account_id, initial_balance=0)
|
12
|
+
@account_id, @balance = account_id, initial_balance
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
class MoneyTransferContext < DCI::Context
|
18
|
+
|
19
|
+
# Roles Definitions
|
20
|
+
role :source_account do
|
21
|
+
def run_transfer_of(amount)
|
22
|
+
self.balance -= amount
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
role :target_account do
|
27
|
+
def run_transfer_of(amount)
|
28
|
+
self.balance += amount
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Interactions
|
33
|
+
def run(amount=settings(:amount))
|
34
|
+
source_account.run_transfer_of(amount)
|
35
|
+
target_account.run_transfer_of(amount)
|
36
|
+
balances
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def accounts
|
41
|
+
[source_account, target_account]
|
42
|
+
end
|
43
|
+
|
44
|
+
def balances
|
45
|
+
accounts.map(&:balance)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "Are common Ruby objects that play roles inside contexts:" do
|
50
|
+
before(:all) do
|
51
|
+
@account1 = CheckingAccount.new(1, 1000)
|
52
|
+
@account2 = CheckingAccount.new(2)
|
53
|
+
@account1_public_interface = @account1.public_methods
|
54
|
+
end
|
55
|
+
|
56
|
+
context "Before becoming roleplayers inside a context..." do
|
57
|
+
it("...they are in an initial state...") do
|
58
|
+
@account1.balance.should be(1000)
|
59
|
+
@account2.balance.should be(0)
|
60
|
+
end
|
61
|
+
it("...and have got a given public interface.") do
|
62
|
+
@account1_public_interface.should be_true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "After playing a role inside a context..." do
|
67
|
+
before(:all) do
|
68
|
+
MoneyTransferContext.new(:source_account => @account1,
|
69
|
+
:target_account => @account2).run(200)
|
70
|
+
end
|
71
|
+
it("...they still preserve their public interface...") do
|
72
|
+
@account1.public_methods.should eql(@account1_public_interface)
|
73
|
+
@account1.should_not respond_to(:run_transfer_of)
|
74
|
+
@account1.private_methods.should_not include(:run_transfer_of)
|
75
|
+
end
|
76
|
+
it("...although their state might have been changed!") do
|
77
|
+
@account1.balance.should_not be(1000)
|
78
|
+
@account2.balance.should_not be(0)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/spec/role_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe 'Role' do
|
4
|
+
|
5
|
+
context "When defining roles inside a DCI::Context subclass..." do
|
6
|
+
before(:all) do
|
7
|
+
class TestingRoleContext < DCI::Context
|
8
|
+
role :rolename do
|
9
|
+
end
|
10
|
+
role :anotherrolename do
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
it("...you can define as many as you want.") do
|
15
|
+
TestingRoleContext.roles.keys.size.should eql(2)
|
16
|
+
end
|
17
|
+
it("Each rolename must be provided as a symbol...") do
|
18
|
+
TestingRoleContext.roles.keys.should include(:rolename, :anotherrolename)
|
19
|
+
end
|
20
|
+
it("...and not as a string.") do
|
21
|
+
expect do
|
22
|
+
class TestingRoleContext < DCI::Context
|
23
|
+
role "rolename" do
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end.to raise_error
|
27
|
+
end
|
28
|
+
it("A block defining rolemethods must be provided as well.") do
|
29
|
+
TestingRoleContext.roles[:rolename].should be_a(Module)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe 'RolePlayers' do
|
5
|
+
|
6
|
+
context "Are Ruby objects inside a context instance..." do
|
7
|
+
before(:all) do
|
8
|
+
class TestingRoleplayersContext < DCI::Context
|
9
|
+
role :role1 do
|
10
|
+
def role1method1; :role1method1_executed end
|
11
|
+
def role1self; self end
|
12
|
+
end
|
13
|
+
|
14
|
+
role :role2 do
|
15
|
+
def role2method1; role1 end
|
16
|
+
def role2self; self end
|
17
|
+
|
18
|
+
private
|
19
|
+
def private_role2method2; :private_rolemethod_return_value end
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_role_interaccess; role2.role2method1 == role1 end
|
23
|
+
|
24
|
+
def check_role1_identity(obj)
|
25
|
+
[role1 == obj, role1.role1self == obj, role1.respond_to?(:role1method1), obj.respond_to?(:role1method1),
|
26
|
+
role1.role1method1 == :role1method1_executed, obj.role1method1 == :role1method1_executed].uniq == [true]
|
27
|
+
end
|
28
|
+
|
29
|
+
def check_role2_identity(obj)
|
30
|
+
[role2 == obj, role2.role2self == obj, role2.respond_to?(:role2method1), obj.respond_to?(:role2method1),
|
31
|
+
role2.send(:private_role2method2) == :private_rolemethod_return_value,
|
32
|
+
obj.send(:private_role2method2) == :private_rolemethod_return_value,
|
33
|
+
role2.role2method1 == role1, obj.role2method1 == role1].uniq == [true]
|
34
|
+
end
|
35
|
+
|
36
|
+
def access_role1_external_interface
|
37
|
+
role1.name
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_role1_context_access
|
41
|
+
role1.context == self
|
42
|
+
end
|
43
|
+
|
44
|
+
def check_role1_settings_access
|
45
|
+
[!role1.respond_to?(:settings), role1.private_methods.map(&:to_s).include?('settings'),
|
46
|
+
role1.send(:settings) == settings].uniq == [true]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@player1, @player2 = OpenStruct.new(:name => 'player1'), OpenStruct.new(:name => 'player2')
|
51
|
+
@testing_roleplayers_context = TestingRoleplayersContext.new(:role1 => @player1,
|
52
|
+
:role2 => @player2,
|
53
|
+
:setting1 => :one,
|
54
|
+
:setting2 => :two,
|
55
|
+
:setting3 => :three)
|
56
|
+
end
|
57
|
+
|
58
|
+
it("...that adquire the public instance methods defined in their role...") do
|
59
|
+
@testing_roleplayers_context.check_role1_identity(@player1).should be_true
|
60
|
+
end
|
61
|
+
it("...as well as the private ones.") do
|
62
|
+
@testing_roleplayers_context.check_role2_identity(@player2).should be_true
|
63
|
+
end
|
64
|
+
|
65
|
+
it("They still preserve their identity") do
|
66
|
+
@testing_roleplayers_context.check_role1_identity(@player1).should be_true
|
67
|
+
end
|
68
|
+
it("...and therefore, their state and behaviour are accessible inside the context.") do
|
69
|
+
@testing_roleplayers_context.access_role1_external_interface.should eq('player1')
|
70
|
+
end
|
71
|
+
|
72
|
+
it("Inside the context, roleplayers have private access to other roleplayers through methods named after their keys.") do
|
73
|
+
@testing_roleplayers_context.check_role_interaccess.should be_true
|
74
|
+
end
|
75
|
+
it("...and offer a public_method to access the context.") do
|
76
|
+
@testing_roleplayers_context.check_role1_context_access.should be_true
|
77
|
+
end
|
78
|
+
|
79
|
+
it("They also have private access to extra args received in the instantiation of its context...") do
|
80
|
+
@testing_roleplayers_context.check_role1_settings_access.should be_true
|
81
|
+
end
|
82
|
+
it("...calling #settings that returns a hash with all the extra args...") do
|
83
|
+
@testing_roleplayers_context.send(:settings).should eq({:setting1 => :one, :setting2 => :two, :setting3 => :three})
|
84
|
+
end
|
85
|
+
it("...or #settings(key) that returns the value of the given extra arg...") do
|
86
|
+
@testing_roleplayers_context.send(:settings, :setting2).should be(:two)
|
87
|
+
end
|
88
|
+
it("...or #settings(key1, key2, ...) that returns a hash with the given extra args.") do
|
89
|
+
@testing_roleplayers_context.send(:settings, :setting1, :setting3).should eq({:setting1 => :one, :setting3 => :three})
|
90
|
+
end
|
91
|
+
|
92
|
+
it("But all these features, are only inside a context. Never out of it!") do
|
93
|
+
@player1.should_not respond_to(:role1method1)
|
94
|
+
@player2.private_methods.map(&:to_s).should_not include(:private_role2method2)
|
95
|
+
@player1.name.should eq('player1')
|
96
|
+
@player2.should_not respond_to(:role1)
|
97
|
+
@player2.should_not respond_to(:context)
|
98
|
+
@player1.should_not respond_to(:settings)
|
99
|
+
@player1.private_methods.map(&:to_s).should include('settings')
|
100
|
+
@player1.send(:settings).should be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'drsi'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: drsi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lorenzo Tello
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
description: Make DCI paradigm available to Ruby applications
|
28
|
+
email:
|
29
|
+
- ltello8a@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- ".rspec"
|
36
|
+
- ".ruby-gemset"
|
37
|
+
- ".ruby-version"
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- drsi.gemspec
|
43
|
+
- examples/money_transfer.rb
|
44
|
+
- lib/drsi.rb
|
45
|
+
- lib/drsi/dci/context.rb
|
46
|
+
- lib/drsi/dci/role.rb
|
47
|
+
- lib/drsi/module.rb
|
48
|
+
- lib/drsi/object.rb
|
49
|
+
- lib/drsi/rolable.rb
|
50
|
+
- lib/drsi/version.rb
|
51
|
+
- spec/context_spec.rb
|
52
|
+
- spec/interaction_spec.rb
|
53
|
+
- spec/players_spec.rb
|
54
|
+
- spec/role_spec.rb
|
55
|
+
- spec/roleplayers_spec.rb
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
homepage: http://github.com/ltello/drsi
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project: drsi
|
77
|
+
rubygems_version: 2.2.1
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Make DCI paradigm available to Ruby applications by enabling developers defining
|
81
|
+
contexts subclassing the class DCI::Context. You define roles inside the definition.
|
82
|
+
Match roles and player objects in context instantiation. Single Identity approach.
|
83
|
+
test_files:
|
84
|
+
- spec/context_spec.rb
|
85
|
+
- spec/interaction_spec.rb
|
86
|
+
- spec/players_spec.rb
|
87
|
+
- spec/role_spec.rb
|
88
|
+
- spec/roleplayers_spec.rb
|
89
|
+
- spec/spec_helper.rb
|