wolfpack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 931aae52e2cabdbbc6f1e861eabb13477ce114a1
4
+ data.tar.gz: 2aa1c44933e7fc4735c32707a55f4d5db1ad956e
5
+ SHA512:
6
+ metadata.gz: 1fb21574f5ea8a345c1803cafb2535c1b4baf2b64c6c1f83a210fa21ce81ace162887a222ad14b459cd775fddc51ee0012008f445c61ebd712947d85739e67cf
7
+ data.tar.gz: 9912bf0f401922b0d00d7f9bf40feb98bc7f8efa02b95e23ea1e4daf42bb54aa40b5eee5a827dcd32931248bf962d20c934e5ee1b94e5e8ca4874b919cbfac36
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wolfpack.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Brad Gessler
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.
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@'~~~ ~~~`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
3
+ @@@@@@@@@@@@@@@@@@@@@@@@' `@@@@@@@@@@@@@@@@@@@@@@@@@@@@
4
+ @@@@@@@@@@@@@@@@@@@@@' `@@@@@@@@@@@@@@@@@@@@@@@@@
5
+ @@@@@@@@@@@@@@@@@@@' `@@@@@@@@@@@@@@@@@@@@@@@
6
+ @@@@@@@@@@@@@@@@@' Wolfpack `@@@@@@@@@@@@@@@@@@@@@
7
+ @@@@@@@@@@@@@@@@' `@@@@@@@@@@@@@@@@@@@@
8
+ @@@@@@@@@@@@@@@' A really stupid way to run `@@@@@@@@@@@@@@@@@@@
9
+ @@@@@@@@@@@@@@@ a bunch of tasks in parallel. @@@@@@@@@@@@@@@@@@@
10
+ @@@@@@@@@@@@@@' `@@@@@@@@@@@@@@@@@@
11
+ @@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@
12
+ @@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@
13
+ @@@@@@@@@@@@@@ n, Awhoooooooo! @@@@@@@@@@@@@@@@@@
14
+ @@@@@@@@@@@@@@ _/ | _ @@@@@@@@@@@@@@@@@@
15
+ @@@@@@@@@@@@@@ /' `'/ @@@@@@@@@@@@@@@@@@
16
+ @@@@@@@@@@@@@@a <~ .' a@@@@@@@@@@@@@@@@@@
17
+ @@@@@@@@@@@@@@@ .' | @@@@@@@@@@@@@@@@@@@
18
+ @@@@@@@@@@@@@@@a _/ | a@@@@@@@@@@@@@@@@@@@
19
+ @@@@@@@@@@@@@@@@a _/ `.`. a@@@@@@@@@@@@@@@@@@@@
20
+ @@@@@@@@@@@@@@@@@a ____/ ' \__ | |______ a@@@@@@@@@@@@@@@@@@@@@
21
+ @@@@@@@@@@@@@@@@@@@a__/___/ /__\ \ \ \___.a@@@@@@@@@@@@@@@@@@@@@@@
22
+ @@@@@@@@@@@@@@@@@@@/ (___.'\_______)\_|_| \@@@@@@@@@@@@@@@@@@@@@@@@
23
+ @@@@@@@@@@@@@@@@@@|\________ ~~~~~\@@@@@@@@@@@@@@@@@@
24
+ ~~~\@@@@@@@@@@@@@@|| |\___________________________/|@/~~~~~~~~~~~\@@@
25
+ |~~~~\@@@@@@@/ | | | | by: S.C.E.S.W. | ||\____________|@@
26
+
27
+ ## Why Wolfpack?
28
+
29
+ One day you realize your CI server takes over an hour to run 2000 specs. Since you're an awesome developer and have fully embraced [The Twelve Factor App](http://12factor.net/) all you want to do is break this massive rspec runner into a bunch of sub-processes that can get the job done in parallel. Wouldn't be awesome if there was a little tool that would let you setup multiple environments of your app on the same machine and run tests against them? You're in luck, because that's what Wolfpack is all about!
30
+
31
+ ## Usage
32
+
33
+ Wolfpack can be used for more than just a Rails app, but making specs run faster is such a great example to demonstrate how the whole thing works, so create a config file in your rails app.
34
+
35
+ ```ruby
36
+ # config/wolfpack.rb
37
+ after_fork do |n, args|
38
+ # This will create n different database variables. Rails `rake db:create db:schema:load`
39
+ # should pick this up for test setup.
40
+ ENV['DATABASE_URL'] = "mysql2://localhost/app_test_#{n}"
41
+
42
+ # Using redis? Cool! But lets partition that too. The bummer here is that
43
+ # redis limits us to 16 databases, so we should throw an exception just
44
+ # to make that clear.
45
+ raise 'Redis only supports 0-15 databases' unless n < 16
46
+ ENV['REDIS_URL'] = "redis://localhost/#{n}"
47
+
48
+ # We'll feed in a list of specs that we want to run from stdin,
49
+ # concat with a space, and output into an ENV var that rspec will pick up.
50
+ ENV['SPEC_FILES'] = args.join(' ')
51
+
52
+ # And whatever else you need to deal with...
53
+ end
54
+ ```
55
+
56
+ Then run wolfpack with the config file.
57
+
58
+ ```sh
59
+ # Running the wolfpack command on a 4 processor machine. Your machine might
60
+ # run with a different number of workers depending on processors.
61
+ $ find spec/**/**_spec.rb | wolfpack run 'echo "Using $DATABASE_URL"; rake db:create db:schema:load; rspec $SPEC_FILES;' -c config/wolfpack.rb
62
+ Using mysql2://localhost/app_test_0
63
+ Using mysql2://localhost/app_test_1
64
+ Using mysql2://localhost/app_test_2
65
+ Using mysql2://localhost/app_test_3
66
+ # All of the spec output will be displayed here..
67
+ ```
68
+
69
+ Pretty cool huh? As you can imagine, Wolfpack can be used to parallelize the execution of any unix program.
70
+
71
+ ## Installation
72
+
73
+ Add this line to your application's Gemfile:
74
+
75
+ gem 'wolfpack'
76
+
77
+ And then execute:
78
+
79
+ $ bundle
80
+
81
+ Or install it yourself as:
82
+
83
+ $ gem install wolfpack
84
+
85
+ ## Contributing
86
+
87
+ 1. Fork it
88
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
89
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
90
+ 4. Push to the branch (`git push origin my-new-feature`)
91
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/wolfpack ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'wolfpack'
3
+ Wolfpack::CLI.start
@@ -0,0 +1,8 @@
1
+ # This example sets environmental variables that mutates the child process.
2
+ after_fork do |n, args|
3
+ # Set the WOLF var to the instance of the wolf.
4
+ ENV['WOLF'] = "Wolf #{n}"
5
+
6
+ # Space the arguments passed into wolfpack with spaces.
7
+ ENV['ARGS'] = args.join(' ')
8
+ end
@@ -0,0 +1,29 @@
1
+ # So, you're a http://12factor.net/ hotshot? You're in luck because
2
+ # it will be pretty easy to get wolfpack working with your specs.
3
+
4
+ after_fork do |n, args|
5
+ # This will create n different database variables. Rails `rake db:create db:schema:load`
6
+ # should pick this up for test setup.
7
+ ENV['DATABASE_URL'] = "mysql2://localhost/app_test_#{n}"
8
+
9
+ # Using redis? Cool! But lets partition that too. The bummer here is that
10
+ # redis limits us to 16 databases, so we should throw an exception just
11
+ # to make that clear.
12
+ raise 'Redis only supports 0-15 databases' unless n < 16
13
+ ENV['REDIS_URL'] = "redis://localhost/#{n}"
14
+
15
+ # We'll feed in a list of specs that we want to run from stdin,
16
+ # concat with a space, and output into an ENV var that rspec will pick up.
17
+ ENV['SPEC_FILES'] = args.join(' ')
18
+
19
+ # And whatever else you need to deal with...
20
+ end
21
+
22
+ __END__
23
+
24
+ Now you're ready to run this! Try something like this on your CLI:
25
+
26
+ $ bundle exec wolfpack exec "rake db:setup rake db:schema:load; rspec $SPEC_FILES;"
27
+
28
+ and you're off to the races! In practice its probably not that easy though because
29
+ specs are generally not developed initially with concurrency in mind.
@@ -0,0 +1,3 @@
1
+ module Wolfpack
2
+ VERSION = "0.0.1"
3
+ end
data/lib/wolfpack.rb ADDED
@@ -0,0 +1,118 @@
1
+ require "wolfpack/version"
2
+ require "thor"
3
+ require "parallel"
4
+
5
+ module Wolfpack
6
+ # Gets the number of "processors" on the current machine. This does not map
7
+ # directly to physical cores. For example, a hyperthreaded Intel chip may
8
+ # have 2 physical cores but show up as 4 cores.
9
+ def self.processor_count
10
+ @processor_count ||= Parallel.processor_count
11
+ end
12
+
13
+ # Configure a runner instance
14
+ class Configurator
15
+ attr_reader :runner
16
+
17
+ # Configures the instance of a given runner with a file.
18
+ def initialize(runner, config_path)
19
+ @runner = runner
20
+ instance_eval(File.read(config_path), config_path)
21
+ end
22
+
23
+ # Replace the instances after_fork hook with after_fork
24
+ # from the configuration file.
25
+ #
26
+ # @example
27
+ # after_fork do |n|
28
+ # ENV['DATABASE_URL'] = "#{ENV['DATABASE_URL']}_#{n}"
29
+ # end
30
+ def after_fork(&block)
31
+ runner.after_fork = block
32
+ end
33
+ end
34
+
35
+ # Encapsulates a command that is te be split up and run.
36
+ class Runner
37
+ attr_accessor :after_fork, :command, :args
38
+
39
+ # Create a command that will run with the give arguments. Optionally
40
+ # a path may be given to a configuration file that sets up a runner.
41
+ def initialize(command, args = [], config_path = nil)
42
+ @command, @args = command, args
43
+ configure(config_path) if config_path
44
+ end
45
+
46
+ # Run the command `n` number of times. Default is the number of processes
47
+ # on the local machine.
48
+ def run(processes = nil)
49
+ # Sometimes a nil will make it here because of the CLI. This will
50
+ # make sure we have a number to work with.
51
+ processes ||= Wolfpack.processor_count
52
+
53
+ # Split args into groups of n processes.
54
+ partions = partition(args, processes)
55
+
56
+ # Now run the command with the processes.
57
+ Parallel.each_with_index(partions, :in_processes => processes) do |args, n|
58
+ after_fork.call(n, args) if after_fork
59
+ system @command
60
+ end
61
+ end
62
+
63
+ # Configures the runner's by reading a configuration file and eval-ing
64
+ # the ruby.
65
+ def configure(config_path)
66
+ Configurator.new(self, config_path)
67
+ end
68
+
69
+ private
70
+ # Take an array and break it into `n` partitions
71
+ def partition(arr, n)
72
+ result = Array.new(n){ [] }
73
+ arr.each_with_index do |el, idx|
74
+ result[idx % n].push el
75
+ end
76
+ result
77
+ end
78
+ end
79
+
80
+ class CLI < Thor
81
+ desc "exec COMMAND", "Runs many tasks in parallel"
82
+ method_options %w( config -c ) => :string
83
+ method_options %w( processes -n ) => :integer
84
+ method_options %w( args -a ) => :array
85
+ def exec(command)
86
+ # Parse out an integer for the # of processors the user specifies since
87
+ # thor doesn't return an integer for its params.
88
+ processes = options[:processes].to_i if options[:processes]
89
+
90
+ # Process stdin that's piped in.
91
+ args = if $stdin.tty?
92
+ options[:args] || []
93
+ else
94
+ # Read from the pipe
95
+ buffer = ""
96
+ until $stdin.eof? do
97
+ buffer << $stdin.read
98
+ end
99
+ buffer.lines
100
+ end
101
+
102
+ # If args are passed in, read those and split, otherwise read stdin
103
+ # from a pipe and split by lines.
104
+
105
+ Wolfpack::Runner.new(command, args, options[:config]).run(processes)
106
+ end
107
+
108
+ desc "version", "Wolfpack version"
109
+ def version
110
+ puts Wolfpack::VERSION
111
+ end
112
+
113
+ desc "processors", "Count of processors on machine"
114
+ def processors
115
+ puts Wolfpack.processor_count
116
+ end
117
+ end
118
+ end
@@ -0,0 +1 @@
1
+ require 'spec_helper'
@@ -0,0 +1,19 @@
1
+ require 'wolfpack'
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # Require this file using `require "spec_helper"` to ensure that it is only
6
+ # loaded once.
7
+ #
8
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
9
+ RSpec.configure do |config|
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+
14
+ # Run specs in random order to surface order dependencies. If you find an
15
+ # order dependency and want to debug it, you can fix the order by providing
16
+ # the seed, which is printed after each run.
17
+ # --seed 1234
18
+ config.order = 'random'
19
+ end
data/wolfpack.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'wolfpack/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "wolfpack"
8
+ spec.version = Wolfpack::VERSION
9
+ spec.authors = ["Brad Gessler"]
10
+ spec.email = ["brad@polleverywhere.com"]
11
+ spec.description = %q{Run stuff in parallel}
12
+ spec.summary = %q{Run ruby tasks in parallel}
13
+ spec.homepage = "https://github.com/polleverywhere/wolfpack"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "guard-rspec"
24
+
25
+ spec.add_dependency "thor"
26
+ spec.add_dependency "parallel"
27
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wolfpack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Brad Gessler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: guard-rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: parallel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Run stuff in parallel
84
+ email:
85
+ - brad@polleverywhere.com
86
+ executables:
87
+ - wolfpack
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - .rspec
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/wolfpack
98
+ - examples/env_var.rb
99
+ - examples/rails_specs.rb
100
+ - lib/wolfpack.rb
101
+ - lib/wolfpack/version.rb
102
+ - spec/lib/wolfpack.rb
103
+ - spec/spec_helper.rb
104
+ - wolfpack.gemspec
105
+ homepage: https://github.com/polleverywhere/wolfpack
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.0.3
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Run ruby tasks in parallel
129
+ test_files:
130
+ - spec/lib/wolfpack.rb
131
+ - spec/spec_helper.rb