robinhood 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 913bb9ef8276fd39f9c3ba6f1ad43548091601d9
4
- data.tar.gz: fbb34da42c7ccc99289a41f5fc38495a9360f373
3
+ metadata.gz: d96b9f7a32f3b3a394c6601f4846040f22395c17
4
+ data.tar.gz: 859e805401593b304091c8f1cba88afe768a3e58
5
5
  SHA512:
6
- metadata.gz: e09bdea365ad31009265edf0cf7607a0ca47c4b26464a646f35d94a1f556c196c9e3f2fec91297a41968bf1f9a0379adfdfc57adc6ca84b49efc8550a1525de5
7
- data.tar.gz: 0e1aaae54ee25b24b35addf173a1f26c94bdd04cf9314ec5fade00e944e61ca106196dafd57caca42b700b415d5bb4e6af6a93fb35e0f57e174db61aec7d1d0c
6
+ metadata.gz: 2ffc6557319047d20031476ed520953999f7da88ebf321a68bfb97dfd5b4a6f71b4ddc8468c0a96b80c7623e881b27df3fcb5529361d4795706153b3083a4485
7
+ data.tar.gz: 01a789dd9627fac4f2d259fb429f1fac7da3266f11e6fa04f02e36fe247b7cfd9c748c0b8dd72fbcc93d7ff2f64613be58f04770a373a92f9ec36a9bf0ee56ea
data/.gitignore CHANGED
@@ -15,3 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ log
19
+ dump.rdb
data/.travis.yml CHANGED
@@ -1,5 +1,7 @@
1
1
  rvm:
2
2
  - 1.9.3
3
3
  - 2.0.0
4
- - rbx-19mode
5
- - jruby-19mode
4
+ - rbx-2.1.1
5
+
6
+ services:
7
+ - redis
data/README.md CHANGED
@@ -9,9 +9,28 @@ the poor.
9
9
  It leverages celluloid actors for each process and uses Redis as a locking
10
10
  mechanism to ensure the process is run in a single server.
11
11
 
12
+ ## Compatibility
13
+
14
+ Robinhood works on MRI 1.9.3 and upwards, and rubinius 2.1 and upwards. We
15
+ don't intend to support JRuby at the moment because it lacks support for
16
+ Process#fork which is needed in order to run robinhood as a daemon.
17
+
18
+ We could make daemonizing a separate module if there was enough interest,
19
+ though.
20
+
12
21
  ## Usage
13
22
 
23
+ Install the gem:
24
+
25
+ ```
26
+ $ gem install robinhood
27
+ ```
28
+
29
+ Create a `Robinhood` file in your root:
30
+
14
31
  ```ruby
32
+ require 'your-app'
33
+
15
34
  Robinhood.define do
16
35
  redis{ Redis.new(:host => "10.0.1.1", :port => 6380) }
17
36
 
@@ -23,8 +42,31 @@ Robinhood.define do
23
42
  Sweeper.sweep!
24
43
  end
25
44
  end
45
+ ```
46
+
47
+ Launch robinhood on the foreground:
48
+
49
+ ```
50
+ $ robinhood
51
+ ```
52
+
53
+ Launch robinhood in a daemonized way:
54
+
55
+ ```
56
+ $ robinhood start
57
+ ```
26
58
 
27
- Robinhood.run
59
+ Stop or restart a daemonized robinhood:
60
+
61
+ ```
62
+ $ robinhood stop
63
+ $ robinhood restart
64
+ ```
65
+
66
+ You can also append options to robinhood's executable:
67
+
68
+ ```
69
+ $ robinhood -c config.rb --pids-path /var/run --log-path /var/log
28
70
  ```
29
71
 
30
72
  ## How does it work?
