dci-ruby 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,52 @@
1
+ # rcov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # yard generated
9
+ doc
10
+ .yardoc
11
+
12
+ # bundler
13
+ .bundle
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19
+ #
20
+ # * Create a file at ~/.gitignore
21
+ # * Include files you want ignored
22
+ # * Run: git config --global core.excludesfile ~/.gitignore
23
+ #
24
+ # After doing this, these files will be ignored in all your git projects,
25
+ # saving you from having to 'pollute' every project you touch with them
26
+ #
27
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
28
+ #
29
+ # For MacOS:
30
+ #
31
+ #.DS_Store
32
+
33
+ # For TextMate
34
+ #*.tmproj
35
+ #tmtags
36
+
37
+ # For emacs:
38
+ #*~
39
+ #\#*
40
+ #.\#*
41
+
42
+ # For vim:
43
+ #*.swp
44
+
45
+ # For redcar:
46
+ #.redcar
47
+
48
+ # For rubinius:
49
+ #*.rbc
50
+
51
+ # RVM
52
+ .rvmrc
data/Gemfile CHANGED
@@ -1,14 +1,5 @@
1
1
  source "http://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
5
2
 
6
- # Add dependencies to develop your gem here.
7
- # Include everything needed to run rake, tests, features, etc.
8
- group :development do
9
- gem "rspec", "~> 2"
10
- gem "rdoc", "~> 3.12"
11
- gem "bundler", "~> 1.0.0"
12
- gem "jeweler", "~> 1.8.4"
13
- gem "rcov", ">= 0"
14
- end
3
+ # It's better to specify your gem's dependencies (both, those of development and those of production) in foodie.gemspec
4
+ gemspec # This makes bundle read dci-ruby.gemspec file to add its dependencies in their corresponding groups
5
+
@@ -1,18 +1,12 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ dci-ruby (2.0.0)
5
+
1
6
  GEM
2
7
  remote: http://rubygems.org/
3
8
  specs:
4
9
  diff-lcs (1.1.3)
5
- git (1.2.5)
6
- jeweler (1.8.4)
7
- bundler (~> 1.0)
8
- git (>= 1.2.5)
9
- rake
10
- rdoc
11
- json (1.7.5)
12
- rake (0.9.2.2)
13
- rcov (1.0.0)
14
- rdoc (3.12)
15
- json (~> 1.4)
16
10
  rspec (2.11.0)
17
11
  rspec-core (~> 2.11.0)
18
12
  rspec-expectations (~> 2.11.0)
@@ -26,8 +20,5 @@ PLATFORMS
26
20
  ruby
27
21
 
28
22
  DEPENDENCIES
29
- bundler (~> 1.0.0)
30
- jeweler (~> 1.8.4)
31
- rcov
32
- rdoc (~> 3.12)
33
- rspec (~> 2)
23
+ dci-ruby!
24
+ rspec (~> 2.0)
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Lorenzo Tello
1
+ Copyright (c) 2012, 2013 Lorenzo Tello
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -8,65 +8,64 @@ This gem makes Data-Context-Interaction paradigm ready to be used in your Ruby a
8
8
 
9
9
  Install as usual, either with rubygems
10
10
  gem install dci-ruby
11
- or including it in your Gemfile and then running bundle install:
11
+
12
+ or including it in your Gemfile and running bundle install:
12
13
  # Gemfile
13
14
  gem "dci-ruby"
14
15
 
15
16
 
16
17
  == Use
17
18
 
18
- dci-ruby gives you the class Context to inherit from to create your own contexts:
19
+ dci-ruby gives you the class DCI::Context to inherit from to create your own contexts:
19
20
 
20
- class MoneyTransfer < Context
21
+ class MoneyTransfer < DCI::Context
21
22
 
22
23
  # Roles
23
24
  role :source_account do
24
25
  def transfer(amount)
25
- balance -= amount
26
- target_account.get_transfer(amount)
26
+ player.balance -= amount
27
27
  end
28
28
  end
29
29
 
30
30
  role :target_account do
31
31
  def get_transfer(amount)
32
- balance += amount
32
+ player.balance += amount
33
33
  end
34
34
  end
35
35
 
36
36
  # Interactions
37
- def run(amount)
37
+ def run(amount=default_amount)
38
38
  source_account.transfer(amount)
39
- end
40
-
41
- def run_default
42
- source_account.transfer(default_amount)
39
+ target_account.get_transfer(amount)
43
40
  end
44
41
  end
45
42
 
46
43
  Every context defines some roles to be played by external objects (players) and their interactions. This way
47
- you have the whole agents and operations in a user case wrapped in just one entity instead of spreaded througout the
48
- whole application code.
44
+ you have all the agents and operations in a user case wrapped in just one entity instead of spreaded throughout the
45
+ application code.
49
46
 
