robinhood 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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