son_of_a_batch 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +66 -0
- data/LICENSE.txt +20 -0
- data/README.textile +65 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/app/endpoints/sleepy.rb +80 -0
- data/app/views/debug.haml +4 -0
- data/app/views/layout.haml +39 -0
- data/app/views/root.haml +28 -0
- data/config/bootstrap.rb +114 -0
- data/config/son_of_a_batch-private.example.rb +8 -0
- data/config/son_of_a_batch-private.rb +5 -0
- data/config/son_of_a_batch.rb +19 -0
- data/lib/boot.rb +72 -0
- data/lib/son_of_a_batch.rb +0 -0
- data/public/stylesheets/style.css +296 -0
- data/son_of_a_batch.gemspec +96 -0
- data/son_of_a_batch.rb +153 -0
- data/spec/son_of_a_batch_spec.rb +7 -0
- data/spec/spec_helper.rb +25 -0
- metadata +203 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# gem 'goliath', :git => 'https://github.com/postrank-labs/goliath.git'
|
4
|
+
gem 'em-synchrony', :git => 'https://github.com/igrigorik/em-synchrony.git'
|
5
|
+
gem 'em-http-request', :git => 'https://github.com/igrigorik/em-http-request.git'
|
6
|
+
gem 'yajl-ruby', "~> 0.8.2"
|
7
|
+
gem 'gorillib', "~> 0.0.4"
|
8
|
+
|
9
|
+
gem 'rack-respond_to'
|
10
|
+
gem 'rack-abstract-format'
|
11
|
+
|
12
|
+
group :development, :test do
|
13
|
+
gem 'bundler', "~> 1.0.12"
|
14
|
+
gem 'yard', "~> 0.6.7"
|
15
|
+
gem 'jeweler', "~> 1.5.2"
|
16
|
+
gem 'rspec', "~> 2.5.0"
|
17
|
+
gem 'rcov', ">= 0"
|
18
|
+
end
|
19
|
+
|
20
|
+
group :test do
|
21
|
+
gem 'spork', '~> 0.9.0.rc5'
|
22
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/igrigorik/em-http-request.git
|
3
|
+
revision: b9c6b4b03f0b4d78f0826314dc1f39f297debc1d
|
4
|
+
specs:
|
5
|
+
em-http-request (1.0.0.beta.3)
|
6
|
+
addressable (>= 2.2.3)
|
7
|
+
em-socksify
|
8
|
+
eventmachine (>= 1.0.0.beta.3)
|
9
|
+
http_parser.rb (>= 0.5.1)
|
10
|
+
|
11
|
+
GIT
|
12
|
+
remote: https://github.com/igrigorik/em-synchrony.git
|
13
|
+
revision: 60887014aa65bc8fb85bcd058c2b5bf938d72ad0
|
14
|
+
specs:
|
15
|
+
em-synchrony (0.3.0.beta.1)
|
16
|
+
eventmachine (>= 1.0.0.beta.1)
|
17
|
+
|
18
|
+
GEM
|
19
|
+
remote: http://rubygems.org/
|
20
|
+
specs:
|
21
|
+
addressable (2.2.5)
|
22
|
+
diff-lcs (1.1.2)
|
23
|
+
em-socksify (0.1.0)
|
24
|
+
eventmachine
|
25
|
+
eventmachine (1.0.0.beta.3)
|
26
|
+
git (1.2.5)
|
27
|
+
gorillib (0.0.4)
|
28
|
+
http_parser.rb (0.5.1)
|
29
|
+
jeweler (1.5.2)
|
30
|
+
bundler (~> 1.0.0)
|
31
|
+
git (>= 1.2.5)
|
32
|
+
rake
|
33
|
+
rack-abstract-format (0.9.9)
|
34
|
+
rack-accept-media-types (0.9)
|
35
|
+
rack-respond_to (0.9.8)
|
36
|
+
rack-accept-media-types (>= 0.6)
|
37
|
+
rake (0.8.7)
|
38
|
+
rcov (0.9.9)
|
39
|
+
rspec (2.5.0)
|
40
|
+
rspec-core (~> 2.5.0)
|
41
|
+
rspec-expectations (~> 2.5.0)
|
42
|
+
rspec-mocks (~> 2.5.0)
|
43
|
+
rspec-core (2.5.1)
|
44
|
+
rspec-expectations (2.5.0)
|
45
|
+
diff-lcs (~> 1.1.2)
|
46
|
+
rspec-mocks (2.5.0)
|
47
|
+
spork (0.9.0.rc5)
|
48
|
+
yajl-ruby (0.8.2)
|
49
|
+
yard (0.6.8)
|
50
|
+
|
51
|
+
PLATFORMS
|
52
|
+
ruby
|
53
|
+
|
54
|
+
DEPENDENCIES
|
55
|
+
bundler (~> 1.0.12)
|
56
|
+
em-http-request!
|
57
|
+
em-synchrony!
|
58
|
+
gorillib (~> 0.0.4)
|
59
|
+
jeweler (~> 1.5.2)
|
60
|
+
rack-abstract-format
|
61
|
+
rack-respond_to
|
62
|
+
rcov
|
63
|
+
rspec (~> 2.5.0)
|
64
|
+
spork (~> 0.9.0.rc5)
|
65
|
+
yajl-ruby (~> 0.8.2)
|
66
|
+
yard (~> 0.6.7)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Philip (flip) Kromer for Infochimps
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
h1. son_of_a_batch
|
2
|
+
|
3
|
+
Smelt from a plentiferous gallimaufry of requests an agglomerated bale of responses. With, y'know, concurrency and all that.
|
4
|
+
|
5
|
+
Install this as a shim to allow batch requests against a remote API.
|
6
|
+
|
7
|
+
h2.
|
8
|
+
|
9
|
+
|
10
|
+
h4. Results vs Errors
|
11
|
+
|
12
|
+
Note that a request may do any of the following:
|
13
|
+
|
14
|
+
* The request might *complete* and be *successful* (eg @200 OK@). Each of these will be in the "results" block, with its status code set appropriately.
|
15
|
+
* The request might *complete* but be *unsuccessful* (eg @404 Not Found@). These are no different to son_of_a_batch, so each of them will also be in the "results" block with its status code set appropriately.
|
16
|
+
* The request might *not complete*. These will be in the "errors" block.
|
17
|
+
|
18
|
+
h2. Usage
|
19
|
+
|
20
|
+
Start a test server:
|
21
|
+
|
22
|
+
<pre>
|
23
|
+
$ ruby app/endpoints/sleepy.rb -sv -p 9002 # an example endpoint that sleeps a given amount of time before responding.
|
24
|
+
[54941:INFO] 2011-04-23 17:22:37 :: Starting server on 0.0.0.0:9002 in development mode. Watch out for stones.
|
25
|
+
|
26
|
+
$ curl 'http://localhost:9002?delay=2.2'
|
27
|
+
{"start":1303597564.663358,"delay":2.2,"actual":2.2001771926879883}
|
28
|
+
</pre>
|
29
|
+
|
30
|
+
Start the son_of_a_batch server:
|
31
|
+
|
32
|
+
<pre>
|
33
|
+
$ ruby app/endpoints/sleepy.rb -sv -p 9002 # an example endpoint that sleeps a given amount of time before responding.
|
34
|
+
[54941:INFO] 2011-04-23 17:22:37 :: Starting server on 0.0.0.0:9002 in development mode. Watch out for stones.
|
35
|
+
|
36
|
+
$ curl 'http://localhost:9002?delay=2.2'
|
37
|
+
{"start":1303597564.663358,"delay":2.2,"actual":2.2001771926879883}
|
38
|
+
</pre>
|
39
|
+
|
40
|
+
h3. Colophon
|
41
|
+
|
42
|
+
Uses
|
43
|
+
|
44
|
+
* Goliath -- fast asynchronous API library
|
45
|
+
* Gorillib -- only the lightest of weight helpers plucked from active_support and friends, with nothing obtrusive required by default.
|
46
|
+
|
47
|
+
h4. Credits
|
48
|
+
|
49
|
+
Built using goliath, gorillib, and the infochimps API
|
50
|
+
|
51
|
+
h4. Contributing to son_of_a_batch
|
52
|
+
|
53
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
54
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
55
|
+
* Fork the project
|
56
|
+
* Start a feature/bugfix branch
|
57
|
+
* Commit and push until you are happy with your contribution
|
58
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
59
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
60
|
+
|
61
|
+
h4. Copyright
|
62
|
+
|
63
|
+
Copyright (c) 2011 Philip (flip) Kromer for Infochimps. See LICENSE.txt for
|
64
|
+
further details.
|
65
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "son_of_a_batch"
|
16
|
+
gem.homepage = "http://infochimps.com/labs"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Smelt from a plentiferous gallimaufry of requests an agglomerated bale of responses. With, y'know, concurrency and all that.}
|
19
|
+
gem.description = %Q{Smelt from a plentiferous gallimaufry of requests an agglomerated bale of responses. With, y'know, concurrency and all that.}
|
20
|
+
gem.email = "coders@infochimps.com"
|
21
|
+
gem.authors = ["Philip (flip) Kromer for Infochimps"]
|
22
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
25
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
26
|
+
end
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
28
|
+
|
29
|
+
require 'rspec/core'
|
30
|
+
require 'rspec/core/rake_task'
|
31
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
32
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
33
|
+
end
|
34
|
+
|
35
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
36
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
37
|
+
spec.rcov = true
|
38
|
+
end
|
39
|
+
|
40
|
+
task :default => :spec
|
41
|
+
|
42
|
+
require 'yard'
|
43
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
@@ -0,0 +1,80 @@
|
|
1
|
+
$: << File.dirname(__FILE__)+'/../../lib'
|
2
|
+
require 'boot'
|
3
|
+
require 'goliath'
|
4
|
+
require 'rack/abstract_format'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Wait the amount of time given by the 'delay' parameter before responding
|
8
|
+
# Handles multiple parallel requests -- its EM::Synchrony call allows the
|
9
|
+
# reactor to keep spinning.
|
10
|
+
#
|
11
|
+
class Sleepy < Goliath::API
|
12
|
+
use Goliath::Rack::Params # parse query & body params
|
13
|
+
use Goliath::Rack::Formatters::JSON # JSON output formatter
|
14
|
+
use Goliath::Rack::Render # auto-negotiate response format
|
15
|
+
use Goliath::Rack::ValidationError # catch and render validation errors
|
16
|
+
use Goliath::Rack::Validation::NumericRange, {:key => 'delay', :max => 15.0, :default => 1.5, :as => Float}
|
17
|
+
use Rack::AbstractFormat, 'application/json'
|
18
|
+
|
19
|
+
def response(env)
|
20
|
+
start = Time.now.utc.to_f
|
21
|
+
delay = env.params['delay']
|
22
|
+
env.logger.debug "timer #{start} [#{delay}]: start of response"
|
23
|
+
|
24
|
+
# EM::Synchrony call allows the reactor to keep spinning: HOORAY CONCURRENCY
|
25
|
+
EM::Synchrony.sleep(delay)
|
26
|
+
body = { :start => start, :delay => delay, :actual => (Time.now.utc.to_f - start) }
|
27
|
+
|
28
|
+
env.logger.debug "timer #{start} [#{delay}]: after sleep: #{body.inspect}"
|
29
|
+
[200, {'X-Responder' => self.class.to_s, 'X-Sleepy-Delay' => delay.to_s, }, body]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Proof of concurrency!
|
35
|
+
#
|
36
|
+
# $ ruby app/rack/sleepy.rb -sv -p 9000
|
37
|
+
# $ ab -c 10 -n 50 'http://127.0.0.1:9000/?delay=2.0'
|
38
|
+
# This is ApacheBench, Version 2.3 <$Revision: 655654 $>
|
39
|
+
# Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
|
40
|
+
# Licensed to The Apache Software Foundation, http://www.apache.org/
|
41
|
+
#
|
42
|
+
# Benchmarking 127.0.0.1 (be patient).....done
|
43
|
+
#
|
44
|
+
#
|
45
|
+
# Server Software: Goliath
|
46
|
+
# Server Hostname: 127.0.0.1
|
47
|
+
# Server Port: 9000
|
48
|
+
#
|
49
|
+
# Document Path: /?delay=2.0
|
50
|
+
# Document Length: 88 bytes
|
51
|
+
#
|
52
|
+
# Concurrency Level: 10
|
53
|
+
# Time taken for tests: 10.117 seconds
|
54
|
+
# Complete requests: 50
|
55
|
+
# Failed requests: 0
|
56
|
+
# Write errors: 0
|
57
|
+
# Total transferred: 11550 bytes
|
58
|
+
# HTML transferred: 4400 bytes
|
59
|
+
# Requests per second: 4.94 [#/sec] (mean)
|
60
|
+
# Time per request: 2023.306 [ms] (mean)
|
61
|
+
# Time per request: 202.331 [ms] (mean, across all concurrent requests)
|
62
|
+
# Transfer rate: 1.11 [Kbytes/sec] received
|
63
|
+
#
|
64
|
+
# Connection Times (ms)
|
65
|
+
# min mean[+/-sd] median max
|
66
|
+
# Connect: 0 0 0.1 0 1
|
67
|
+
# Processing: 2004 2018 11.0 2017 2044
|
68
|
+
# Waiting: 2004 2018 11.0 2017 2044
|
69
|
+
# Total: 2004 2018 11.0 2018 2044
|
70
|
+
#
|
71
|
+
# Percentage of the requests served within a certain time (ms)
|
72
|
+
# 50% 2018
|
73
|
+
# 66% 2025
|
74
|
+
# 75% 2028
|
75
|
+
# 80% 2029
|
76
|
+
# 90% 2032
|
77
|
+
# 95% 2036
|
78
|
+
# 98% 2044
|
79
|
+
# 99% 2044
|
80
|
+
# 100% 2044 (longest request)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
- title ||= 'Goliath'
|
2
|
+
!!! 5
|
3
|
+
%html
|
4
|
+
%head
|
5
|
+
%meta{ :charset => "utf-8" }/
|
6
|
+
%meta{ :content => "IE=edge,chrome=1", "http-equiv" => "X-UA-Compatible" }/
|
7
|
+
|
8
|
+
%title= title
|
9
|
+
<link href="data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAAF0lEQVRIx2NgGAWjYBSMglEwCkbBSAcACBAAAeaR9cIAAAAASUVORK5CYII=" rel="icon" type="image/x-icon" />
|
10
|
+
%link{ :href => "/stylesheets/style.css", :rel => "stylesheet", :type => "text/css", :media => "all" }
|
11
|
+
|
12
|
+
%body{ :lang => 'en' }
|
13
|
+
#header-container
|
14
|
+
%header.wrapper
|
15
|
+
%h1#title
|
16
|
+
== <a href="/">#{title}</a>
|
17
|
+
|
18
|
+
%nav
|
19
|
+
%ul.menu
|
20
|
+
%li.item
|
21
|
+
<a href="/">Home</a>
|
22
|
+
%li.item
|
23
|
+
<a href="/debug">Debug</a>
|
24
|
+
%li
|
25
|
+
|
26
|
+
#main.wrapper{ :role => 'main' }
|
27
|
+
!= yield
|
28
|
+
|
29
|
+
#footer-container
|
30
|
+
%footer.wrapper
|
31
|
+
.copyright
|
32
|
+
%p Copyright © #{Time.now.strftime("%Y")}
|
33
|
+
|
34
|
+
%script{ :src => "http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js", :type => "text/javascript" }/
|
35
|
+
:javascript
|
36
|
+
!window.jQuery && document.write(unescape('%3Cscript src="/javascripts/jquery.min.js"%3E%3C/script%3E'))
|
37
|
+
|
38
|
+
/
|
39
|
+
haml layout
|
data/app/views/root.haml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
%h2 Routes
|
2
|
+
|
3
|
+
%ul
|
4
|
+
%li <a href="/joke">Tell me a joke</a>
|
5
|
+
%li <a href="/erb_me">Tell me a joke, but with a boring .erb layout</a>
|
6
|
+
%li <a href="/debug">debug info</a>
|
7
|
+
%li <a href="/oops">Template missing</a>
|
8
|
+
|
9
|
+
%h2 Dashboard
|
10
|
+
|
11
|
+
%table.vertical
|
12
|
+
%tr
|
13
|
+
%th Script
|
14
|
+
%td= $0
|
15
|
+
%tr
|
16
|
+
%th Server Port
|
17
|
+
%td= env['SERVER_PORT']
|
18
|
+
%tr
|
19
|
+
%th Latency
|
20
|
+
%td= recent_latency
|
21
|
+
%tr
|
22
|
+
%th Request took
|
23
|
+
%td= (Time.now.to_f - env[:start_time]).round(3)
|
24
|
+
%tr
|
25
|
+
%th Config
|
26
|
+
%td
|
27
|
+
%pre
|
28
|
+
= "\n"+JSON.pretty_generate(env.config)
|
data/config/bootstrap.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#/ Usage: config/bootstrap.rb [<options>]
|
3
|
+
#/ Bootstraps the gem environment.
|
4
|
+
#/
|
5
|
+
#/ Options are passed through to the bundle-install command. In most cases you
|
6
|
+
#/ won't need these. They're used primarily in production environments.
|
7
|
+
#/ --local use gems in vendor/cache instead of rubygems.org
|
8
|
+
#/ --without=<groups> do not install gems in the groups specified
|
9
|
+
#
|
10
|
+
# =============================================================================
|
11
|
+
# Uses bundler to install all gems specified in the Gemfile under vendor/gems,
|
12
|
+
# records the load path in config/loadpath, and generates bundler-free binstubs
|
13
|
+
# under bin.
|
14
|
+
#
|
15
|
+
# The basic idea is to use bundler to install necessary gems but not
|
16
|
+
# rely on it to manage the load path at runtime because it's slow. Requiring
|
17
|
+
# 'bundler/setup' takes ~500ms user CPU time in production and ~1500ms in
|
18
|
+
# development/test. This makes it unusable in scenarios that require a fast
|
19
|
+
# boot (e.g., script/gerve, proxymachine daemons, ernie/smoke, etc.). It's also
|
20
|
+
# a problem in development where it slows tools like rake command line
|
21
|
+
# completion to a crawl and adds at least a second to single-file test runs.
|
22
|
+
#
|
23
|
+
# There's very little reason to use bundler at runtime since everything
|
24
|
+
# is known at install time. We simply save off the result of the work done by
|
25
|
+
# bundle/setup and use it until bundle-install is run again.
|
26
|
+
|
27
|
+
# show usage message with --help
|
28
|
+
if ARGV.include?('--help')
|
29
|
+
system "grep '^#/' <'#{__FILE__}' |cut -c4-"
|
30
|
+
exit 2
|
31
|
+
end
|
32
|
+
|
33
|
+
# go into the project root because it makes everything easier
|
34
|
+
root = File.expand_path('../..', __FILE__)
|
35
|
+
Dir.chdir(root)
|
36
|
+
|
37
|
+
# point bundler to the right stuff
|
38
|
+
ENV['BUNDLE_GEMFILE'] = "#{root}/Gemfile"
|
39
|
+
ENV['BUNDLE_PATH'] = "#{root}/vendor/gems"
|
40
|
+
|
41
|
+
# bring in rubygems and make sure bundler is installed.
|
42
|
+
require 'rubygems'
|
43
|
+
begin
|
44
|
+
require 'bundler'
|
45
|
+
rescue LoadError => boom
|
46
|
+
warn "Bundler not found. Install it with `gem install bundler' and try again."
|
47
|
+
exit 0
|
48
|
+
end
|
49
|
+
|
50
|
+
# record the Gemfile checksum so we can tell if the Gemfile has changed
|
51
|
+
# since our loadpath was last generated. this is used in config/boot.rb
|
52
|
+
# to verify the environment is bootstrapped and up-to-date.
|
53
|
+
checksum = `cksum Gemfile`.to_i
|
54
|
+
installed = File.read('.bundle/checksum').to_i rescue nil
|
55
|
+
|
56
|
+
# run a quick check to see if everything's installed and up-to-date so we can
|
57
|
+
# skip the install and loadpath generation step if possible.
|
58
|
+
if checksum == installed && system('bundle check 1>/dev/null 2>&1')
|
59
|
+
puts "Gem environment up-to-date."
|
60
|
+
else
|
61
|
+
# run bundle-install to install any missing gems
|
62
|
+
argv = ['--no-color', 'install', '--path', 'vendor/gems'] + ARGV
|
63
|
+
system("bundle", *argv) || begin
|
64
|
+
warn "bundle executable not found. Ensure bundler is installed (`gem " +
|
65
|
+
"install bundler`) and that the gem bin path is in your PATH"
|
66
|
+
exit($?.exitstatus)
|
67
|
+
end
|
68
|
+
|
69
|
+
# load the Gemfile
|
70
|
+
bundle = Bundler.setup
|
71
|
+
|
72
|
+
# extract load paths for each gem and write to the config/loadpath file.
|
73
|
+
load_paths = []
|
74
|
+
bundle.gems.each do |gem|
|
75
|
+
next if gem.name == 'bundler'
|
76
|
+
gem.load_paths.each do |path|
|
77
|
+
if path[0, root.size] == root
|
78
|
+
path = path[(root.size + 1), path.size]
|
79
|
+
load_paths << path
|
80
|
+
else
|
81
|
+
warn "external load path directory detected: #{path}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# move the loadpath and checksum files into place if everything was installed
|
87
|
+
# okay and the load path file was written successfully.
|
88
|
+
File.open('.bundle/loadpath+', 'wb') { |fd| fd.write(load_paths.join("\n")) }
|
89
|
+
File.rename('.bundle/loadpath+', '.bundle/loadpath')
|
90
|
+
File.open('.bundle/checksum', 'wb') { |fd| fd.puts(checksum) }
|
91
|
+
|
92
|
+
# write binstubs for all executables. we can't use bundler's --binstubs option
|
93
|
+
# because the generated executables require 'bundler/setup'. the binstubs
|
94
|
+
# generated here require only config/boot.rb, which sets up the loadpath
|
95
|
+
# manually using the .bundle/loadpath file.
|
96
|
+
Dir.mkdir "vendor/bin" unless File.directory?("vendor/bin")
|
97
|
+
template = DATA.read
|
98
|
+
lineno = File.read(__FILE__).count("\n") - template.count("\n")
|
99
|
+
bundle.gems.each do |spec|
|
100
|
+
spec.executables.each do |executable|
|
101
|
+
script = eval('%Q{' + template + '}', binding, __FILE__, lineno)
|
102
|
+
File.open("vendor/bin/#{executable}+", 'wb') { |fd| fd.write(script) }
|
103
|
+
File.chmod 0755, "vendor/bin/#{executable}+"
|
104
|
+
File.rename("vendor/bin/#{executable}+", "vendor/bin/#{executable}")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
__END__
|
110
|
+
#!/usr/bin/env #{RbConfig::CONFIG['ruby_install_name']}
|
111
|
+
#
|
112
|
+
# This file was generated by config/bootstrap.
|
113
|
+
require '#{root}/lib/boot'
|
114
|
+
load File.join('#{spec.full_gem_path}', 'vendor', '#{spec.bindir}', '#{executable}')
|