50
- Use the defined contexts in your application, instantiating them wherever you need them in your code:
47
+ Use the defined contexts, instantiating them wherever you need in your code:
51
48
 
52
49
  MoneyTransfer.new(:source_account => Account.new(1),
53
50
  :target_account => Account.new(2)).run(100)
54
51
 
55
- Role player objects (the two account instances above) respond to their corresponding role methods inside the context.
56
- Moreover, every role player has access to the rest of role players. That's why target_account is reachable inside source_account#transfer
57
- rolemethod.
52
+ In a context instance, every role instanciates an object (roleplayer) that gathers the behaviour defined inside its role,
53
+ and that roleplayer object has private access to the original object adopting the role (player): the Account instances above are players.
54
+ Instances of MoneyTransfer::SourceAccount and MoneyTransfer::TargetAccount accessible inside MoneyTransfer.new via
55
+ the private instance_methods #source_account and #target_account are roleplayers.
56
+ Also, every roleplayer has private access to the rest of roleplayers in its context.
57
+
58
58
  When instanciating a Context, the extra no-role pairs given as arguments are read-only attributes accessible inside the instance:
59
59
 
60
60
  MoneyTransfer.new(:source_account => Account.new(1),
61
61
  :target_account => Account.new(2),
62
- :default_amount => 500).run_default
62
+ :default_amount => 500).run
63
63
 
64
- here, default_amount is not a roleplayer (has no associated role) but is still accessible in the interactions.
64
+ here, default_amount is not a player (has no associated role) but is still accessible in the interactions.
65
65
 
66
66
  See the examples[https://github.com/ltello/dci-ruby/tree/master/examples] folder for examples of use and the DCI-Sample[https://github.com/ltello/DCI-Sample] repository for a sample application using DCI through this gem.
67
67
 
68
68
  == Copyright
69
69
 
70
- Copyright (c) 2012 Lorenzo Tello. See LICENSE.txt for
71
- further details.
70
+ Copyright (c) 2012, 2013 Lorenzo Tello. See LICENSE.txt for further details.
72
71
 
data/Rakefile CHANGED
@@ -1,49 +1 @@
1
- # encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'bundler'
5
- begin
6
- Bundler.setup(:default, :development)
7
- rescue Bundler::BundlerError => e
8
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
10
- exit e.status_code
11
- end
12
- require 'rake'
13
-
14
- require 'jeweler'
15
- Jeweler::Tasks.new do |gem|
16
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
- gem.name = "dci-ruby"
18
- gem.homepage = "http://github.com/ltello/dci-ruby"
19
- gem.license = "MIT"
20
- gem.summary = %Q{Make DCI paradigm available to Ruby applications by enabling developers defining contexts suclassing the new class Context. You define roles inside the definition. Match roles and player objects in context instanciation.}
21
- gem.description = %Q{Make DCI paradigm available to Ruby applications}
22
- gem.email = "ltello8a@gmail.com"
23
- gem.authors = ["Lorenzo Tello"]
24
- # dependencies defined in Gemfile
25
- end
26
- Jeweler::RubygemsDotOrgTasks.new
27
-
28
- require 'rspec/core'
29
- require 'rspec/core/rake_task'
30
- RSpec::Core::RakeTask.new(:spec) do |spec|
31
- spec.pattern = FileList['spec/**/*_spec.rb']
32
- end
33
-
34
- RSpec::Core::RakeTask.new(:rcov) do |spec|
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
37
- end
38
-
39
- task :default => :spec
40
-
41
- require 'rdoc/task'
42
- Rake::RDocTask.new do |rdoc|
43
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
-
45
- rdoc.rdoc_dir = 'rdoc'
46
- rdoc.title = "dci-ruby #{version}"
47
- rdoc.rdoc_files.include('README*')
48
- rdoc.rdoc_files.include('lib/**/*.rb')
49
- end
1
+ require "bundler/gem_tasks"
@@ -1,68 +1,25 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "dci-ruby/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = "dci-ruby"
8
- s.version = "1.0.0"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Lorenzo Tello"]
12
- s.date = "2012-10-25"
6
+ s.name = "dci-ruby"
7
+ s.version = DCI::VERSION
8
+ s.authors = ["Lorenzo Tello"]
9
+ s.email = ["ltello8a@gmail.com"]
10
+ s.homepage = "http://github.com/ltello/dci-ruby"
11
+ s.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."
13
12
  s.description = "Make DCI paradigm available to Ruby applications"
14
- s.email = "ltello8a@gmail.com"
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".rspec",
22
- "Gemfile",
23
- "Gemfile.lock",
24
- "LICENSE.txt",
25
- "README.rdoc",
26
- "Rakefile",
27
- "VERSION",
28
- "dci-ruby.gemspec",
29
- "examples/money_transfer.rb",
30
- "lib/dci-ruby.rb",
31
- "lib/dci-ruby/kernel.rb",
32
- "spec/context_spec.rb",
33
- "spec/interaction_spec.rb",
34
- "spec/role_spec.rb",
35
- "spec/roleplayers_spec.rb",
36
- "spec/spec_helper.rb"
37
- ]
38
- s.homepage = "http://github.com/ltello/dci-ruby"
39
13
  s.licenses = ["MIT"]
