MonkeyEngine 1.0.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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.idea/.name +1 -0
  4. data/.idea/.rakeTasks +7 -0
  5. data/.idea/MonkeyEngine.iml +133 -0
  6. data/.idea/codeStyleSettings.xml +13 -0
  7. data/.idea/encodings.xml +5 -0
  8. data/.idea/misc.xml +5 -0
  9. data/.idea/modules.xml +9 -0
  10. data/.idea/runConfigurations/All_specs_in_test__MonkeyEngine.xml +38 -0
  11. data/.idea/runConfigurations/IRB_console.xml +25 -0
  12. data/.idea/runConfigurations/Start_Yard_Server.xml +26 -0
  13. data/.idea/runConfigurations/monkey_run.xml +26 -0
  14. data/.idea/scopes/scope_settings.xml +5 -0
  15. data/.idea/vcs.xml +7 -0
  16. data/.idea/workspace.xml +886 -0
  17. data/.ruby-version +1 -0
  18. data/Gemfile +4 -0
  19. data/LICENSE.txt +22 -0
  20. data/README.md +29 -0
  21. data/Rakefile +3 -0
  22. data/lib/Action/action.rb +29 -0
  23. data/lib/Monkey.rb +1 -0
  24. data/lib/Monkey/monkey.rb +108 -0
  25. data/lib/MonkeyAction/monkey_action.rb +14 -0
  26. data/lib/MonkeyAction/monkey_action_dead.rb +26 -0
  27. data/lib/MonkeyAction/monkey_action_eat.rb +31 -0
  28. data/lib/MonkeyAction/monkey_action_pause.rb +32 -0
  29. data/lib/MonkeyAction/monkey_action_sleep.rb +31 -0
  30. data/lib/MonkeyAction/monkey_action_type.rb +40 -0
  31. data/lib/MonkeyAction/monkey_action_wake.rb +26 -0
  32. data/lib/MonkeyAction/monkey_timed_action.rb +27 -0
  33. data/lib/MonkeyActions.rb +6 -0
  34. data/lib/MonkeyEngine.rb +1 -0
  35. data/lib/MonkeyEngine/action_rules.rb +111 -0
  36. data/lib/MonkeyEngine/exceptions.rb +21 -0
  37. data/lib/MonkeyEngine/monkey_engine.rb +53 -0
  38. data/lib/MonkeyEngine/version.rb +3 -0
  39. data/lib/MonkeyFactory.rb +1 -0
  40. data/lib/MonkeyFactory/monkey_factory.rb +14 -0
  41. data/lib/MonkeyKeyboard/keyboard_char.rb +10 -0
  42. data/lib/MonkeyKeyboard/keyboard_input.rb +14 -0
  43. data/lib/MonkeyKeyboard/keyboard_key.rb +26 -0
  44. data/lib/MonkeyKeyboard/keyboard_key_evaluator.rb +25 -0
  45. data/lib/MonkeyKeyboard/monkey_keyboard_en_us.rb +137 -0
  46. data/lib/MonkeyKeyboardEnUs.rb +1 -0
  47. data/lib/MonkeyManager.rb +1 -0
  48. data/lib/MonkeyManager/monkey_manager.rb +162 -0
  49. data/lib/MonkeyService.rb +1 -0
  50. data/lib/MonkeyService/monkey_service.rb +137 -0
  51. data/lib/tasks/engine.rb +91 -0
  52. data/monkeyengine.gemspec +32 -0
  53. data/spec/action_rules_spec.rb +59 -0
  54. data/spec/engine_spec.rb +56 -0
  55. data/spec/keyboard_char_spec.rb +12 -0
  56. data/spec/keyboard_key_spec.rb +15 -0
  57. data/spec/monkey_action_eat_spec.rb +86 -0
  58. data/spec/monkey_action_pause_spec.rb +91 -0
  59. data/spec/monkey_action_sleep_spec.rb +90 -0
  60. data/spec/monkey_action_type_spec.rb +94 -0
  61. data/spec/monkey_action_wake_spec.rb +58 -0
  62. data/spec/monkey_factory_spec.rb +23 -0
  63. data/spec/monkey_keyboard_en_us_spec.rb +42 -0
  64. data/spec/monkey_manager_spec.rb +8 -0
  65. data/spec/monkey_service_spec.rb +96 -0
  66. data/spec/monkey_spec.rb +8 -0
  67. data/spec/spec_helpers.rb +20 -0
  68. data/spec/support/shared_examples.rb +41 -0
  69. metadata +258 -0
