stormtroopers 0.0.1 → 0.0.2

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.
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