40
- s.require_paths = ["lib"]
41
- s.rubygems_version = "1.8.24"
42
- s.summary = "Make DCI paradigm available to Ruby applications by enabling developers defining contexts suclassing the new class Context. You define roles inside the definition. Match roles and player objects in context instanciation."
43
14
 
44
- if s.respond_to? :specification_version then
45
- s.specification_version = 3
15
+ s.rubyforge_project = "dci-ruby"
46
16
 
47
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
- s.add_development_dependency(%q<rspec>, ["~> 2"])
49
- s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
50
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
51
- s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
52
- s.add_development_dependency(%q<rcov>, [">= 0"])
53
- else
54
- s.add_dependency(%q<rspec>, ["~> 2"])
55
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
56
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
57
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
58
- s.add_dependency(%q<rcov>, [">= 0"])
59
- end
60
- else
61
- s.add_dependency(%q<rspec>, ["~> 2"])
62
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
63
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
64
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
65
- s.add_dependency(%q<rcov>, [">= 0"])
66
- end
67
- end
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
68
21
 
22
+ # specify any dependencies here; for example:
23
+ s.add_development_dependency "rspec", "~> 2.0"
24
+ # s.add_runtime_dependency "rest-client"
25
+ end
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'dci-ruby'
2
3
 
3
4
 
@@ -5,27 +6,36 @@ class CheckingAccount
5
6
  attr_reader :account_id
6
7
  attr_accessor :balance
7
8
 
8
- def initialize(account_id)
9
- @account_id, @balance = account_id, 0
9
+ def initialize(account_id, initial_balance=0)
10
+ @account_id, @balance = account_id, initial_balance
10
11
  end
11
12
  end
12
13
 
13
14
 
14
- class MoneyTransferContext < Context
15
+ class MoneyTransferContext < DCI::Context
15
16
 
16
17
  # Roles Definitions
17
18
 
18
19
  role :source_account do
20
+ extend ::Forwardable
21
+ def_delegators :player, :account_id, :balance, :balance=
22
+
19
23
  def run_transfer_of(amount)
20
24
  self.balance -= amount
21
- puts "Source Account #{account_id} sent $#{amount} to Target Account #{target_account.account_id}."
25
+ puts "\tAccount(\##{account_id}) sent #{amount} to Account(\##{target_account.account_id})."
22
26
  end
23
27
  end
24
28
 
25
29
  role :target_account do
30
+ #[:account_id, :balance, :balance=].each {|field| define_method(field) {|*args| player.send(field, *args)}}
31
+ def account_id; player.account_id end
32
+ def balance; player.balance end
33
+ def balance=(amount) player.balance=(amount) end
34
+ private :balance=
35
+
26
36
  def run_transfer_of(amount)
27
37
  self.balance += amount
28
- puts "Target Account #{account_id} received $#{amount} from Source Account #{source_account.account_id}."
38
+ puts "\tAccount(\##{account_id}) received #{amount} from Account(\##{source_account.account_id})."
29
39
  end
30
40
  end
31
41
 
@@ -47,10 +57,10 @@ class MoneyTransferContext < Context
47
57
  end
48
58
 
49
59
  def balances
50
- accounts.map {|account| "$#{account.balance}"}.join(' - ')
60
+ accounts.map {|account| "#{account.balance}"}.join(' - ')
51
61
  end
52
62
  end
53
63
 