data/Rakefile CHANGED
@@ -3,4 +3,8 @@ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task default: :spec
6
+ task 'spinach' do
7
+ sh 'spinach'
8
+ end
9
+
10
+ task default: [:spec, :spinach]
data/bin/robinhood ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ require 'robinhood'
3
+ require 'robinhood/daemon'
4
+ require 'robinhood/daemon/options'
5
+
6
+ # This executable is responsible of starting robinhood as a daemon, its basic usage is:
7
+ #
8
+ # robinhood start
9
+ #
10
+ # This order will start a daemon with a file called robinhood.rb as the Robinhood instance
11
+ #
12
+ # Some options can be provided in order to modify the default behaviour, those are:
13
+ #
14
+ # robinhood start --pids-path /tmp/pids
15
+ # robinhood stop --log-path /tmp/logs
16
+ # robinhood -c config_file
17
+ #
18
+ # To stop the daemon we will execute:
19
+ #
20
+ # robinhood stop
21
+ #
22
+ # To run it as a foreground process:
23
+ #
24
+ # robinhood run
25
+ #
26
+
27
+ # Extract the arguments
28
+ options = Robinhood::Daemon::Options.new(ARGV).parse
29
+
30
+ options[:command] ||= 'run'
31
+
32
+ # Create the Daemonize object with the extracted arguments
33
+ daemon = Robinhood::Daemon.new(options)
34
+
35
+ if daemon.respond_to?(options[:command])
36
+ daemon.send(options[:command])
37
+ else
38
+ $stderr.puts "Robinhood has no command #{options[:command]}"
39
+ end
@@ -0,0 +1,32 @@
1
+ class Spinach::Features::Throttling < Spinach::FeatureSteps
2
+ include Spinach::Robinhood
3
+
4
+ step 'I have a Robinhood file with a throttling ratio set at 3 seconds' do
5
+ @file_name = File.expand_path('./tmp/robin_tmp')
6
+ write_robinhood %Q{
7
+ Robinhood.define do
8
+ redis { Redis.new(database: 9) }
9
+ process :test, throttle: 3 do
10
+ File.open('#{@file_name}', 'a') do |f|
11
+ f.write("yeah")
12
+ end
13
+ end
14
+ end
15
+ }
16
+ end
17
+
18
+ step 'I run robinhood for 4 seconds' do
19
+ `bin/robinhood start -c tmp/Robinhood`
20
+ sleep 4
21
+ `bin/robinhood stop -c tmp/Robinhood`
22
+ end
23
+
24
+ step 'It has run less than 10 times' do
25
+ expect(File.read(@file_name).split('yeah').length).to be < 10
26
+ end
27
+
28
+ after do
29
+ FileUtils.rm(@file_name)
30
+ FileUtils.rm('tmp/Robinhood')
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ require 'rspec'
2
+
3
+ Spinach::FeatureSteps.include(RSpec::Matchers)
@@ -0,0 +1,10 @@
1
+ require 'fileutils'
2
+
3
+ module Spinach::Robinhood
4
+ def write_robinhood(content)
5
+ FileUtils.mkdir_p('tmp')
6
+ File.open('tmp/Robinhood', 'w') do |f|
7
+ f.write(content)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ Feature: Throttling
2
+ In order to limit the execution rate of a process
3
+ As a developer
4
+ I want to be able to specify throttling rates
5
+
6
+ Scenario: Throttle a process
7
+ Given I have a Robinhood file with a throttling ratio set at 3 seconds
8
+ When I run robinhood for 4 seconds
9
+ Then It has run less than 10 times
data/lib/robinhood.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require "robinhood/version"
2
2
  require "robinhood/dsl"
3
3
  require "robinhood/runtime"
4
- require "celluloid/autostart"
5
4
 
6
5
  module Robinhood
7
6
  # Public: The starting point for Robinhood's DSL.
