stormtroopers 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
+ .vagrant
5
6
  .yardoc
6
7
  Gemfile.lock
7
8
  InstalledFiles
@@ -9,6 +10,7 @@ _yardoc
9
10
  coverage
10
11
  doc/
11
12
  lib/bundler/man
13
+ log/*
12
14
  pkg
13
15
  rdoc
14
16
  spec/reports
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ notifications:
2
+ recipients:
3
+ - mark@socialreferral.com
4
+ - andre@socialreferral.com
5
+ rvm:
6
+ - jruby-19mode
7
+ script: "bundle exec rspec"
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Stormtroopers
1
+ # Stormtroopers [![Build Status](https://secure.travis-ci.org/socialreferral/stormtroopers.png)](http://travis-ci.org/socialreferral/stormtroopers)
2
2
 
3
- Stormtroopers is a jruby execution environment for delayed jobs
3
+ Stormtroopers is a jruby execution environment for delayed jobs, Work In Progress!
4
4
 
5
5
  ## Installation
6
6
 
data/Vagrantfile ADDED
@@ -0,0 +1,10 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ Vagrant::Config.run do |config|
5
+ config.vm.host_name = "stormtroopers"
6
+ config.vm.box = "precise64"
7
+ config.vm.box_url = "http://files.vagrantup.com/precise64.box"
8
+ config.vm.share_folder "v-root", "/vagrant", "."
9
+ config.vm.provision :shell, :path => "vagrant-provision"
10
+ end
data/bin/stormtroopers ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require "config/environment"
5
+ rescue LoadError
6
+ puts "Could not load environment"
7
+ end
8
+
9
+ require_relative "../lib/stormtroopers"
10
+
11
+ Stormtroopers::Manager.instance.manage
@@ -1,11 +1,12 @@
1
1
  armies:
2
2
  - factory:
3
3
  type: :dummy
4
- name: "Dummy army 1"
4
+ name: "Dummy Trooper 1"
5
5
  sleep_duration: 10
6
6
  max_threads: 2
7
+ name: "Dad's Army"
7
8
  - factory:
8
9
  type: :dummy
9
- name: "Dummy army 2"
10
+ name: "Dummy Trooper 2"
10
11
  sleep_duration: 5
11
12
  max_threads: 2
data/lib/stormtroopers.rb CHANGED
@@ -1,12 +1,8 @@
1
- require "stormtroopers/version"
2
- require "stormtroopers/trooper"
3
- require "stormtroopers/manager"
4
- require "stormtroopers/factory"
5
- require "stormtroopers/army"
1
+ require_relative "./stormtroopers/version"
2
+ require_relative "./stormtroopers/trooper"
3
+ require_relative "./stormtroopers/manager"
4
+ require_relative "./stormtroopers/factory"
5
+ require_relative "./stormtroopers/army"
6
6
 
7
- Dir['./lib/stormtroopers/factory/*.rb'].each{ |f| require f }
8
- Dir['./lib/stormtroopers/trooper/*.rb'].each{ |f| require f }
9
-
10
- module Stormtroopers
11
-
12
- end
7
+ Dir["#{File.dirname(__FILE__)}/stormtroopers/factory/*.rb"].each{ |f| require f }
8
+ Dir["#{File.dirname(__FILE__)}/stormtroopers/trooper/*.rb"].each{ |f| require f }
@@ -0,0 +1,4 @@
1
+ module Stormtroopers
2
+ class AlreadyRunning < StandardError
3
+ end
4
+ end
@@ -1,30 +1,54 @@
1
1
  module Stormtroopers
2
2
  class Army
3
- attr_reader :factory, :threads, :max_threads
3
+ attr_reader :factory, :threads, :max_threads, :name
4
4
 
5
5
  def initialize(config)
6
- @factory = "stormtroopers/#{config[:factory].delete(:type)}_factory".camelize.constantize.new(config[:factory])
6
+ @name = config[:name] || factory_class(config).name
7
+ @factory = factory_class(config).new(config[:factory])
7
8
  @max_threads = config[:max_threads] || 1
8
9
  @threads = []
9
10
  end
10
11
 
12
+ def factory_class(config)
13
+ @factory_class ||= begin
14
+ raise ArgumentError, "Factory class or type must be defined" if config[:factory][:class].blank? && config[:factory][:type].blank?
15
+ class_name ||= config[:factory].delete(:class)
16
+ class_name ||= "stormtroopers/#{config[:factory].delete(:type)}_factory".camelize
17
+ class_name.constantize
18
+ end
19
+ end
20
+
11
21
  def manage
12
22
  cleanup
13
23
  if threads.count < max_threads
14
24
  if trooper = factory.produce
15
- threads << Thread.new { trooper.run }
25
+ threads << Thread.new do
26
+ begin
27
+ trooper.run
28
+ ensure
29
+ if defined?(::Mongoid)
30
+ ::Mongoid::IdentityMap.clear
31
+ ::Mongoid.session(:default).disconnect
32
+ end
33
+ end
34
+ end
16
35
  end
17
36
  end
18
37
  end
19
38
 
20
39
  def finish
40
+ logger.debug("#{name}: Finishing")
21
41
  threads.each(&:join)
22
42
  end
23
43
 
44
+ def logger
45
+ Stormtroopers::Manager.logger
46
+ end
47
+
24
48
  private
25
49
 
26
50
  def cleanup
27
51
  threads.reject!{ |thread| !thread.alive? }
28
52
  end
29
53
  end
30
- end
54
+ end
@@ -9,5 +9,9 @@ module Stormtroopers
9
9
  def produce
10
10
  raise NotImplementedError.new("produce method not implemented on the factory")
11
11
  end
12
+
13
+ def logger
14
+ Manager.logger
15
+ end
12
16
  end
13
- end
17
+ end
@@ -1,10 +1,12 @@
1
1
  module Stormtroopers
2
2
  class DelayedJobFactory < Factory
3
3
  def produce
4
- worker = Struct.new(name: "Stormtroopers")
4
+ worker = Delayed::Worker.new(options)
5
+ worker.name = "rand #{Time.now.utc.to_f} #{rand(1000)}"
5
6
  if job = Delayed::Job.reserve(worker)
7
+ logger.info("#{options[:name]} producing trooper to run #{job.queue} job #{job.id}")
6
8
  DelayedJobTrooper.new(job)
7
9
  end
8
10
  end
9
11
  end
10
- end
12
+ end
@@ -5,4 +5,4 @@ module Stormtroopers
5
5
  DummyTrooper.new(options)
6
6
  end
7
7
  end
8
- end
8
+ end
@@ -1,24 +1,37 @@
1
+ require 'logger'
2
+ require 'yaml'
1
3
  require 'singleton'
2
4
  require 'active_support/core_ext/hash'
3
5
  require 'active_support/hash_with_indifferent_access'
6
+ require_relative "./already_running"
4
7
 
5
8
  module Stormtroopers
6
9
  class Manager
7
10
  include Singleton
8
- # This class is dependant on rails and active support
9
11
 
10
12
  def manage
13
+ raise AlreadyRunning if managing?
14
+ @managing = true
15
+
16
+ Signal.trap("INT") do
17
+ logger.info "Stopping, waiting for running jobs to complete"
18
+ @managing = false
19
+ end
20
+
11
21
  logger.info "Starting"
12
- loop do
22
+ while managing? do
13
23
  armies.each(&:manage)
14
24
  sleep 0.1
15
25
  end
16
- rescue Interrupt => e
17
- logger.info "Stopping, waiting for running jobs to complete"
26
+
18
27
  armies.each(&:finish)
19
28
  logger.info "Stopped, all running jobs completed"
20
29
  end
21
30
 
31
+ def managing?
32
+ @managing || false
33
+ end
34
+
22
35
  def armies
23
36
  @armies ||= config[:armies].map do |army_config|
24
37
  Army.new(army_config)
@@ -29,20 +42,22 @@ module Stormtroopers
29
42
  @config ||= HashWithIndifferentAccess.new(YAML.load_file(config_file))
30
43
  end
31
44
 
32
- def config_file
45
+ def working_directory
33
46
  if defined?(Rails)
34
- "#{Rails.root}/config/stormtroopers.yml"
47
+ Rails.root
35
48
  else
36
- "#{File.expand_path('../../../config', __FILE__)}/stormtroopers.yml"
49
+ Dir.getwd
37
50
  end
38
51
  end
39
52
 
53
+ def config_file
54
+ File.join(working_directory, "config", "stormtroopers.yml")
55
+ end
56
+
40
57
  def logger
41
- if defined?(Rails)
42
- Rails.logger
43
- else
44
- @logger ||= Logger.new(STDOUT)
45
- end
58
+ log_directory = File.join(working_directory, "log")
59
+ Dir.mkdir(log_directory) unless File.directory?(log_directory)
60
+ @logger ||= Logger.new(File.join(working_directory, "log", "stormtroopers.log"))
46
61
  end
47
62
 
48
63
  private
@@ -1,3 +1,3 @@
1
1
  module Stormtroopers
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,104 @@
1
+ require "stormtroopers/manager"
2
+
3
+ describe Stormtroopers::Manager do
4
+ let(:manager) { Stormtroopers::Manager.instance }
5
+
6
+ before(:each) {
7
+ stub_const("Stormtroopers::Army", Class.new)
8
+ }
9
+
10
+ describe "#working_directory" do
11
+ context "with Rails" do
12
+ it "uses the Rails root" do
13
+ stub_const("Rails", Class.new)
14
+ Rails.should_receive(:root).and_return("/path/to/app")
15
+ manager.working_directory.should eq("/path/to/app")
16
+ end
17
+ end
18
+
19
+ context "without Rails" do
20
+ it "uses the current working directory" do
21
+ manager.working_directory.should eq(Dir.pwd)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "#logger" do
27
+ it "creates a logger to log/stormtroopers.log" do
28
+ logger = stub.as_null_object
29
+ Logger.should_receive(:new).with(File.join(manager.working_directory, "log", "stormtroopers.log")).and_return(logger)
30
+ manager.logger.should equal(logger)
31
+ end
32
+ end
33
+
34
+ describe "#config_file" do
35
+ it "uses the config_file relative to the current working directory" do
36
+ manager.config_file.should eq(File.join(manager.working_directory, "config", "stormtroopers.yml"))
37
+ end
38
+ end
39
+
40
+ describe "#config" do
41
+ it "loads the config_file into a HashWithIndifferentAccess" do
42
+ YAML.should_receive(:load_file).and_return({dummy: "value"})
43
+ manager.config.should eq(HashWithIndifferentAccess.new({dummy: "value"}))
44
+ end
45
+ end
46
+
47
+ describe "#armies" do
48
+ it "returns an array of armies matching using #config" do
49
+ army1_config = {factory: {type: :dummy, name: "Trooper 1", sleep_duration: 2}, name: "Dad's Army", max_threads: 1}
50
+ army1 = stub
51
+ Stormtroopers::Army.should_receive(:new).with(HashWithIndifferentAccess.new(army1_config)).and_return(army1)
52
+ army2_config = {factory: {type: :dummy, name: "Trooper 2", sleep_duration: 5}, name: "Mom's Army", max_threads: 2}
53
+ army2 = stub
54
+ Stormtroopers::Army.should_receive(:new).with(HashWithIndifferentAccess.new(army2_config)).and_return(army2)
55
+ config = HashWithIndifferentAccess.new(
56
+ {
57
+ armies: [
58
+ army1_config,
59
+ army2_config,
60
+ ]
61
+ }
62
+ )
63
+ manager.should_receive(:config).and_return(config)
64
+ armies = manager.armies
65
+ manager.armies.size.should eq(2)
66
+ manager.armies.should include(army1)
67
+ manager.armies.should include(army2)
68
+ end
69
+ end
70
+
71
+ describe "#manage" do
72
+ let(:army1) { stub(manage: nil, finish: nil) }
73
+ let(:army2) { stub(manage: nil, finish: nil) }
74
+
75
+ before(:each) do
76
+ manager.stub(:armies).and_return([army1, army2])
77
+ end
78
+
79
+ it "calls manage on each army while managing" do
80
+ manager.stub(:managing?).and_return(false, true, true, false)
81
+ army1.should_receive(:manage).exactly(2).times
82
+ army2.should_receive(:manage).exactly(2).times
83
+ manager.manage
84
+ end
85
+
86
+ it "calls finish on each army when no longer managing" do
87
+ manager.stub(:managing?).and_return(false)
88
+ army1.should_receive(:finish)
89
+ army2.should_receive(:finish)
90
+ manager.manage
91
+ end
92
+
93
+ it "raises an error when already running" do
94
+ manager.stub(:managing?).and_return(true)
95
+ expect { manager.manage }.to raise_error(Stormtroopers::AlreadyRunning)
96
+ end
97
+ end
98
+
99
+ describe ".logger" do
100
+ it "returns the singleton instance's logger" do
101
+ Stormtroopers::Manager.logger.should eq(manager.logger)
102
+ end
103
+ end
104
+ end
@@ -7,13 +7,16 @@ Gem::Specification.new do |gem|
7
7
  gem.name = "stormtroopers"
8
8
  gem.version = Stormtroopers::VERSION
9
9
  gem.authors = ["Andre Meij", "Mark Kremer"]
10
- gem.email = ["andre@socialreferral.com"]
10
+ gem.email = ["andre@socialreferral.com", "mark@socialreferral.com"]
11
11
  gem.description = %q{Stormtroopers is a jruby execution environment for delayed jobs }
12
12
  gem.summary = %q{Execute delayed jobs in a threaded jruby environment}
13
13
  gem.homepage = "http://github.com/socialreferral/stormtroopers"
14
14
 
15
15
  gem.add_dependency('activesupport', '>= 3.2.0')
16
16
 
17
+ gem.add_development_dependency('rspec')
18
+
19
+ gem.bindir = 'bin'
17
20
  gem.files = `git ls-files`.split($/)
18
21
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
22
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
data/vagrant-provision ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ export DEBIAN_FRONTEND=noninteractive
3
+
4
+ apt-get update > /dev/null
5
+ apt-get -y install build-essential git-core openjdk-7-jre-headless
6
+
7
+ # Create directory for local installs
8
+ mkdir -p /home/vagrant/local
9
+ chown -R vagrant:vagrant /home/vagrant/local
10
+
11
+ if [ -f "/home/vagrant/local/jruby-1.7.0/bin/jruby" ]; then
12
+ echo "Already installed jruby 1.7"
13
+ else
14
+ echo "Downloading jruby 1.7"
15
+ wget -q http://jruby.org.s3.amazonaws.com/downloads/1.7.0/jruby-bin-1.7.0.tar.gz
16
+ echo "Unpacking jruby 1.7"
17
+ tar -xzf jruby-bin-1.7.0.tar.gz
18
+ echo "Installing jruby 1.7"
19
+ chown -R vagrant:vagrant jruby-1.7.0 && mv jruby-1.7.0 /home/vagrant/local
20
+ echo "export PATH=\"/home/vagrant/local/jruby-1.7.0/bin:\$PATH\"" >> /home/vagrant/.bashrc
21
+ echo "export PATH=\"/home/vagrant/local/jruby-1.7.0/bin:\$PATH\"" >> /home/vagrant/.zshenv
22
+ ln -s /home/vagrant/local/jruby-1.7.0/bin/jruby /home/vagrant/local/jruby-1.7.0/bin/ruby
23
+ fi
24
+
25
+ echo "Activating jruby 1.7"
26
+ export PATH=/home/vagrant/local/jruby-1.7.0/bin:$PATH
27
+
28
+ gem install bundler pry
29
+
30
+ su -c 'cd /vagrant && bundle check || bundle' vagrant
metadata CHANGED
@@ -1,49 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stormtroopers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.0.2
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Andre Meij
9
9
  - Mark Kremer
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-11-05 00:00:00.000000000 Z
13
+ date: 2012-11-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
- requirement: !ruby/object:Gem::Requirement
17
+ version_requirements: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.0
18
22
  none: false
23
+ requirement: !ruby/object:Gem::Requirement
19
24
  requirements:
20
25
  - - ! '>='
21
26
  - !ruby/object:Gem::Version
22
27
  version: 3.2.0
23
- type: :runtime
28
+ none: false
24
29
  prerelease: false
30
+ type: :runtime
31
+ - !ruby/object:Gem::Dependency
32
+ name: rspec
25
33
  version_requirements: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: !binary |-
38
+ MA==
26
39
  none: false
40
+ requirement: !ruby/object:Gem::Requirement
27
41
  requirements:
28
42
  - - ! '>='
29
43
  - !ruby/object:Gem::Version
30
- version: 3.2.0
44
+ version: !binary |-
45
+ MA==
46
+ none: false
47
+ prerelease: false
48
+ type: :development
31
49
  description: ! 'Stormtroopers is a jruby execution environment for delayed jobs '
32
50
  email:
33
51
  - andre@socialreferral.com
52
+ - mark@socialreferral.com
34
53
  executables:
35
- - stormtroopers.rb
54
+ - stormtroopers
36
55
  extensions: []
37
56
  extra_rdoc_files: []
38
57
  files:
39
58
  - .gitignore
59
+ - .rspec
60
+ - .travis.yml
40
61
  - Gemfile
41
62
  - LICENSE.txt
42
63
  - README.md
43
64
  - Rakefile
44
- - bin/stormtroopers.rb
65
+ - Vagrantfile
66
+ - bin/stormtroopers
45
67
  - config/stormtroopers.yml
46
68
  - lib/stormtroopers.rb
69
+ - lib/stormtroopers/already_running.rb
47
70
  - lib/stormtroopers/army.rb
48
71
  - lib/stormtroopers/factory.rb
49
72
  - lib/stormtroopers/factory/delayed_job.rb
@@ -53,30 +76,34 @@ files:
53
76
  - lib/stormtroopers/trooper/delayed_job.rb
54
77
  - lib/stormtroopers/trooper/dummy.rb
55
78
  - lib/stormtroopers/version.rb
79
+ - spec/stormtroopers/manager_spec.rb
56
80
  - stormtroopers.gemspec
81
+ - vagrant-provision
57
82
  homepage: http://github.com/socialreferral/stormtroopers
58
83
  licenses: []
59
- post_install_message:
84
+ post_install_message:
60
85
  rdoc_options: []
61
86
  require_paths:
62
87
  - lib
63
88
  required_ruby_version: !ruby/object:Gem::Requirement
64
- none: false
65
89
  requirements:
66
90
  - - ! '>='
67
91
  - !ruby/object:Gem::Version
68
- version: '0'
69
- required_rubygems_version: !ruby/object:Gem::Requirement
92
+ version: !binary |-
93
+ MA==
70
94
  none: false
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
96
  requirements:
72
97
  - - ! '>='
73
98
  - !ruby/object:Gem::Version
74
- version: '0'
99
+ version: !binary |-
100
+ MA==
101
+ none: false
75
102
  requirements: []
76
- rubyforge_project:
77
- rubygems_version: 1.8.23
78
- signing_key:
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.24
105
+ signing_key:
79
106
  specification_version: 3
80
107
  summary: Execute delayed jobs in a threaded jruby environment
81
- test_files: []
82
- has_rdoc:
108
+ test_files:
109
+ - spec/stormtroopers/manager_spec.rb
data/bin/stormtroopers.rb DELETED
File without changes