54
- MoneyTransferContext.new(:source_account => CheckingAccount.new(1),
64
+ MoneyTransferContext.new(:source_account => CheckingAccount.new(1, 500),
55
65
  :target_account => CheckingAccount.new(2),
56
66
  :amount => 500).run
@@ -1,96 +1,7 @@
1
- require 'forwardable'
1
+ require 'dci-ruby/version'
2
2
  require 'dci-ruby/kernel'
3
+ require 'dci-ruby/dci/context'
3
4
 
4
-
5
- class Context
6
-
7
- class << self
8
-
9
- # Every subclass of Context has is own class and instance method roles defined.
10
- # The instance method delegates value to the class.
11
- def inherited(subklass)
12
- subklass.class_eval do
13
- def self.roles; {} end
14
- def roles; self.class.roles end
15
- end
16
- end
17
-
18
-
19
- private
20
-
21
- # The macro role is defined to allow a subclass of Context to define roles in its definition.
22
- # Every new role redefines the role class method to contain a hash accumulating all defined roles in that subclass.
23
- # An accessor to the object playing the new role is also defined and available in every instance of the context subclass.
24
- def role(role_key, &block)
25
- raise "role name must be a symbol" unless role_key.is_a?(Symbol)
26
- updated_roles = roles.merge(role_key => Module.new(&block))
27
- singleton_class.class_exec(updated_roles) do |new_roles|
28
- remove_method(:roles) rescue nil
29
- define_method(:roles) { new_roles }
30
- end
31
- attr_reader role_key
32
- end
33
-
34
- end
35
-
36
-
37
- # Instances of a defined subclass of Context are initialized checking first that all subclass defined roles
38
- # are provided in the creation invocation raising an error if any of them is missing.
39
- # Once the previous check is met, every object playing in the context instance is associated to the stated role.
40
- # Non players args are associated to instance_variables and readers defined.
41
- def initialize(args={})
42
- check_all_roles_provided_in!(args)
43
- players, noplayers = args.partition {|key, value| roles.keys.include?(key)}.map {|group| Hash[*group.flatten]}
44
- assign_roles_to_players(players)
45
- define_attr_readers_for_no_players(noplayers)
46
- end
47
-
48
-
49
- private
50
-
51
- # Checks there is an intented player for every role.
52
- # Raises and error message in case of missing roles.
53
- def check_all_roles_provided_in!(players={})
54
- missing_roles = missing_roles(players)
55
- raise "missing roles #{missing_roles}" unless missing_roles.empty?
56
- end
57
-
58
- # The list of roles with no player provided
59
- def missing_roles(players={})
60
- (roles.keys - players.keys)
61
- end
62
-
63
- # Associates every role to the intended player.
64
- def assign_roles_to_players(players={})
65
- roles.keys.each do |role_key|
66
- assign_role_to_player(role_key, players[role_key])
67
- end
68
- end
69
-
70
- # Associates a role to an intended player:
71
- # - The object to play the role extends the defined module for that role, so it now respond to the methods
72
- # defined in the role definition.
73
- # - The object to play the role has access to the context it is playing.
74
- # - The object to play the role has access to the rest of players in its context.
75
- # - The context instance has access to this new player through a new instance variable defined with the name of the role.
76
- def assign_role_to_player(role_key, player)
77
- role_module = roles[role_key]
78
- other_role_keys = roles.keys - [role_key]
79
- player.extend(role_module, ::SingleForwardable)
80
- player.singleton_class.class_exec(self) do |context|
81
- define_method(:context) {context}
82
- end
83
- player.def_delegators(:context, *other_role_keys)
84
- instance_variable_set(:"@#{role_key}", player)
85
- end
86
-
87
- def define_attr_readers_for_no_players(vars={})
88
- vars.each do |name, value|
89
- instance_variable_set(:"@#{name}", value)
90
- singleton_class.class_exec(name.to_sym) do |varkey|
91
- attr_reader varkey
92
- end
93
- end
94
-
95
- end
5
+ module DCI
96
6
  end
7
+
@@ -0,0 +1,95 @@
1
+ require 'dci-ruby/dci/role'
2
+
3
+ module DCI
4
+
5
+ class Context
6
+
7
+ class << self
8
+
9
+ # Every subclass of Context has is own class and instance method roles defined.
10
+ # The instance method delegates value to the class.
11
+ def inherited(subklass)
12
+ subklass.class_eval do
13
+ @roles ||= {}
14
+ def self.roles; @roles end
15
+ def roles; self.class.roles end
16
+ end
17
+ end
18
+
19
+
20
+ private
21
+
22
+ # The macro role is defined to allow a subclass of Context to define roles in its definition.
23
+ # Every new role redefines the role class method to contain a hash accumulating all defined roles in that subclass.
24
+ # An accessor to the object playing the new role is also defined and available in every instance of the context subclass.
25
+ def role(role_key, &block)
26
+ raise "role name must be a symbol" unless role_key.is_a?(Symbol)
27
+ new_klass_name = role_key.to_s.split(/\_+/).map(&:capitalize).join('')
28
+ const_set(new_klass_name, Class.new(::DCI::Role, &block))
29
+ roles.merge!(role_key => const_get(new_klass_name.to_sym))
30
+ attr_reader role_key
31
+ private role_key
32
+ end
33
+
34
+ end
35
+
36
+
37
+ # Instances of a defined subclass of Context are initialized checking first that all subclass defined roles
38
+ # are provided in the creation invocation raising an error if any of them is missing.
39
+ # Once the previous check is met, every object playing in the context instance is associated to the stated role.
40
+ # Non players args are associated to instance_variables and readers defined.
41
+ def initialize(args={})
42
+ check_all_roles_provided_in!(args)
43
+ players, noplayers = args.partition {|key, value| roles.has_key?(key)}.map {|group| Hash[*group.flatten]}
44
+ assign_roles_to_players(players)
45
+ define_readers_for_no_players(noplayers)
46
+ end
47
+
48
+
49
+ private
50
+
51
+ # Checks there is a player for each role.
52
+ # Raises and error message in case of missing roles.
53
+ def check_all_roles_provided_in!(players={})
54
+ missing_roles = missing_roles(players)
55
+ raise "missing roles #{missing_roles}" unless missing_roles.empty?
56
+ end
57
+
58
+ # The list of roles with no player provided
59
+ def missing_roles(players={})
60
+ (roles.keys - players.keys)
61
+ end
62
+
63
+ # Associates every role to the intended player.
64
+ def assign_roles_to_players(players={})
65
+ roles.keys.each do |role_key|
66
+ assign_role_to_player(role_key, players[role_key])
67
+ end
68
+ end
69
+
70
+ # Associates a role to an intended player:
71
+ # - A new role instance is created from the associated role_key class and the player to get that role.
72
+ # - The new role instance has access to the context it is playing.
73
+ # - The new role instance has access to the rest of players in its context through instance methods named after their role keys.
74
+ # - The context instance has access to this new role instance through an instance method named after the role key.
75
+ def assign_role_to_player(role_key, player)
76
+ role_klass = roles[role_key]
77
+ other_role_keys = roles.keys - [role_key]
78
+ role_instance = role_klass.new(:player => player, :context => self, :role_mate_keys => other_role_keys)
79
+ instance_variable_set(:"@#{role_key}", role_instance)
80
+ end
81
+
82
+ # For each given pair in vars, define a private method named the key that returns the entry associated value.
83
+ def define_readers_for_no_players(vars={})
84
+ vars.each do |name, value|
85
+ instance_variable_set(:"@#{name}", value)
86
+ singleton_class.class_exec(name.to_sym) do |varkey|
87
+ private
88
+ attr_reader varkey
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,49 @@
1
+ module DCI
2
+
3
+ class Role
4
+
5
+ class << self
6
+
7
+ # Make this class abstract: will not allow create instances.
8
+ def new(*args, &block)
9
+ raise 'This class is meant to be abstract and not instantiable' if self == DCI::Role
10
+ super
11
+ end
12
+
13
+ end
14
+
15
+ # Opts:
16
+ # player => the object to adquire this role,
17
+ # context => the context instance in which this role instance will play
18
+ # role_mate_keys => list of keys of the rest of roles playing in the given context
19
+ def initialize(opts={})
20
+ @player = opts[:player]
21
+ @context = opts[:context]
22
+ define_role_mate_methods!(opts[:role_mate_keys])
23
+ end
24
+
25
+
26
+ private
27
+
28
+ # Make the original object playing the role accessible only inside role definition code!
29
+ def player
30
+ @player
31
+ end
32
+
33
+ # For each role in the context, define a method so inside a role you can access all the others.
34
+ def define_role_mate_methods!(role_mate_keys)
35
+ self.class.class_exec(role_mate_keys) do |other_role_keys|
36
+ other_role_keys.each do |role_key|
37
+ if not private_method_defined?(role_key)
38
+ define_method(role_key) do
39
+ @context.send(role_key)
40
+ end
41
+ private role_key
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,3 @@
1
+ module DCI
2
+ VERSION = "2.0.0"
3
+ end
@@ -1,12 +1,14 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- describe Context do
3
+ describe DCI::Context do
4
4
 
5
- context "Definition" do
6
- context "When Inherit from Context" do
5
+ context "Definition:" do
6
+ context "When Inheriting from DCI::Context..." do
7
7
  before(:all) do
8
- class ExampleContext < Context
9
- role :rolename do
8
+ class TestingDefinitionContext < DCI::Context
9
+ role :role_name do
10
+ def role_name_method
11
+ end
10
12
  end
11
13
 
12
14
  def interaction1
@@ -14,17 +16,42 @@ describe Context do
14
16
  end
15
17
  end
16
18
 
17
- it("A new context subclass is ready to be used...") {ExampleContext.superclass.should be(Context)}
18
- it("...in which the developer can define roles...") {ExampleContext.private_methods.should include("role")}
19
- it("...but privately inside the class.") {ExampleContext.should_not respond_to(:role)}
20
- it("He can also define contextmethods (instance methods) that acts as interactions.") {ExampleContext.public_instance_methods(false).should include('interaction1')}
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.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).roles.should eq(:role_name => TestingDefinitionContext::RoleName)
33
+ end
34
+ it("... where every ContextSubclass::Rolekey is a new class created at load time,...") do
35
+ TestingDefinitionContext.roles[:role_name].should be_a(Class)
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.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).should include('interaction1')
47
+ end
21
48
  end