@@ -0,0 +1,77 @@
1
+ require 'robinhood'
2
+ require 'daemons'
3
+
4
+ module Robinhood
5
+ # This class will start robinhood as a daemon
6
+ #
7
+ class Daemon
8
+ attr_reader :file, :pids_path, :log_path
9
+
10
+ # Public: Instantiates a new Robinhood::Daemon object
11
+ #
12
+ # args - Hash with the different options that can be supplied to the Daemonize object
13
+ # :config_file - File that will contain the robinhood tasks, by default it will be robinhood.rb
14
+ # :pids_path - Dir to store de Pidfile for the daemon, by default it will be 'tmp/pids'
15
+ # :log_path - Dir to store the logs for the daemon, by default it will be 'log'
16
+ #
17
+ # Returns a new Robinhood::Daemonize
18
+ def initialize(options={})
19
+ @options = default_options.merge(options)
20
+ @file = File.expand_path(@options[:config_file])
21
+ @pids_path = File.expand_path(@options[:pids_path])
22
+ @log_path = File.expand_path(@options[:log_path])
23
+ end
24
+
25
+ # Public: Start the daemon
26
+ #
27
+ # Returns nothing
28
+ def start
29
+ definition = File.read(file)
30
+
31
+ FileUtils.mkdir_p(log_path)
32
+ FileUtils.mkdir_p(pids_path)
33
+
34
+ Daemons.run_proc(filename, daemon_options.merge(ARGV: [@options[:command]])) do
35
+ instance_eval(definition, @file)
36
+ Robinhood.run
37
+ end
38
+ end
39
+ alias :run :start
40
+
41
+ def stop
42
+ Daemons.run_proc(filename, daemon_options.merge(ARGV: ['stop']))
43
+ end
44
+
45
+ def restart
46
+ stop
47
+ start
48
+ end
49
+
50
+ private
51
+
52
+ def default_options
53
+ {
54
+ command: 'start',
55
+ config_file: 'Robinhood',
56
+ log_path: 'log',
57
+ pids_path: File.join("tmp", "pids")
58
+ }
59
+ end
60
+
61
+ def daemon_options
62
+ {
63
+ dir_mode: :normal,
64
+ dir: pids_path,
65
+ monitor: true,
66
+ multiple: true,
67
+ app_name: 'robinhood',
68
+ log_dir: log_path,
69
+ log_output: true
70
+ }
71
+ end
72
+
73
+ def filename
74
+ File.basename file
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,34 @@
1
+ require_relative '../daemon'
2
+ require 'optparse'
3
+
4
+ class Robinhood::Daemon::Options
5
+ def initialize(data)
6
+ @data = data.dup
7
+ end
8
+
9
+ def parse
10
+ options = {}
11
+ parser(options).parse!(@data)
12
+ options[:command] = @data[0]
13
+
14
+ options
15
+ end
16
+
17
+ private
18
+
19
+ def parser(result)
20
+ OptionParser.new do |opts|
21
+ opts.on('-c', "--config FILE", String) do |file|
22
+ result[:config_file] = file
23
+ end
24
+
25
+ opts.on('--pids-path PATH') do |dir|
26
+ result[:pids_path] = dir
27
+ end
28
+
29
+ opts.on('--log-path PATH') do |dir|
30
+ result[:log_path] = dir
31
+ end
32
+ end
33
+ end
34
+ end
@@ -44,7 +44,7 @@ module Robinhood
44
44
  return unless lock
45
45
 
46
46
  begin
47
- time = Benchmark.realtime{ @block.call }
47
+ time = Benchmark.realtime{ instance_eval(&@block) }
48
48
  if difference = throttle - time
49
49
  sleep(difference)
50
50
  end
@@ -35,6 +35,8 @@ module Robinhood
35
35
  #
36
36
  # Returns the Runtime.
37
37
  def run(options = {})
38
+ Celluloid.start
39
+
38
40
  setup_supervision_group
39
41
  Mutex.db = redis
40
42
 
@@ -1,3 +1,3 @@
1
1
  module Robinhood
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/robinhood.gemspec CHANGED
@@ -20,10 +20,13 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency 'celluloid'
22
22
  spec.add_dependency 'redis-mutex'