@@ -0,0 +1 @@
1
+ require_relative 'MonkeyManager/monkey_manager'
@@ -0,0 +1,162 @@
1
+ require 'singleton'
2
+ require 'forwardable'
3
+
4
+ module MonkeyEngine
5
+
6
+ # The MonkeyManager.
7
+ #
8
+ # This class provides the functionality needed to manage Monkeys (See Monkey, MonkeyService)
9
+ #
10
+ class MonkeyManager
11
+ include Enumerable
12
+ extend Forwardable
13
+
14
+ def_delegators :@monkeys, :each
15
+
16
+ include Singleton
17
+
18
+ # The constructor
19
+ #
20
+ def initialize
21
+ @monkeys = Array.new
22
+ end
23
+
24
+ public
25
+
26
+ # Adds the Monkey to the list of Monkeys to be managed.
27
+ #
28
+ # @param (Monkey, #read) monkey The Monkey to add.
29
+ #
30
+ # @return [Monkey] the Monkey added.
31
+ #
32
+ # @raise [MonkeyEngine::Exceptions::InvalidArgumentTypeException] if parameter monkey is not a Monkey object.
33
+ # @raise [MonkeyEngine::Exceptions::UniqueObjectException] if monkey already exists.
34
+ #
35
+ def add(monkey)
36
+ raise MonkeyEngine::Exceptions::InvalidArgumentTypeException.new "Parameter 'monkey' is not Monkey object" unless monkey.is_a? Monkey
37
+ raise MonkeyEngine::Exceptions::UniqueObjectException.new "Monkeys must be unique" if exists? monkey
38
+
39
+ @monkeys.push(monkey)
40
+ monkey
41
+ end
42
+
43
+ # Returns the total count of Monkey objects being managed by this MonkeyManager.
44
+ #
45
+ # @return [int] The Monkey count.
46
+ #
47
+ def count
48
+ @monkeys.count
49
+ end
50
+
51
+ # Returns a [Boolean] indicating whether or not the Monkey exists (e.g. is currently being managed by this MonkeyManager).
52
+ #
53
+ # @param [Monkey, #read] monkey The Monkey to check.
54
+ #
55
+ # @return [Boolean] true if the Monkey is currently being manager by this MonkeyManager, false otherwise.
56
+ #
57
+ def exists?(monkey)
58
+ true unless get(monkey).nil?
59
+ end
60
+
61
+ # Returns a [Boolean] indicating whether or not the Monkey is alive (Monkey#alive?).
62
+ #
63
+ # @param [Monkey] monkey The Monkey to check.
64
+ #
65
+ # @return [Boolean] true if the Monkey is currently active (Monkey#.alive?), false otherwise.
66
+ #
67
+ def alive?(monkey)
68
+ monkey = get(monkey)
69
+ monkey.alive? unless monkey.nil?
70
+ end
71
+
72
+ # Returns the Monkey indicated by *monkey*.
73
+ #
74
+ # @param [Monkey, Symbol, String, #read] monkey The Monkey to return.
75
+ #
76
+ # @note Monkey#monkey_symbol is interrogated. If *monkey* is a [Monkey] object, Monkey#monkey_symbol is used;
77
+ # if *monkey* is a [Symbol], it is used; if *monkey* is a [String], String#to_sym is used.
78
+ #
79
+ # @return [Monkey] The Monkey if it exists.
80
+ #
81
+ # @raise [MonkeyEngine::Exceptions::InvalidArgumentTypeException] if parameter monkey is not a Monkey, Symbol, or String object.
82
+ #
83
+ def get(monkey)
84
+ return nil if @monkeys.empty?
85
+
86
+ # TODO: This seems inefficient.
87
+ return @monkeys.select { |m| m.monkey_symbol == monkey }.first if monkey.is_a? Symbol
88
+ return @monkeys.select { |m| m.monkey_symbol == monkey.to_sym }.first if monkey.is_a? String
89
+ return @monkeys.select { |m| m.monkey_symbol == monkey.monkey_symbol }.first if monkey.is_a? Monkey
90
+
91
+ raise MonkeyEngine::Exceptions::InvalidArgumentTypeException.new "Parameter 'monkey' is not a Symbol, String or Monkey object"
92
+ end
93
+
94
+ # Returns a duplicate Array of Monkey objects managed by this MonkeyManager.
95
+ #
96
+ # @return [Array] An Array of Monkey objects managed by this MonkeyManager.
97
+ #
98
+ def get_all
99
+ @monkeys.dup
100
+ end
101
+
102
+ # Removes the Monkey from the list of Monkeys being managed by this MonkeyManager.
103
+ #
104
+ # @param [Monkey] monkey The Monkey to delete.
105
+ #
106
+ # @note The Monkey to be deleted becomes unmanaged; the Monkey is NOT killed (Monkey#kill)
107
+ #
108
+ # @return [Monkey, nil] Returns the deleted Monkey, or nil if no matching Monkey was found to delete.
109
+ #
110
+ def delete(monkey)
111
+ @monkeys.delete(monkey)
112
+ end
113
+
114
+ # Suspends the calling threads execution until all Monkey threads exit, or,
115
+ # until _limit_ seconds have passed.
116
+ #
117
+ # @param [int] limit The seconds to wait for all Monkey threads to exit, or _nil_ to
118
+ # wait indefinitely.
119
+ #
120
+ # @return [Array, nil] Returns an [Array] of Monkey objects, or, nil, if no Monkey objects are being managed by
121
+ # this MonkeyManager.
122
+ #
123
+ def join_all(limit=nil)
124
+ limit = 0 unless limit.nil?
125
+ @monkeys.each { |monkey| monkey.join limit } unless @monkeys.empty?
126
+ end
127
+
128
+ # Kills the monkey.
129
+ #
130
+ # @param [Monkey, #read/#write, Symbol, String] monkey the Monkey to kill.
131
+ #
132
+ # @note The Monkey#monkey_do method executed continually by Monkey#thread is
133
+ # terminated.
134
+ #
135
+ # @return [Monkey] the Monkey that was killed.
136
+ #
137
+ def kill!(monkey)
138
+ monkey = get(monkey)
139
+ return if monkey.nil?
140
+
141
+ @monkeys.delete(monkey)
142
+ monkey.kill
143
+
144
+ monkey
145
+ end
146
+
147
+ # Kills all monkeys managed by this MonkeyManager.
148
+ #
149
+ # @note The Monkey#monkey_do method executed continually by Monkey#thread, for each Monkey is
150
+ # terminated.
151
+ #
152
+ # @return [Array] an Array of Monkey objects that were killed.
153
+ #
154
+ def kill_all!
155
+ @monkeys.each { |monkey| monkey.kill } unless @monkeys.empty?
156
+ monkeys = @monkeys.dup
157
+ @monkeys.clear
158
+
159
+ monkeys
160
+ end
161
+ end
162
+ end
@@ -0,0 +1 @@
1
+ require_relative 'MonkeyService/monkey_service'
@@ -0,0 +1,137 @@
1
+ require 'singleton'
2
+ require 'observer'
3
+ require 'forwardable'
4
+
5
+ require 'Monkey'
6
+ require 'MonkeyEngine'
7
+ require 'MonkeyManager'
8
+
9
+ module MonkeyEngine
10
+
11
+ # The monkey service.
12
+ #
13
+ # Provides the main interface for all functionality relating to
14
+ # the MonkeyEngine (see MonkeyEngine) and this gem. This class wraps
15
+ # MonkeyManager and MonkeyEngine classes.
16
+ #
17
+ class MonkeyService
18
+ include Singleton
19
+ include Observable
20
+ include Enumerable
21
+ extend Forwardable
22
+
23
+ def_delegators :@monkey_manager, :alive?, :get, :get_all, :count, :exists?, :join_all
24
+
25
+ def initialize
26
+ @monkey_manager = MonkeyEngine::MonkeyManager.instance
27
+ @engine = MonkeyEngine::Engine.instance
28
+ end
29
+
30
+ # Adds the Monkey to be managed.
31
+ #
32
+ # @param [Monkey, #read] monkey the Monkey to add.
33
+ #
34
+ # @note Observers are notified after this operation in the form of these update method params:
35
+ # ObserverKlass#update(param1, param2, param3); where param1=Time of the operation,
36
+ # param2=Symbol representing the operation (in this case, :add), param3=The following Hash:
37
+ # {monkey: [monkey]} where monkey=The Monkey added.
38
+ #
39
+ def add(monkey)
40
+ @monkey_manager.add(monkey).start
41
+ do_notify_observers(:add, {monkey: monkey})
42
+ end
43
+
44
+ # Determines if any Monkeys that are being managed by the
45
+ # underlining MonkeyManager are alive.
46
+ #
47
+ # @note A Monkey is considered alive if the Monkey#thread.alive? is true.
48
+ #
49
+ # @return [Boolean] true if any Monkeys are alive, false otherwise.
50
+ #
51
+ def any_alive?
52
+ return false if @monkey_manager.count == 0
53
+
54
+ alive_count = 0
55
+
56
+ @monkey_manager.each { |monkey| alive_count += 1 if monkey.alive? }
57
+
58
+ alive_count > 0
59
+ end
60
+
61
+ # Kills the monkey.
62
+ #
63
+ # @param [Monkey, #read/#write] monkey the Monkey to kill.
64
+ #
65
+ # @note The Monkey#monkey_do method executed continually by Monkey#thread is
66
+ # terminated.
67
+ #
68
+ # @note Observers are notified after this operation in the form of these update method params:
69
+ # ObserverKlass#update(param1, param2, param3); where param1=Time of the operation,
70
+ # param2=Symbol representing the operation (in this case, :kill!), param3=The following Hash:
71
+ # {monkey: [monkey]} where monkey=The Monkey killed.
72
+ #
73
+ def kill!(monkey)
74
+ @monkey_manager.kill!(monkey)
75
+ do_notify_observers(:kill!, {monkey: monkey})
76
+ end
77
+
78
+ # Kills all monkeys managed by the underlying MonkeyManager.
79
+ #
80
+ # @note The Monkey#monkey_do method executed continually by Monkey#thread, for each Monkey is
81
+ # terminated.
82
+ #
83
+ # @note Observers are notified after this operation in the form of these update method params:
84
+ # ObserverKlass#update(param1, param2, param3); where param1=Time of the operation,
85
+ # param2=Symbol representing the operation (in this case, :kill_all!), param3=nil.
86
+ #
87
+ # @return [Array] an Array of Monkey objects that were killed.
88
+ #
89
+ def kill_all!
90
+ monkey_array = @monkey_manager.kill_all!
91
+ do_notify_observers(:kill_all!, nil)
92
+ monkey_array
93
+ end
94
+
95
+ def new_action(monkey)
96
+ @engine.new_action monkey
97
+ end
98
+
99
+ # Performs the action against the Monkey associated with this action.
100
+ #
101
+ # @param [MonkeyActionDead, MonkeyActionEat, MonkeyActionPause, MonkeyActionSleep, MonkeyActionType, MonkeyActionWake, etc., #read/#write]
102
+ # action the action to apply against the Monkey associated with this action.
103
+ #
104
+ # @note Observers are notified after this operation in the form of these update method params:
105
+ # ObserverKlass#update(param1, param2, param3); where param1=Time of the operation,
106
+ # param2=Symbol representing the operation (in this case, :action_complete), param3=The following Hash:
107
+ # {action: [action]} where action=The action that was completed.
108
+ #
109
+ # @return (Boolean) true if the action performed has been completed (Action#action_completed?), false otherwise.
110
+ #
111
+ def monkey_do(action)
112
+ do_notify_observers(:action_completed, {action: action}) if @engine.do_action action
113
+ end
114
+
115
+ # Evaluates the given action, to determine whether or not the action is completed (Action#action_completed?).
116
+ #
117
+ # @param [MonkeyActionDead, MonkeyActionEat, MonkeyActionPause, MonkeyActionSleep, MonkeyActionType, MonkeyActionWake, etc., #read]
118
+ # action the action to interrogate as to whether or not it is completed (Action#action_completed?).
119
+ #
120
+ # @return (Boolean) true if the action is completed (Action#action_completed?), false otherwise.
121
+ #
122
+ def action_eval!(action)
123
+ @engine.action_eval! action
124
+ end
125
+
126
+ private
127
+
128
+ # Notifies all observers.
129
+ #
130
+ def do_notify_observers(param1, param2)
131
+ changed
132
+ notify_observers(Time.now, param1, param2)
133
+ end
134
+
135
+ end
136
+
137
+ end
@@ -0,0 +1,91 @@
1
+ require 'pry'
2
+
3
+ require 'yaml'
4
+ require "minitest/autorun"
5
+ require 'LittleWeasel'
6
+
7
+ require File.expand_path('../../MonkeyFactory', __FILE__)
8
+ require File.expand_path('../../MonkeyEngine', __FILE__)
9
+
10
+ module Runner
11
+
12
+ class MonkeyRun
13
+ attr_reader :monkey_service, :runtime, :thread, :words
14
+
15
+ def initialize(monkey_service, runtime)
16
+ @words = Array.new
17
+ @monkey_service = monkey_service
18
+ @runtime = runtime.to_i
19
+
20
+ @monkey_service.add_observer self
21
+
22
+ @thread = Thread.new {
23
+ go
24
+ sleep @runtime
25
+ }
26
+
27
+ self
28
+ end
29
+
30
+ #protected
31
+
32
+ def go
33
+ @monkey_service.add(MonkeyFactory::create :groucho)
34
+ @monkey_service.add(MonkeyFactory::create :harpo)
35
+ @monkey_service.add(MonkeyFactory::create :chico)
36
+ @monkey_service.add(MonkeyFactory::create :zeppo)
37
+ end
38
+
39
+ def update(time, action, param)
40
+ begin
41
+
42
+ return unless param.is_a?(Hash) && param.has_key?(:action)
43
+
44
+ if param[:action].is_a?(MonkeyActionType) && action == :action_completed
45
+
46
+ monkey = param[:action].monkey.monkey_symbol.to_s
47
+ is_word = param[:action].keyboard_input.is_word
48
+ word = param[:action].keyboard_input.input_to_s
49
+
50
+ #if param[:action].is_a?(MonkeyActionType) && action == :action_completed
51
+ puts "Monkey: [#{monkey.capitalize}] | Is Word: [#{is_word}] | Value: [#{word.capitalize}]"
52
+ #end
53
+
54
+ if is_word
55
+ @words << { word: word, monkey: monkey }
56
+ end
57
+ end
58
+ rescue Exception => e
59
+ puts "Exception: #{e}"
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+ namespace :engine do
67
+ desc "Run the MonkeyEngine"
68
+ task :run do
69
+ runtime = 10 #ARGV[0]
70
+
71
+ service = MonkeyEngine::MonkeyService.instance
72
+
73
+ LittleWeasel::Checker.instance.options = {exclude_alphabet: true, strip_whitespace: false, ignore_numeric: false}
74
+
75
+ runner = Runner::MonkeyRun.new service, runtime
76
+ runner.thread.join
77
+
78
+ service.kill_all!
79
+ service.join_all(10)
80
+
81
+ puts "Total valid words: #{runner.words.count}"
82
+
83
+ runner.words.sort!{|a,b| a[:monkey]<=>b[:monkey]}
84
+
85
+ runner.words.each { |word|
86
+ puts "Monkey [#{word[:monkey].capitalize}] typed [#{word[:word].capitalize}]"
87
+ }
88
+ end
89
+ end
90
+
91
+
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'MonkeyEngine/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "MonkeyEngine"
8
+ spec.version = MonkeyEngine::VERSION
9
+ spec.authors = ["Gene M. Angelo, Jr."]
10
+ spec.email = ["public.gma@gmail.com"]
11
+ spec.description = %q{MonkeyEngine - FUN!}
12
+ spec.summary = %q{The engine that drives my monkeys!}
13
+ spec.homepage = "http://www.geneangelo.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = '~> 2.1'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'rake', '~> 0'
25
+ spec.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0'
26
+ spec.add_development_dependency 'yard', '0.8.6.2'
27
+ spec.add_development_dependency 'redcarpet', '~> 2.3', '>= 2.3.0'
28
+ spec.add_development_dependency 'pry'
29
+
30
+ spec.add_runtime_dependency 'LittleWeasel', '~> 3.0'
31
+ spec.add_runtime_dependency 'ProtectedConstructor', '~> 2.0', '>=2.0.0'
32
+ end
@@ -0,0 +1,59 @@
1
+ require_relative 'spec_helpers'
2
+
3
+ require 'Monkey'
4
+ require 'MonkeyFactory'
5
+ require 'MonkeyEngine'
6
+ require 'MonkeyManager'
7
+ require 'MonkeyActions'
8
+ require_relative '../lib/MonkeyKeyboard/keyboard_input'
9
+
10
+ describe 'ActionRules' do
11
+
12
+ before(:each) do
13
+ end
14
+
15
+ after(:all) do
16
+ end
17
+
18
+ context 'get_next_action' do
19
+
20
+ it 'should throw an exception if the action is not completed' do
21
+ monkey = MonkeyFactory::create :groucho
22
+
23
+ monkey.extend(SpecHelpers::SetMonkeyAction)
24
+
25
+ keyboard_input = KeyboardInput.new
26
+ keyboard_input.input = %w{H e l l o}
27
+
28
+ action = MonkeyActionType.new(monkey, keyboard_input)
29
+ action.action_completed = false
30
+
31
+ monkey.set_action(action)
32
+
33
+ lambda { ActionRules.instance.get_next_action monkey }.should raise_error MonkeyEngine::Exceptions::InvalidOperationException
34
+ end
35
+
36
+ it 'should get correct action if current action is nil?' do
37
+ monkey = MonkeyFactory::create :groucho
38
+
39
+ monkey.extend(SpecHelpers::SetMonkeyAction)
40
+
41
+ monkey.set_action(nil)
42
+
43
+ ActionRules.instance.get_next_action(monkey).is_a?(MonkeyActionWake).should == true
44
+ end
45
+
46
+ it 'should get correct action if current action is MonkeyActionSleep' do
47
+ monkey = MonkeyFactory::create :groucho
48
+
49
+ monkey.extend(SpecHelpers::SetMonkeyAction)
50
+
51
+ action = MonkeyActionSleep.new(monkey, 6 * 60)
52
+ action.action_completed = true
53
+
54
+ monkey.set_action(action)
55
+
56
+ ActionRules.instance.get_next_action(monkey).is_a?(MonkeyActionWake).should == true
57
+ end
58
+ end
59
+ end