22
49
  end
23
50
 
24
- context "Use" do
25
- context "To use a Context" do
51
+ context "Use:" do
52
+ context "To use a Context..." do
26
53
  before(:all) do
27
- class AnotherContext < Context
54
+ class TestingUseContext < DCI::Context
28
55
  role :role1 do
29
56
  end
30
57
  role :role2 do
@@ -39,23 +66,32 @@ describe Context do
39
66
  end
40
67
  end
41
68
  @player1, @player2 = Object.new, Object.new
42
- @example_context = AnotherContext.new(:role1 => @player1, :role2 => @player2)
69
+ @context_instance_1 = TestingUseContext.new(:role1 => @player1, :role2 => @player2)
70
+ @context_instance_2 = TestingUseContext.new(:role1 => @player1, :role2 => @player2, :extra_arg => :extra)
43
71
  end
44
72
 
45
- it("You instanciate it...") {@example_context.class.should be(AnotherContext)}
46
- it("...providing a hash of type {:rolename1 => player1, ... }") do
47
- expect {AnotherContext.new(@player1, @player2)}.to raise_error
73
+ it("...instanciate it from its correspondig DCI::Context subclass...") {@context_instance_1.should be_a(TestingUseContext)}
74
+ it("...providing pairs of type :rolekey1 => player1 as arguments...") do
75
+ expect {TestingUseContext.new(@player1, @player2)}.to raise_error
48
76
  end