23
+ spec.add_dependency 'daemons'
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.3"
25
26
  spec.add_development_dependency "rake"
26
27
  spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "spinach", ">= 0.8.7"
27
29
  spec.add_development_dependency "pry"
28
30
  spec.add_development_dependency "pry-nav"
31
+ spec.add_development_dependency "fakefs"
29
32
  end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+ require 'fakefs/safe'
4
+ require 'robinhood/daemon'
5
+
6
+ describe Robinhood::Daemon do
7
+ let(:default_options) {
8
+ {
9
+ dir_mode: :normal,
10
+ dir: File.expand_path(File.join('tmp', 'pids')),
11
+ monitor: true,
12
+ multiple: true,
13
+ app_name: 'robinhood',
14
+ log_dir: File.expand_path('log'),
15
+ log_output: true,
16
+ ARGV: ['start']
17
+ }
18
+ }
19
+
20
+ describe '#start' do
21
+
22
+ before do
23
+ FakeFS.activate!
24
+
25
+ FileUtils.touch("Robinhood")
26
+ end
27
+
28
+ after do
29
+ FakeFS.deactivate!
30
+ end
31
+
32
+ context 'when no additional option is provided' do
33
+ it 'should start the daemon' do
34
+ daemon = Robinhood::Daemon.new
35
+
36
+ Daemons.should_receive(:run_proc).with('Robinhood', default_options)
37
+
38
+ daemon.start
39
+ end
40
+ end
41
+
42
+ context 'when different robinhood file is provided' do
43
+ it 'should start the daemon with the given file' do
44
+ daemon = Robinhood::Daemon.new(
45
+ config_file: File.expand_path('robinhood_2.rb')
46
+ )
47
+
48
+ Daemons.should_receive(:run_proc).with('robinhood_2.rb', default_options)
49
+
50
+ FileUtils.touch("robinhood_2.rb")
51
+
52
+ daemon.start
53
+ end
54
+ end
55
+
56
+ context 'when pid dir is provided' do
57
+ it 'start the daemon with the pid dir option' do
58
+ dir = File.expand_path(File.join('tmp', 'pids_2'))
59
+
60
+ daemon = Robinhood::Daemon.new(
61
+ pids_path: dir
62
+ )
63
+
64
+ Daemons.should_receive(:run_proc).with('Robinhood', default_options.merge(dir: dir))
65
+
66
+ daemon.start
67
+ end
68
+ end
69
+
70
+ context 'when log dir is provided' do
71
+ it 'start the daemon with the log dir option' do
72
+ dir = File.expand_path('log_2')
73
+
74
+ daemon = Robinhood::Daemon.new(
75
+ log_path: dir
76
+ )
77
+
78
+ Daemons.should_receive(:run_proc).with('Robinhood', default_options.merge(log_dir: dir))
79
+
80
+ daemon.start
81
+ end
82
+ end
83
+
84
+ describe '#stop' do
85
+ it 'should stop the daemon' do
86
+ daemon = Robinhood::Daemon.new(command: 'stop')
87
+
88
+ Daemons.should_receive(:run_proc).with('Robinhood', default_options.merge(ARGV: ['stop']))
89
+
90
+ daemon.stop
91
+ end
92
+ end
93
+ end
94
+ end
metadata CHANGED
@@ -1,129 +1,179 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: robinhood
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep Jaume
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-06 00:00:00.000000000 Z
11
+ date: 2013-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: celluloid
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: redis-mutex
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: daemons
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - ~>
59
+ - - "~>"
46
60
  - !ruby/object:Gem::Version
47
61
  version: '1.3'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - ~>
66
+ - - "~>"
53
67
  - !ruby/object:Gem::Version
54
68
  version: '1.3'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - '>='
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
75
  version: '0'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - '>='
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rspec
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - '>='
87
+ - - ">="
74
88
  - !ruby/object:Gem::Version
