actor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 692bbec40fb07dccc05300db738a52d61779194d
4
+ data.tar.gz: fd21f5720cca18985abda493c3bdc55886b4ec71
5
+ SHA512:
6
+ metadata.gz: 27afa42688e8e9608dac5ecbffda7a677ef46d94ab192f5dbe8f9b10c88ab1f2a2ca5d0be40844e22fe603ecffa8806459602a8ad556053f5c8ec6282de07b59
7
+ data.tar.gz: 975d6e1f537467f2cd21c2efe9397c9dd0519337ed236b163ad6da55a6cf59a654b366bd4b4b59b4a08e0bfd28e4429a197fadff45c5a39572506f21ed681cf6
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+ /html/
23
+
24
+ ## Environment normalisation:
25
+ /.bundle/
26
+ /lib/bundler/man/
27
+
28
+ Gemfile.lock
29
+ .ruby-version
30
+ .ruby-gemset
31
+
32
+ .rvmrc
33
+
34
+ .idea/
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ cache: bundler
3
+
4
+ gemfile:
5
+ - gemfiles/Gemfile.travis
6
+ rvm:
7
+ - 2.0.0
8
+ - 1.9.3
9
+ - jruby-19mode
10
+ - rbx-2
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in actor.gemspec
4
+ gemspec path: File.expand_path('../', __FILE__)
5
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Max
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.
@@ -0,0 +1,114 @@
1
+ # Actor
2
+
3
+ [![Build Status](https://travis-ci.org/maxgale/actor.svg?branch=master)](https://travis-ci.org/maxgale/actor)
4
+
5
+ The actor gem provides a completely implicit implementation of the actor pattern. The goal of the library is to provide
6
+ an easy way to implement fast, concurrent code without having to worry about race conditions or unexpected side-effects.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'actor'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install actor
21
+
22
+ ## Usage
23
+
24
+ ### Actors
25
+
26
+ require 'actor/base'
27
+
28
+ class MyActor
29
+ include Actor::Base
30
+
31
+ def example_method
32
+ 'hi'
33
+ end
34
+ end
35
+
36
+ my_actor = MyActor.new
37
+
38
+ That's it! Now any interations with `my_actor` will be executed concurrently. It's also worth noting that there are no
39
+ return values. Instead, code is executed via message passing. Instead, there are callbacks.
40
+
41
+ ### Callbacks
42
+
43
+ my_actor.before_action :example_method do
44
+ # Code here is executed before :example_method is executed by the actor
45
+ puts 'before'
46
+ end
47
+
48
+ my_actor.after_action :example_method do |result|
49
+ # Code here is executed after :example_method is executed by the actor
50
+ puts 'after'
51
+ puts result
52
+ end
53
+
54
+ my_actor.example_method
55
+
56
+ => before
57
+ => after
58
+ => hi
59
+
60
+ ### Timers
61
+
62
+ Another useful feature is the timer. The timer is an object that periodically executes a block of code.
63
+
64
+ require 'actor/timer'
65
+
66
+ # Create a timer the executes every 1/30th of a second. Only executes twice.
67
+ Actor::Timer.new 0.033, 2 do
68
+ puts 'hi'
69
+ end
70
+
71
+ => hi
72
+ => hi
73
+
74
+ Passing in `0` as the number of iterations to the timer causes it to execute indefinitely. You can also pause, resume,
75
+ and wait for timers to finish execution.
76
+
77
+ my_timer = Timer.new 0.033, 0 do
78
+ # Do periodic work
79
+ end
80
+
81
+ my_timer.pause # Temporarily stop work
82
+
83
+ my_timer.resume # Resume the work
84
+
85
+ my_timer.wait # Since `iterations = 0`, this will block forever
86
+
87
+ ## Gotchas
88
+
89
+ Unfortunately, there are a few "gotchas" when using this gem.
90
+
91
+ 1. `Actor::Base` overrides the including class's `:new` method and renames it to `:__actor_new`. This sets up the
92
+ possibility of naming conflicts.
93
+ 1. `Actor::Base` overrides `:send`, making it impossible to use `:send` without concurrent execution and callbacks.
94
+ 1. All actors are wrapped in a proxy class. This proxy forwards all methods to the
95
+ instance to be executed in a concurrent way. This means it is impossible to execute code normally when handling the
96
+ proxy. You can access the underlying instance by calling `:__proxy_target` on the proxy. Note: No callbacks are not
97
+ triggered when accessing the instance directly (unless you use send).
98
+ 1. The timer period is counted from the end of the last block to the start of the next block. This means that the timer
99
+ firing is very approximate and definitely not designed for blocking code.
100
+
101
+ ## Todo
102
+
103
+ 1. Using a thread pool would be nice.
104
+ 2. Benchmarks comparing MRI 2.1.2 vs. Rubinisu 2.2.6.
105
+ 3. Benchmarks comparing of this gem vs. Celluloid
106
+ 5. Add a way of using actors over a network via RPC
107
+
108
+ ## Contributing
109
+
110
+ 1. Fork it ( https://github.com/maxgale/actor/fork )
111
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
112
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
113
+ 4. Push to the branch (`git push origin my-new-feature`)
114
+ 5. Create a new Pull Request
@@ -0,0 +1,14 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rdoc/task'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new :spec do |task|
6
+ task.rspec_opts = %w(--color --format nested)
7
+ end
8
+
9
+ RDoc::Task.new :rdoc do |rdoc|
10
+ rdoc.rdoc_files.include "lib/**/*.rb"
11
+ rdoc.options << "--all"
12
+ end
13
+
14
+ task default: [:spec, :rdoc]
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'actor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "actor"
8
+ spec.version = Actor::VERSION
9
+ spec.authors = ["Max Gale"]
10
+ spec.email = ["maxgale4@gmail.com"]
11
+ spec.summary = %q{A simple implementation of the actor pattern}
12
+ spec.homepage = "https://github.com/maxgale/actor"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rake", "~> 10.3"
22
+ spec.add_development_dependency "rspec", "~> 2.14"
23
+ spec.add_development_dependency "rdoc", "~> 4.1"
24
+ end
@@ -0,0 +1,5 @@
1
+ platforms :rbx do
2
+ gem 'rubysl', '~> 2.0'
3
+ end
4
+
5
+ eval_gemfile File.expand_path('../../Gemfile', __FILE__)
@@ -0,0 +1,11 @@
1
+ require 'actor/base'
2
+ require 'actor/proxy'
3
+ require 'actor/timer'
4
+ require 'actor/version'
5
+
6
+ ##
7
+ # Module containing actor functionality. See Actor::Base, Actor::Proxy, and
8
+ # Actor::Timer for specific functionality.
9
+ module Actor
10
+
11
+ end
@@ -0,0 +1,89 @@
1
+ require 'set'
2
+ require 'actor/proxy'
3
+
4
+ module Actor
5
+ ##
6
+ # Module that actors should include
7
+ module Base
8
+ ##
9
+ # Adds a new message to the queue. Args and block are optional
10
+ #
11
+ # * *Args*:
12
+ # - +method_name+: symbol representation of the action (method) to preform
13
+ # - +args+: the arguments to pass to the action when it is executed
14
+ # - +block+: the block to pass to the action when it is executed
15
+ def send method_name, *args, &block
16
+ @mailbox << [method_name, args, block]
17
+ end
18
+
19
+ ##
20
+ # Adds a listener that is called before the including object performs
21
+ # the specified action.
22
+ #
23
+ # * *Args*:
24
+ # - +action+: the action (symbol) the hook into
25
+ # - +block+: the block to execute before the specified action
26
+ def before_action action, &block
27
+ @audience[:before][action] ||= Set.new
28
+ @audience[:before][action] << block
29
+ end
30
+
31
+ ##
32
+ # Adds a listener that is called after the including object performs
33
+ # the specified action.
34
+ #
35
+ # * *Args*:
36
+ # - +action+: the action the hook into
37
+ # - +block+: the block to execute after the specified action
38
+ # * *Yields*: the value returned by the action
39
+ def after_action action, &block
40
+ @audience[:after][action] ||= Set.new
41
+ @audience[:after][action] << block
42
+ end
43
+
44
+ ##
45
+ # Adds a hook before object initialization to automatically sets up the
46
+ # thread that executes actions and sets up the callback data structures.
47
+ #
48
+ # * *Args*:
49
+ # - +klass+: the class whose initialization is hooked into.
50
+ def self.included klass
51
+ class << klass
52
+ alias_method :__actor_new, :new
53
+
54
+ ##
55
+ # Hooks into the initialization of the object to initialize the
56
+ # mailbox. Also starts the thread executing async method calls
57
+ def new *args
58
+ instance = __actor_new *args
59
+ instance.instance_variable_set :@audience, {}
60
+ instance.instance_variable_set :@mailbox, Queue.new
61
+
62
+ audience = {}
63
+ audience[:before] = {}
64
+ audience[:after] = {}
65
+ instance.instance_variable_set :@audience, audience
66
+
67
+ Thread.new do
68
+ loop do
69
+ mailbox = instance.instance_variable_get :@mailbox
70
+ method_name, args, block = mailbox.pop
71
+
72
+ if audience[:before][method_name]
73
+ audience[:before][method_name].each { |callback| callback.call }
74
+ end
75
+
76
+ result = instance.method(method_name).call *args, &block
77
+
78
+ if audience[:after][method_name]
79
+ audience[:after][method_name].each { |callback| callback.yield result }
80
+ end
81
+ end
82
+ end
83
+
84
+ Proxy.new instance
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,46 @@
1
+ module Actor
2
+ ##
3
+ # The proxy class wraps an object and invokes all of its method using :send
4
+ class Proxy < BasicObject
5
+ ##
6
+ # Create a new proxy
7
+ #
8
+ # * *Args*:
9
+ # - +proxy_target+: the instance to proxy
10
+ def initialize proxy_target
11
+ @proxy_target = proxy_target
12
+ end
13
+
14
+ ##
15
+ # Proxies the method call to the proxy target using :send
16
+ def method_missing name, *args, &block
17
+ @proxy_target.send name, *args, &block
18
+ end
19
+
20
+ ##
21
+ # Get the proxy target of this proxy
22
+ #
23
+ # * *Returns*: the proxy target
24
+ def __proxy_target
25
+ @proxy_target
26
+ end
27
+
28
+ ##
29
+ # Overrides the proxy equals to pass the equality check to the proxy target
30
+ def == other
31
+ @proxy_target == other
32
+ end
33
+
34
+ ##
35
+ # Overrides the proxy equals to pass the equality check to the proxy target
36
+ def != other
37
+ @proxy_target != other
38
+ end
39
+
40
+ ##
41
+ # Overrides the proxy equals to pass the equality check to the proxy target
42
+ def equal? other
43
+ @proxy_target.equal? other
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,46 @@
1
+ module Actor
2
+ ##
3
+ # Simple timer implementation
4
+ class Timer
5
+ ##
6
+ # Create a new timer that fires every <period> seconds. The number of
7
+ # iterations specifies how many times the timer should fire.
8
+ #
9
+ # * *Args*:
10
+ # - +period+: the time, in seconds, between firing the timer.
11
+ # - +iterations+: the number times the timer should fire. 0 iterations
12
+ # means fire infinitely
13
+ def initialize period, iterations, &block
14
+ @pause_queue = Queue.new
15
+
16
+ i = iterations == 0 ? 1.0 / 0.0 : iterations
17
+ @timer_thread = Thread.new do
18
+ (1..i).step do
19
+ sleep unless @pause_queue.empty?
20
+ block.call
21
+ sleep period
22
+ end
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Pauses the timer. The currently executing iteration is finished before
28
+ # the time is paused
29
+ def pause
30
+ @pause_queue << :paused
31
+ end
32
+
33
+ ##
34
+ # Resumes the timer
35
+ def resume
36
+ @pause_queue.clear
37
+ @timer_thread.wakeup
38
+ end
39
+
40
+ ##
41
+ # Block the current thread until the timer has finished executing
42
+ def wait
43
+ @timer_thread.join
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ module Actor
2
+ ##
3
+ # Version number (semantic versioning)
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe Actor::Base do
4
+ before :each do
5
+ @actor_class = Class.new do
6
+ include Actor::Base
7
+
8
+ def action
9
+ end
10
+ end
11
+ end
12
+
13
+ it 'initializes mailbox on creation' do
14
+ proxy = @actor_class.new
15
+ actor = proxy.__proxy_target
16
+
17
+ expect(actor.instance_variable_get :@mailbox).not_to be nil
18
+ end
19
+
20
+ it 'initializes mailbox on creation of subclass' do
21
+ child_actor = Class.new(@actor_class).new.__proxy_target
22
+ expect(child_actor.instance_variable_get :@mailbox).not_to be nil
23
+ end
24
+
25
+ it 'receives proxied messages' do
26
+ Thread.stub :new
27
+
28
+ proxy = @actor_class.new
29
+ actor = proxy.__proxy_target
30
+
31
+ proxy.message1
32
+ proxy.message2 :arg1, :arg2
33
+
34
+ messages = actor.instance_variable_get :@mailbox
35
+ expect(messages.size).to be 2
36
+ expect(messages.pop).to eq [:message1, [], nil]
37
+ expect(messages.pop).to eq [:message2, [:arg1, :arg2], nil]
38
+ end
39
+
40
+ it 'processes messages' do
41
+ proxy = @actor_class.new
42
+ actor = proxy.__proxy_target
43
+
44
+ actor.should_receive :message1
45
+ actor.should_receive(:message2).with :arg1
46
+ actor.should_receive(:message3).with :arg1, :arg2
47
+
48
+ proxy.message1
49
+ proxy.message2 :arg1
50
+ proxy.message3 :arg1, :arg2
51
+
52
+ # Sleep because threads are lame.
53
+ sleep 0.1
54
+
55
+ messages = actor.instance_variable_get :@mailbox
56
+ expect(messages.size).to be 0
57
+ end
58
+
59
+ it 'executes before/after callbacks' do
60
+ proxy = @actor_class.new
61
+ actor = proxy.__proxy_target
62
+
63
+ proxy.before_action :action do
64
+ actor.before_method
65
+ end
66
+
67
+ ret_value = nil
68
+ proxy.after_action :action do |result|
69
+ ret_value = result
70
+ actor.after_method
71
+ end
72
+
73
+ actor.should_receive(:before_method).ordered
74
+ actor.should_receive(:action).ordered.and_return('ret_value')
75
+ actor.should_receive(:after_method).ordered
76
+
77
+ proxy.action
78
+ sleep 0.1
79
+
80
+ expect(ret_value).to eq 'ret_value'
81
+ end
82
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Actor::Proxy do
4
+ before :each do
5
+ @target = Object.new
6
+ @proxy = Actor::Proxy.new @target
7
+ end
8
+
9
+ it 'sets the proxy target' do
10
+ expect(@proxy.instance_eval '@proxy_target').to be @target
11
+ end
12
+
13
+ it 'preserves equality' do
14
+ expect(@proxy).to eq @target
15
+ expect(@proxy != @target).to be false
16
+ expect(@proxy.equal? @target).to be true
17
+ end
18
+
19
+ it 'gets the proxy target' do
20
+ expect(@proxy.__proxy_target).to be @target
21
+ end
22
+
23
+ it 'forwards messages via send' do
24
+ @target.should_receive(:send).with :method, :to_s
25
+ @proxy.method :to_s
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ require 'actor'
2
+
3
+ Thread.abort_on_exception = true
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Actor::Timer do
4
+ it 'executes for the given number of iterations' do
5
+ iterations = 0
6
+ Actor::Timer.new 0.033, 30 do
7
+ iterations += 1
8
+ end.wait
9
+
10
+ expect(iterations).to be 30
11
+ end
12
+
13
+ it 'fires every period' do
14
+ deltas = []
15
+ last_time = Time.now - 0.033
16
+
17
+ Actor::Timer.new 0.033, 30 do
18
+ deltas << (Time.now - last_time).to_f
19
+ last_time = Time.now
20
+ end.wait
21
+
22
+ deltas.each { |delta| expect(delta).to be_within(0.01).of(0.033) }
23
+ end
24
+
25
+ it 'pauses and resumes' do
26
+ long_pointless_sum = 0
27
+ timer = Actor::Timer.new 0.033, 3000 do
28
+ long_pointless_sum += 1
29
+ end
30
+
31
+ timer.pause
32
+ timer.pause # Verify that two messages get queued
33
+ post_pause_sum = long_pointless_sum
34
+ sleep 1
35
+
36
+ pause_queue = timer.instance_variable_get(:@pause_queue)
37
+
38
+ expect(long_pointless_sum).to be post_pause_sum
39
+ expect(timer.instance_variable_get(:@timer_thread).status).to eq 'sleep'
40
+ expect(pause_queue.pop).to be :paused
41
+ expect(pause_queue.pop).to be :paused
42
+ expect(pause_queue).to be_empty
43
+
44
+ pause_queue.should_receive :clear
45
+ timer.resume
46
+ sleep 1
47
+
48
+ expect(long_pointless_sum).to be > post_pause_sum
49
+ end
50
+
51
+ it 'blocks the calling thread when waiting' do
52
+ side_effect = false
53
+ Actor::Timer.new 0.01, 1 do
54
+ sleep 1
55
+ side_effect = true
56
+ end.wait
57
+
58
+ expect(side_effect).to be true
59
+ end
60
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: actor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Max Gale
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2014-05-12 00:00:00 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ prerelease: false
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: "1.6"
22
+ type: :development
23
+ version_requirements: *id001
24
+ - !ruby/object:Gem::Dependency
25
+ name: rake
26
+ prerelease: false
27
+ requirement: &id002 !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ version: "10.3"
32
+ type: :development
33
+ version_requirements: *id002
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id003 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: "2.14"
42
+ type: :development
43
+ version_requirements: *id003
44
+ - !ruby/object:Gem::Dependency
45
+ name: rdoc
46
+ prerelease: false
47
+ requirement: &id004 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ~>
50
+ - !ruby/object:Gem::Version
51
+ version: "4.1"
52
+ type: :development
53
+ version_requirements: *id004
54
+ description:
55
+ email:
56
+ - maxgale4@gmail.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files: []
62
+
63
+ files:
64
+ - .gitignore
65
+ - .travis.yml
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - actor.gemspec
71
+ - gemfiles/Gemfile.travis
72
+ - lib/actor.rb
73
+ - lib/actor/base.rb
74
+ - lib/actor/proxy.rb
75
+ - lib/actor/timer.rb
76
+ - lib/actor/version.rb
77
+ - spec/base_spec.rb
78
+ - spec/proxy_spec.rb
79
+ - spec/spec_helper.rb
80
+ - spec/timer_spec.rb
81
+ homepage: https://github.com/maxgale/actor
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+
86
+ post_install_message:
87
+ rdoc_options: []
88
+
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - &id005
94
+ - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: "0"
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - *id005
100
+ requirements: []
101
+
102
+ rubyforge_project:
103
+ rubygems_version: 2.2.2
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: A simple implementation of the actor pattern
107
+ test_files:
108
+ - spec/base_spec.rb
109
+ - spec/proxy_spec.rb
110
+ - spec/spec_helper.rb
111
+ - spec/timer_spec.rb