49
- it("...with ALL rolenames as keys...") do
50
- expect {AnotherContext.new(:role1 => @player1)}.to raise_error('missing roles role2')
77
+ it("...with ALL the role_keys in the subclass as keys...") do
78
+ expect {TestingUseContext.new(:role1 => @player1)}.to raise_error('missing roles role2')
51
79
  end
52
80
  it("...and the objects to play those roles as values.") do
53
- [@player1, @player2].should include(@example_context.role1, @example_context.role2)
81
+ [@player1, @player2].should include(@context_instance_1.send(:role1).send(:player), @context_instance_1.send(:role2).send(:player))
82
+ end
83
+
84
+ it("...You can also include other extra pairs as arguments...") do
85
+ expect {TestingUseContext.new(:role1 => @player1, :role2 => @player2, :extra_arg => :extra)}.not_to raise_error
86
+ end
87
+
88
+
89
+ it("Once instanciated...") {@context_instance_1.should be_a(TestingUseContext)}
90
+ it("...you call an interaction (instance method) on it") do
91
+ @context_instance_1.should respond_to(:interaction1)
54
92
  end
55
- it("Once instanciated...") {@example_context.class.should be(AnotherContext)}
56
- it("...you call an interaction (instance method) on it") {@example_context.should respond_to(:interaction1)}
57
- it("...to start interaction among roleplayers inside the context") do
58
- @example_context.interaction2.should be_instance_of(Fixnum)
93
+ it("...to start interaction among roleplayers inside the context.") do
94
+ @context_instance_1.interaction2.should be_instance_of(Fixnum)
59
95
  end
60
96
 
61
97
  end
@@ -1,10 +1,10 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- describe 'Interaction' do
3
+ describe 'Interaction:' do
4
4
 
5
- context "Inside a contextmethod(interaction)" do
5
+ context "Inside a Context instance method(interaction)..." do
6
6
  before(:all) do
7
- class AnotherContext < Context
7
+ class TestingInteractionsContext < DCI::Context
8
8
  role :role1 do
9
9
  end
10
10
  role :role2 do
@@ -15,12 +15,23 @@ describe 'Interaction' do
15
15
  end
16
16
  end
17
17
  @player1, @player2 = Object.new, Object.new
18
- @example_context = AnotherContext.new(:role1 => @player1, :role2 => @player2)
18
+ @test_interactions_context = TestingInteractionsContext.new(:role1 => @player1, :role2 => @player2, :extra_arg => :extra)
19
19
  end
20
20
 
21
- it("The developer has access to all the roleplayers...") {@example_context.interaction1.should be(@player1)}
22
- it("...named after the rolenames.") {@example_context.methods.should include('role1', 'role2')}
23
- end
21
+ it("...the developer has access to all the roleplayers...") do
22
+ @test_interactions_context.interaction1.should be_a(TestingInteractionsContext::Role1)
23
+ @test_interactions_context.send(:role2).should be_a(TestingInteractionsContext::Role2)
24
+ end
25
+ it("...via private instance methods named after their role keys.") do
26
+ @test_interactions_context.private_methods(false).should include('role1', 'role2')
27
+ end
24
28
 
29
+ it("He also have access to extra args received in the instantiation of its context...") do
30
+ @test_interactions_context.send(:extra_arg).should be(:extra)
31
+ end
32
+ it("...through private methods named after their keys.") do
33
+ @test_interactions_context.private_methods(false).should include('extra_arg')
34
+ end
35
+ end
25
36
 
26
37
  end
@@ -2,19 +2,32 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe 'Role' do
4
4
 
5
- context "When defining roles inside a Context subclass..." do
5
+ context "When defining roles inside a DCI::Context subclass..." do
6
6
  before(:all) do