75
89
  version: '0'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - '>='
94
+ - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: spinach
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 0.8.7
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 0.8.7
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: pry
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
- - - '>='
115
+ - - ">="
88
116
  - !ruby/object:Gem::Version
89
117
  version: '0'
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
- - - '>='
122
+ - - ">="
95
123
  - !ruby/object:Gem::Version
96
124
  version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: pry-nav
99
127
  requirement: !ruby/object:Gem::Requirement
100
128
  requirements:
101
- - - '>='
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: fakefs
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
102
144
  - !ruby/object:Gem::Version
103
145
  version: '0'
104
146
  type: :development
105
147
  prerelease: false
106
148
  version_requirements: !ruby/object:Gem::Requirement
107
149
  requirements:
108
- - - '>='
150
+ - - ">="
109
151
  - !ruby/object:Gem::Version
110
152
  version: '0'
111
153
  description: Robin hood leverages celluloid actors and redis-mutex to distribute long-lived,
112
154
  single-instance processes across multiple servers.
113
155
  email:
114
156
  - josepjaume@gmail.com
115
- executables: []
157
+ executables:
158
+ - robinhood
116
159
  extensions: []
117
160
  extra_rdoc_files: []
118
161
  files:
119
- - .gitignore
120
- - .ruby-version
121
- - .travis.yml
162
+ - ".gitignore"
163
+ - ".ruby-version"
164
+ - ".travis.yml"
122
165
  - Gemfile
123
166
  - LICENSE.txt
124
167
  - README.md
125
168
  - Rakefile
169
+ - bin/robinhood
170
+ - features/steps/throttling.rb
171
+ - features/support/env.rb
172
+ - features/support/robinhood.rb
173
+ - features/throttling.feature
126
174
  - lib/robinhood.rb
175
+ - lib/robinhood/daemon.rb
176
+ - lib/robinhood/daemon/options.rb
127
177
  - lib/robinhood/dsl.rb
128
178
  - lib/robinhood/mutex.rb
129
179
  - lib/robinhood/process.rb
@@ -131,6 +181,7 @@ files:
131
181
  - lib/robinhood/version.rb
132
182
  - robinhood.gemspec
133
183
  - spec/acceptance/dsl_spec.rb
184
+ - spec/lib/robinhood/daemon_spec.rb
134
185
  - spec/lib/robinhood/dsl_spec.rb
135
186
  - spec/lib/robinhood/process_spec.rb
136
187
  - spec/lib/robinhood/runtime_spec.rb
@@ -145,23 +196,28 @@ require_paths:
145
196
  - lib
146
197
  required_ruby_version: !ruby/object:Gem::Requirement
147
198
  requirements:
148
- - - '>='
199
+ - - ">="
149
200
  - !ruby/object:Gem::Version
150
201
  version: '0'
151
202
  required_rubygems_version: !ruby/object:Gem::Requirement
152
203
  requirements:
153
- - - '>='
204
+ - - ">="
154
205
  - !ruby/object:Gem::Version
155
206
  version: '0'
156
207
  requirements: []
157
208
  rubyforge_project:
158
- rubygems_version: 2.0.3
209
+ rubygems_version: 2.1.5
159
210
  signing_key:
160
211
  specification_version: 4
161
212
  summary: Robin hood leverages celluloid actors and redis-mutex to distribute long-lived,
162
213
  single-instance processes across multiple servers.
163
214
  test_files:
215
+ - features/steps/throttling.rb
216
+ - features/support/env.rb
217
+ - features/support/robinhood.rb
218
+ - features/throttling.feature
164
219
  - spec/acceptance/dsl_spec.rb
220
+ - spec/lib/robinhood/daemon_spec.rb
165
221
  - spec/lib/robinhood/dsl_spec.rb
166
222
  - spec/lib/robinhood/process_spec.rb
167
223
  - spec/lib/robinhood/runtime_spec.rb