spinoff 0.0.1

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 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/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in spinoff.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # Spinoff
2
+
3
+ Spinoff will help you to speed up your Ruby test workflow.
4
+
5
+ It does that by preloading your environment and then using functions like
6
+ `fork` for each run of your test suite to avoid loading framework code
7
+ like Rails over and over again. (it supports JRuby as well)
8
+
9
+ # Credits
10
+
11
+ Lots of code has been taken from the [Spin](https://github.com/jstorimer/spin)
12
+ project by Jesse Storimer. Thanks for sharing, Jesse!
13
+
14
+ # How does it work
15
+
16
+ Spinoff operates with the assumption that most of the dependencies of your
17
+ application do not change. If you are working on a Rails project, you
18
+ probably do not have to reload all Rails classes for each test run.
19
+
20
+ The `spinoff-server` command starts a server process that will listen for
21
+ a list of test files on a unix domain socket. It will also load an init file
22
+ which contains all code that should be preloaded.
23
+ (like `require 'config/application'` for Rails)
24
+
25
+ The `spinoff-client` command will be called with a list of test files that
26
+ should be executed. It sends that file list to the server process via the
27
+ unix domain socket.
28
+
29
+ Once the server process receives a list of files, it will fork the Ruby process
30
+ and execute the files with the selected test runner. All code that changes
31
+ frequently will only be loaded in the forked process.
32
+
33
+ This allows Spinoff to speed up our test suite without any knowledge of the
34
+ frameworks and libraries we use to build our app.
35
+
36
+ # How is it different from Spin?
37
+
38
+ Spinoff is based on [Spin](https://github.com/jstorimer/spin) but differs on
39
+ the following points.
40
+
41
+ 1. It is framework agnostic and thus needs an init file to preload code.
42
+ Spin does only support Rails at this time of writing.
43
+
44
+ 2. It works with [JRuby](http://www.jruby.org/)!
45
+
46
+ # Usage
47
+
48
+ Spinoff needs an init file to preload code. Please make sure you only load
49
+ libraries that do not change between your test runs. If you change anything
50
+ you preload in the init file, you have to restart the Spinoff server.
51
+
52
+ Example:
53
+
54
+ ```ruby
55
+ require 'config/application'
56
+ require 'rspec'
57
+ ```
58
+
59
+ Starting the server:
60
+
61
+ $ spinoff-server --rspec config/spinoff.rb
62
+
63
+ Please use `spinoff-server --help` to show all available options.
64
+
65
+ Sending a list of test files to the server:
66
+
67
+ $ spinoff-client spec/test1_spec.rb spec/foo/bar_spec.rb
68
+
69
+ Spinoff should work well with autotest(ish) tools. Just start the server and
70
+ execute the spinoff client on file changes.
71
+
72
+ # JRuby Support
73
+
74
+ Since JRuby does not support the fork system call, Spinoff is using a
75
+ new `ScriptingContainer` for each test run. That also means we cannot really
76
+ preload any code. But it saves us from starting a new JVM for each test run.
77
+
78
+ # Contributing
79
+
80
+ I'm happy about any kind of feedback and/or contribution.
81
+
82
+ # Caveats
83
+
84
+ * New and not well tested.
85
+ * No test suite yet.
86
+
87
+ # Author
88
+
89
+ [Bernd Ahlers](https://github.com/bernd)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'spinoff/client'
5
+
6
+ Spinoff::Client.start(ARGV)
7
+
8
+ exit 0
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+ require 'spinoff/server'
6
+
7
+ config = {}
8
+
9
+ parser = OptionParser.new do |o|
10
+ o.banner = 'Usage: spinoff-server [options] <init file>'
11
+
12
+ o.on('--rspec', 'Use rspec framework (default)') do |v|
13
+ config[:test_framework] = :rspec
14
+ end
15
+
16
+ o.on('--test-unit', 'Use test/unit framework') do |v|
17
+ config[:test_framework] = :testunit
18
+ end
19
+
20
+ o.on('-h', '--help') do
21
+ STDERR.puts o
22
+ exit 1
23
+ end
24
+ end
25
+ parser.parse!
26
+
27
+ unless config[:test_framework]
28
+ STDERR.puts "Please select a test framework! (see --help)"
29
+ exit 1
30
+ end
31
+
32
+ config[:init_script] = ARGV.shift unless ARGV.empty?
33
+
34
+ Spinoff::Server.start(config)
35
+
36
+ exit 0
@@ -0,0 +1,22 @@
1
+ require 'socket'
2
+ require 'spinoff/socket_path'
3
+
4
+ module Spinoff
5
+ module Client
6
+ def self.start(argv)
7
+ files = argv.select {|f| File.exist?(f) }.uniq.join(File::PATH_SEPARATOR)
8
+
9
+ return if files.empty?
10
+
11
+ socket = UNIXSocket.open(Spinoff.socket_path)
12
+ socket.puts files
13
+
14
+ while line = socket.gets
15
+ break if line == "\0"
16
+ print line
17
+ end
18
+ rescue Errno::ECONNREFUSED
19
+ abort "Connection to spinoff server was refused."
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module Spinoff
2
+ module Server
3
+ def self.start(config)
4
+ case RUBY_PLATFORM
5
+ when /java/i
6
+ require 'spinoff/server/jruby'
7
+ Spinoff::Server::JRuby.start(config)
8
+ else
9
+ require 'spinoff/server/fork'
10
+ Spinoff::Server::Fork.start(config)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ require 'spinoff/server/generic'
2
+ require 'spinoff/test_runner'
3
+ require 'benchmark'
4
+
5
+ module Spinoff
6
+ module Server
7
+ class Fork < Spinoff::Server::Generic
8
+ def start
9
+ test_runner = Spinoff::TestRunner.init(config[:test_framework])
10
+ load_init_script(init_script)
11
+
12
+ accept_loop do |files|
13
+ start = Time.now
14
+
15
+ fork do
16
+ STDERR.puts "Loading #{files.inspect}"
17
+ test_runner << files
18
+ end
19
+
20
+ Process.wait
21
+
22
+ STDERR.puts "Execution time: %.4fs" % (Time.now - start)
23
+ end
24
+ end
25
+
26
+ private
27
+ def load_init_script(file)
28
+ if File.exist?(file)
29
+ sec = Benchmark.realtime do
30
+ require File.expand_path(file)
31
+ end
32
+ STDERR.puts "Loaded init script in %.4fs (%s)" % [sec, file]
33
+ else
34
+ STDERR.puts "WARNING: Init script '#{file}' does not exist!"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,49 @@
1
+ require 'socket'
2
+ require 'spinoff/socket_path'
3
+
4
+ module Spinoff
5
+ module Server
6
+ class Generic
7
+ def self.start(config)
8
+ new(config).start
9
+ end
10
+
11
+ attr_reader :config, :socket
12
+
13
+ def initialize(config)
14
+ @config = config
15
+ @socket = create_socket
16
+ end
17
+
18
+ def init_script
19
+ File.expand_path(@config.fetch(:init_script, File.join(Dir.pwd, 'spinoff.rb')))
20
+ end
21
+
22
+ def start
23
+ raise "#{self.class}#start not implemented."
24
+ end
25
+
26
+ def accept_loop
27
+ loop do
28
+ connection = socket.accept
29
+ files = connection.gets.chomp.split(File::PATH_SEPARATOR)
30
+
31
+ disconnect_client(connection)
32
+ yield(files)
33
+ end
34
+ end
35
+
36
+ def disconnect_client(client)
37
+ client.print("\0")
38
+ client.close
39
+ end
40
+
41
+ private
42
+ def create_socket
43
+ path = Spinoff.socket_path
44
+ File.delete(path) if File.exist?(path)
45
+ UNIXServer.open(path)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,44 @@
1
+ require 'java'
2
+ require 'spinoff/server/generic'
3
+ require 'benchmark'
4
+
5
+ module Spinoff
6
+ module Server
7
+ class JRuby < Spinoff::Server::Generic
8
+ import 'org.jruby.embed.PathType'
9
+ import 'org.jruby.embed.LocalContextScope'
10
+ import 'org.jruby.embed.ScriptingContainer'
11
+
12
+ def start
13
+ accept_loop do |files|
14
+ start = Time.now
15
+
16
+ STDERR.puts "Creating JRuby scripting container"
17
+ context = ScriptingContainer.new(LocalContextScope::SINGLETHREAD)
18
+
19
+ if File.exist?(init_script)
20
+ sec = Benchmark.realtime do
21
+ context.run_scriptlet(PathType::ABSOLUTE, init_script)
22
+ end
23
+ STDERR.puts "Loaded init script in %.4fs (%s)" % [sec, init_script]
24
+ else
25
+ STDERR.puts "WARNING: Init script '#{init_script}' does not exist!"
26
+ end
27
+
28
+ STDERR.puts "Loading #{files.inspect}"
29
+ context.put('framework', config[:test_framework])
30
+ context.put('files', files)
31
+
32
+ context.run_scriptlet <<-__RUBY
33
+ require 'spinoff/test_runner'
34
+ runner = Spinoff::TestRunner.init(framework)
35
+ runner << files
36
+ __RUBY
37
+ context.terminate
38
+
39
+ STDERR.puts "Execution time: %.4fs" % (Time.now - start)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,8 @@
1
+ require 'tempfile'
2
+ require 'digest/md5'
3
+
4
+ module Spinoff
5
+ def self.socket_path
6
+ File.join(Dir.tmpdir, [Digest::MD5.hexdigest(Dir.pwd), 'spinoff'].join('.'))
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ module Spinoff
2
+ module TestRunner
3
+ def self.init(framework)
4
+ STDERR.puts "Test framework: #{framework}"
5
+
6
+ case framework
7
+ when :rspec
8
+ require 'spinoff/test_runner/rspec'
9
+ Spinoff::TestRunner::RSpec.new
10
+ when :testunit
11
+ require 'spinoff/test_runner/test_unit'
12
+ Spinoff::TestRunner::TestUnit.new
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'rspec/core'
2
+
3
+ module Spinoff
4
+ module TestRunner
5
+ class RSpec
6
+ def <<(files)
7
+ # Overwrite the trap_interrupt method to avoid weird behaviour
8
+ # on SIGINT.
9
+ ::RSpec::Core::Runner.class_eval do
10
+ def self.trap_interrupt; end
11
+ end
12
+
13
+ ::RSpec::Core::Runner.run(files)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Spinoff
2
+ module TestRunner
3
+ class TestUnit
4
+ def <<(files)
5
+ Array(files).each {|file| require File.expand_path(file) }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Spinoff
2
+ VERSION = "0.0.1"
3
+ end
data/spinoff.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/spinoff/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Bernd Ahlers"]
6
+ gem.email = ["bernd@tuneafish.de"]
7
+ gem.description = %q{Environment preloader}
8
+ gem.summary = %q{Spinoff preloads your Ruby environment based on an initialization file.}
9
+ gem.homepage = "https://github.com/bernd/spinoff"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "spinoff"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Spinoff::VERSION
17
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spinoff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bernd Ahlers
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-26 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Environment preloader
15
+ email:
16
+ - bernd@tuneafish.de
17
+ executables:
18
+ - spinoff-client
19
+ - spinoff-server
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - .gitignore
24
+ - Gemfile
25
+ - README.md
26
+ - Rakefile
27
+ - bin/spinoff-client
28
+ - bin/spinoff-server
29
+ - lib/spinoff/client.rb
30
+ - lib/spinoff/server.rb
31
+ - lib/spinoff/server/fork.rb
32
+ - lib/spinoff/server/generic.rb
33
+ - lib/spinoff/server/jruby.rb
34
+ - lib/spinoff/socket_path.rb
35
+ - lib/spinoff/test_runner.rb
36
+ - lib/spinoff/test_runner/rspec.rb
37
+ - lib/spinoff/test_runner/test_unit.rb
38
+ - lib/spinoff/version.rb
39
+ - spinoff.gemspec
40
+ homepage: https://github.com/bernd/spinoff
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.11
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Spinoff preloads your Ruby environment based on an initialization file.
64
+ test_files: []