7
- class ExampleContext < Context
7
+ class TestingRoleContext < DCI::Context
8
8
  role :rolename do
9
9
  end
10
10
  role :anotherrolename do
11
11
  end
12
12
  end
13
13
  end
14
- it("You can define as many as you want") {ExampleContext.roles.keys.size.should eql(2)}
15
- it("Each rolename must be provided as a symbol...") {ExampleContext.roles.keys.should include(:rolename)}
16
- it("...and not as a string") {expect {class ExampleContext < Context; role "rolename" do; end; end}.to raise_error}
17
- it("A block defining rolemethods can also be provided.") {ExampleContext.roles[:rolename].class.should be(Module)}
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(Class)
30
+ end
18
31
  end
19
32
 
20
33
  end
@@ -3,18 +3,25 @@ require 'ostruct'
3
3
 
4
4
  describe 'RolePlayers' do
5
5
 
6
- context "Are Ruby objects inside a context instance" do
6
+ context "Are Ruby objects inside a context instance..." do
7
7
  before(:all) do
8
- class AnotherContext < Context
8
+ class TestingRoleplayersContext < DCI::Context
9
9
  role :role1 do
10
10
  def rolemethod1
11
11
  :rolemethod1_executed
12
12
  end
13
13
  end
14
+
14
15
  role :role2 do
15
16
  def rolemethod2
16
17
  role1
17
18
  end
19
+
20
+ private
21
+
22
+ def private_rolemethod2
23
+ :public_rolemethod_return_value
24
+ end
18
25
  end
19
26
 
20
27
  def interaction1
@@ -25,21 +32,45 @@ describe 'RolePlayers' do
25
32
  role1.object_id - role2.object_id
26
33
  end
27
34
  end
28
- @player1, @player2 = OpenStruct.new(:field1 => 'value1'), OpenStruct.new(:field2 => 'value2')
29
- @example_context = AnotherContext.new(:role1 => @player1, :role2 => @player2)
35
+ @player1, @player2 = OpenStruct.new(:name => 'player1'), OpenStruct.new(:name => 'player2')
36
+ @testing_roleplayers_context = TestingRoleplayersContext.new(:role1 => @player1, :role2 => @player2)
37
+ end
38
+
39
+ it("...each one instance of the class defined after the role he plays...") do
40
+ @testing_roleplayers_context.send(:role1).should be_a(TestingRoleplayersContext::Role1)
41
+ @testing_roleplayers_context.send(:role2).should be_a(TestingRoleplayersContext::Role2)
42
+ end
43
+ it("...so they adquire the public instance methods defined in their role...") do
44
+ @testing_roleplayers_context.send(:role1).public_methods(false).should include('rolemethod1')
45
+ @testing_roleplayers_context.send(:role1).should respond_to(:rolemethod1)
46
+ @testing_roleplayers_context.send(:role1).rolemethod1.should eql(:rolemethod1_executed)
47
+ end
48
+ it("...as well as the private ones.") do
49
+ @testing_roleplayers_context.send(:role2).private_methods(false).should include('private_rolemethod2')
50
+ @testing_roleplayers_context.send(:role2).should_not respond_to(:private_rolemethod2)
51
+ @testing_roleplayers_context.send(:role2).send(:private_rolemethod2).should eql(:public_rolemethod_return_value)
30
52
  end
31
53
 
32
- it("that besides their normal behaviour...") do
33
- @player1.field1.should eql('value1')
54
+ it("Also, through the private method #player") do
55
+ @testing_roleplayers_context.send(:role1).private_methods.should include('player')
34
56
  end
35
- it("...also respond to the rolemethods defined in their playing role.") do
36
- @example_context.role1.rolemethod1.should eql(:rolemethod1_executed)
57
+ it("...they have access to the original object playing the role...") do
58
+ @testing_roleplayers_context.send(:role1).send(:player).should be(@player1)
59
+ @testing_roleplayers_context.send(:role2).send(:player).should be(@player2)
37
60
  end
38
- it("Roleplayers can access other roleplayers in their context...") do
39
- @example_context.role2.rolemethod2.should eql(@example_context.role1)
61
+ it("...and, therefore, its public interface.") do
62
+ @testing_roleplayers_context.send(:role1).send(:player).name.should eq('player1')
63
+ @testing_roleplayers_context.send(:role2).send(:player).name.should eq('player2')
64
+ end
65
+
66
+ it("Roleplayers have private access to other roleplayers in their context...") do
67
+ @testing_roleplayers_context.send(:role2).private_methods(false).should include('role1')
68
+ @testing_roleplayers_context.send(:role2).rolemethod2.should eql(@testing_roleplayers_context.send(:role1))
40
69
  end
41
70
  it("...and even the context itself.") do
42
- @example_context.role2.context.should eql(@example_context)
71
+ expect {@testing_roleplayers_context.send(:role2).send(:context)}.to raise_error(NoMethodError)
72
+ @testing_roleplayers_context.send(:role2).instance_variables.should include('@context')
73
+ @testing_roleplayers_context.send(:role2).instance_variable_get(:@context).should be(@testing_roleplayers_context)
43
74
  end
44
75
  end
45
76
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dci-ruby
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
- - 1
7
+ - 2
8
8
  - 0
9
9
  - 0
10
- version: 1.0.0
10
+ version: 2.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Lorenzo Tello
@@ -15,105 +15,48 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-10-25 00:00:00 Z
18
+ date: 2013-10-29 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- requirement: &id001 !ruby/object:Gem::Requirement
22
- none: false
23
- requirements:
24
- - - ~>
25
- - !ruby/object:Gem::Version
26
- hash: 7
27
- segments:
28
- - 2
29
- version: "2"
30
- version_requirements: *id001
31
21
  name: rspec
32
22
  prerelease: false
33
- type: :development
34
- - !ruby/object:Gem::Dependency
35
- requirement: &id002 !ruby/object:Gem::Requirement
36
- none: false
37
- requirements:
38
- - - ~>
39
- - !ruby/object:Gem::Version
40
- hash: 31
41
- segments:
42
- - 3
43
- - 12
44
- version: "3.12"
45
- version_requirements: *id002
46
- name: rdoc
47
- prerelease: false
48
- type: :development
49
- - !ruby/object:Gem::Dependency
50
- requirement: &id003 !ruby/object:Gem::Requirement
51
- none: false
52
- requirements:
53
- - - ~>
54
- - !ruby/object:Gem::Version
55
- hash: 23
56
- segments:
57
- - 1
58
- - 0
59
- - 0
60
- version: 1.0.0
61
- version_requirements: *id003
62
- name: bundler
63
- prerelease: false
64
- type: :development
65
- - !ruby/object:Gem::Dependency
66
- requirement: &id004 !ruby/object:Gem::Requirement
23
+ requirement: &id001 !ruby/object:Gem::Requirement
67
24
  none: false
68
25
  requirements:
69
26
  - - ~>
70
- - !ruby/object:Gem::Version
71
- hash: 63
72
- segments:
73
- - 1
74
- - 8
75
- - 4
76
- version: 1.8.4
77
- version_requirements: *id004
78
- name: jeweler
79
- prerelease: false
80
- type: :development
81
- - !ruby/object:Gem::Dependency
82
- requirement: &id005 !ruby/object:Gem::Requirement
83
- none: false
84
- requirements:
85
- - - ">="
86
27
  - !ruby/object:Gem::Version
87
28
  hash: 3
88
29
  segments:
30
+ - 2
89
31
  - 0
90
- version: "0"
91
- version_requirements: *id005
92
- name: rcov
93
- prerelease: false
32
+ version: "2.0"
94
33
  type: :development
34
+ version_requirements: *id001
95
35
  description: Make DCI paradigm available to Ruby applications
96
- email: ltello8a@gmail.com
36
+ email:
37
+ - ltello8a@gmail.com
97
38
  executables: []
98
39
 
99
40
  extensions: []
100
41
 
101
- extra_rdoc_files:
102
- - LICENSE.txt
103
- - README.rdoc
42
+ extra_rdoc_files: []
43
+
104
44
  files:
105
45
  - .document
46
+ - .gitignore
106
47
  - .rspec
107
48
  - Gemfile
108
49
  - Gemfile.lock
109
50
  - LICENSE.txt
110
51
  - README.rdoc
111
52
  - Rakefile
112
- - VERSION
113
53
  - dci-ruby.gemspec
114
54
  - examples/money_transfer.rb
115
55
  - lib/dci-ruby.rb
56
+ - lib/dci-ruby/dci/context.rb
57
+ - lib/dci-ruby/dci/role.rb
116
58
  - lib/dci-ruby/kernel.rb
59
+ - lib/dci-ruby/version.rb
117
60
  - spec/context_spec.rb
118
61
  - spec/interaction_spec.rb
119
62
  - spec/role_spec.rb
@@ -147,10 +90,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
90
  version: "0"
148
91
  requirements: []
149
92
 
150
- rubyforge_project:
93
+ rubyforge_project: dci-ruby
151
94
  rubygems_version: 1.8.24
152
95
  signing_key:
153
96
  specification_version: 3
154
- summary: Make DCI paradigm available to Ruby applications by enabling developers defining contexts suclassing the new class Context. You define roles inside the definition. Match roles and player objects in context instanciation.
155
- test_files: []
156
-
97
+ 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.
98
+ test_files:
99
+ - spec/context_spec.rb
100
+ - spec/interaction_spec.rb
101
+ - spec/role_spec.rb
102
+ - spec/roleplayers_spec.rb
103
+ - spec/spec_helper.rb
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 1.0.0