bumbler 0.1.2 → 0.1.3
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/bumbler.gemspec +11 -7
- data/lib/bumbler.rb +3 -2
- data/lib/bumbler/bundler.rb +40 -39
- data/lib/bumbler/hooks.rb +64 -53
- data/lib/bumbler/progress.rb +80 -80
- data/lib/bumbler/stats.rb +30 -0
- metadata +3 -2
data/bumbler.gemspec
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
|
3
|
+
# Bumbler may be already required due to the RUBYOPT=-rbumbler/go that we use to activate. Clear it
|
4
|
+
# if so to get rid of our warning, and then load it. We just need the version number
|
5
|
+
Bumbler.send(:remove_const, :VERSION) if Object.const_defined? :Bumbler
|
6
|
+
load File.expand_path('../lib/bumbler.rb', __FILE__)
|
3
7
|
|
4
8
|
Gem::Specification.new do |s|
|
5
|
-
s.name =
|
9
|
+
s.name = 'bumbler'
|
6
10
|
s.version = Bumbler::VERSION
|
7
11
|
s.platform = Gem::Platform::RUBY
|
8
|
-
s.authors = [
|
9
|
-
s.email = [
|
10
|
-
s.homepage =
|
12
|
+
s.authors = ['Ian MacLeod']
|
13
|
+
s.email = ['ian@nevir.net']
|
14
|
+
s.homepage = 'https://github.com/nevir/Bumbler'
|
11
15
|
s.summary = %q{Track the load progress of your Bundler-based projects}
|
12
16
|
s.description = %q{Why stare blankly at your terminal window when you can clutter it up with awesome progress bars?}
|
13
17
|
|
14
|
-
s.rubyforge_project =
|
18
|
+
s.rubyforge_project = 'bumbler'
|
15
19
|
|
16
20
|
s.files = `git ls-files`.split("\n")
|
17
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
22
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
-
s.require_paths = [
|
23
|
+
s.require_paths = ['lib']
|
20
24
|
end
|
data/lib/bumbler.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module Bumbler
|
2
2
|
# We can be required twice due to the command line require
|
3
|
-
VERSION = '0.1.
|
3
|
+
VERSION = '0.1.3' unless self.const_defined? :VERSION
|
4
4
|
|
5
|
-
autoload :Hooks, 'bumbler/hooks'
|
6
5
|
autoload :Bundler, 'bumbler/bundler'
|
6
|
+
autoload :Hooks, 'bumbler/hooks'
|
7
7
|
autoload :Progress, 'bumbler/progress'
|
8
|
+
autoload :Stats, 'bumbler/stats'
|
8
9
|
end
|
data/lib/bumbler/bundler.rb
CHANGED
@@ -1,51 +1,52 @@
|
|
1
1
|
module Bumbler
|
2
2
|
module Bundler
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def self.require_started(path)
|
11
|
-
gem_name = self.gem_for_require(path)
|
12
|
-
return unless gem_name
|
13
|
-
|
14
|
-
Bumbler::Progress.item_started(:bundler, gem_name)
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.require_finished(path, load_time)
|
18
|
-
self.read_bundler_environment if @gem_state.nil?
|
3
|
+
class << self
|
4
|
+
# Returns which gem a require maps to, or nil.
|
5
|
+
def gem_for_require(path)
|
6
|
+
self.read_bundler_environment if @require_map.nil?
|
7
|
+
|
8
|
+
return @require_map[path]
|
9
|
+
end
|
19
10
|
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
def require_started(path)
|
12
|
+
gem_name = self.gem_for_require(path)
|
13
|
+
return unless gem_name
|
14
|
+
|
15
|
+
Bumbler::Progress.item_started(:bundler, gem_name)
|
16
|
+
end
|
23
17
|
|
24
|
-
|
18
|
+
def require_finished(path, load_time)
|
19
|
+
self.read_bundler_environment if @gem_state.nil?
|
20
|
+
|
21
|
+
# Tick it off for the gem.
|
22
|
+
gem_name = self.gem_for_require(path)
|
23
|
+
return unless gem_name
|
24
|
+
|
25
|
+
@gem_state[gem_name][path] = true
|
26
|
+
|
27
|
+
if @gem_state[gem_name].values.all?
|
28
|
+
Bumbler::Progress.item_finished(:bundler, gem_name, load_time)
|
29
|
+
end
|
30
|
+
end
|
25
31
|
|
26
|
-
|
27
|
-
|
32
|
+
def start!
|
33
|
+
self.read_bundler_environment
|
28
34
|
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.start!
|
32
|
-
self.read_bundler_environment
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
def self.read_bundler_environment
|
37
|
-
@require_map = {}
|
38
|
-
@gem_state = {}
|
39
35
|
|
40
|
-
|
41
|
-
@
|
36
|
+
def read_bundler_environment
|
37
|
+
@require_map = {}
|
38
|
+
@gem_state = {}
|
42
39
|
|
43
|
-
|
44
|
-
@
|
45
|
-
|
40
|
+
::Bundler.environment.current_dependencies.each do |spec|
|
41
|
+
@gem_state[spec.name] = {}
|
42
|
+
|
43
|
+
Array(spec.autorequire || spec.name).each do |path|
|
44
|
+
@require_map[path] = spec.name
|
45
|
+
@gem_state[spec.name][path] = false
|
46
|
+
end
|
47
|
+
|
48
|
+
Bumbler::Progress.register_item(:bundler, spec.name)
|
46
49
|
end
|
47
|
-
|
48
|
-
Bumbler::Progress.register_item(:bundler, spec.name)
|
49
50
|
end
|
50
51
|
end
|
51
52
|
end
|
data/lib/bumbler/hooks.rb
CHANGED
@@ -1,72 +1,83 @@
|
|
1
1
|
module Bumbler
|
2
2
|
module Hooks
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
SLOW_REQUIRE_THRESHOLD = 100.0
|
4
|
+
@previous_gems = {}
|
5
|
+
@slow_requires = {}
|
6
|
+
|
7
|
+
# Everything's a class method (we're a singleton)
|
8
|
+
class << self
|
9
|
+
def slow_requires
|
10
|
+
@slow_requires
|
11
|
+
end
|
6
12
|
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
# Inject our custom handling of require into the Kernel.
|
14
|
+
def hook_require!
|
15
|
+
@hooking_require = true
|
16
|
+
|
17
|
+
# There are two independent require methods. Joy!
|
18
|
+
::Kernel.module_eval do
|
19
|
+
class << self
|
20
|
+
orig_public_require = Kernel.public_method(:require)
|
21
|
+
define_method(:require) do |path, *args|
|
22
|
+
::Bumbler::Hooks.handle_require(path) do
|
23
|
+
orig_public_require.call(path, *args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
orig_instance_require = self.instance_method(:require)
|
11
29
|
define_method(:require) do |path, *args|
|
12
30
|
::Bumbler::Hooks.handle_require(path) do
|
13
|
-
|
31
|
+
orig_instance_require.bind(self).call(path, *args)
|
14
32
|
end
|
15
33
|
end
|
16
34
|
end
|
17
35
|
|
18
|
-
|
19
|
-
define_method(:require) do |path, *args|
|
20
|
-
::Bumbler::Hooks.handle_require(path) do
|
21
|
-
orig_instance_require.bind(self).call(path, *args)
|
22
|
-
end
|
23
|
-
end
|
36
|
+
@hooking_require = nil
|
24
37
|
end
|
25
38
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# Fix those hooks.
|
36
|
-
::Bumbler::Hooks.hook_require!
|
39
|
+
# Even better: Other gems hook require as well. The instance method one at least.
|
40
|
+
def watch_require!
|
41
|
+
::Kernel.module_eval do
|
42
|
+
# It isn't previously defined in Kernel. This could be a bit dangerous, though.
|
43
|
+
def self.method_added(method_name, *args)
|
44
|
+
if method_name == :require && !::Bumbler::Hooks.hooking_require?
|
45
|
+
# Fix those hooks.
|
46
|
+
::Bumbler::Hooks.hook_require!
|
47
|
+
end
|
37
48
|
end
|
38
49
|
end
|
39
50
|
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
def self.hooking_require?
|
44
|
-
@hooking_require
|
45
|
-
end
|
46
|
-
|
47
|
-
# Actually do something about a require here.
|
48
|
-
def self.handle_require(path)
|
49
|
-
# break out early if we're already handling this
|
50
|
-
return yield if path == @previous_require
|
51
|
-
@previous_require = path
|
52
|
-
|
53
|
-
# Shortcut unless we're tracking the gem
|
54
|
-
gem_name = Bumbler::Bundler.gem_for_require(path)
|
55
|
-
return yield unless gem_name
|
56
|
-
|
57
|
-
# Track load starts
|
58
|
-
@previous_gems ||= {}
|
59
|
-
Bumbler::Bundler.require_started(path) unless @previous_gems[gem_name]
|
60
|
-
@previous_gems[gem_name] = true
|
61
51
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
require_time = (Time.now.to_f - start) * 1000 # ms
|
66
|
-
|
67
|
-
Bumbler::Bundler.require_finished(path, require_time) if result
|
52
|
+
def hooking_require?
|
53
|
+
@hooking_require
|
54
|
+
end
|
68
55
|
|
69
|
-
|
56
|
+
# Actually do something about a require here.
|
57
|
+
def handle_require(path)
|
58
|
+
# break out early if we're already handling this
|
59
|
+
return yield if path == @previous_require
|
60
|
+
@previous_require = path
|
61
|
+
|
62
|
+
# Shortcut unless we're tracking the gem
|
63
|
+
gem_name = Bumbler::Bundler.gem_for_require(path)
|
64
|
+
return yield unless gem_name
|
65
|
+
|
66
|
+
# Track load starts
|
67
|
+
Bumbler::Bundler.require_started(path) unless @previous_gems[gem_name]
|
68
|
+
@previous_gems[gem_name] = true
|
69
|
+
|
70
|
+
# Let's time them
|
71
|
+
start = Time.now.to_f
|
72
|
+
result = yield
|
73
|
+
require_time = (Time.now.to_f - start) * 1000 # ms
|
74
|
+
|
75
|
+
@slow_requires[path] = require_time if require_time > SLOW_REQUIRE_THRESHOLD
|
76
|
+
|
77
|
+
Bumbler::Bundler.require_finished(path, require_time) if result
|
78
|
+
|
79
|
+
return result
|
80
|
+
end
|
70
81
|
end
|
71
82
|
end
|
72
83
|
end
|
data/lib/bumbler/progress.rb
CHANGED
@@ -1,94 +1,94 @@
|
|
1
1
|
module Bumbler
|
2
|
-
# Singletons are fun, yay!
|
3
2
|
module Progress
|
4
|
-
|
5
|
-
|
6
|
-
unless self.registry[type][name]
|
7
|
-
@item_count ||= 0
|
8
|
-
@item_count += 1
|
9
|
-
end
|
10
|
-
|
11
|
-
self.registry[type][name] = {}
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.item_started(type, name)
|
15
|
-
@curr_item = {:type => type, :name => name}
|
16
|
-
|
17
|
-
self.render_progress
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.item_finished(type, name, time)
|
21
|
-
self.registry[type][name] = {:time => time}
|
22
|
-
|
23
|
-
@loaded_items ||= 0
|
24
|
-
@loaded_items += 1
|
25
|
-
|
26
|
-
@prev_item = {:type => type, :name => name, :time => time}
|
27
|
-
@curr_item = nil if @curr_item && @curr_item[:name] == @prev_item[:name] && @curr_item[:type] == @prev_item[:type]
|
28
|
-
|
29
|
-
self.render_progress
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.start!
|
33
|
-
# No-op for now.
|
34
|
-
end
|
3
|
+
@item_count = 0
|
4
|
+
@loaded_items = 0
|
35
5
|
|
36
|
-
private
|
37
6
|
# registry[item_type][item_name] = {:time => 123.45}
|
38
|
-
|
39
|
-
@registry ||= Hash.new { |h,k| h[k] = {} }
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.tty_width
|
43
|
-
`tput cols`.to_i || 80
|
44
|
-
end
|
7
|
+
@registry = Hash.new { |h,k| h[k] = {} }
|
45
8
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
fill_size = ((@loaded_items.to_f / @item_count.to_f) * inner_size).to_i
|
50
|
-
fill = '#' * fill_size
|
51
|
-
empty = ' ' * (inner_size - fill_size)
|
52
|
-
|
53
|
-
return "[#{fill}#{empty}]"
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.render_progress
|
57
|
-
unless $stdout.tty?
|
58
|
-
puts '(%s/%d) %s' % [@loaded_items.to_s.rjust(@item_count.to_s.size), @item_count, message]
|
59
|
-
return
|
9
|
+
class << self
|
10
|
+
def registry
|
11
|
+
@registry
|
60
12
|
end
|
61
13
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@loaded_items ||= 0
|
71
|
-
@item_count ||= 0
|
14
|
+
def register_item(type, name)
|
15
|
+
# Build a blank key for the item
|
16
|
+
unless @registry[type][name]
|
17
|
+
@item_count += 1
|
18
|
+
end
|
19
|
+
|
20
|
+
@registry[type][name] = {}
|
21
|
+
end
|
72
22
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
count = '(%s/%d) ' % [@loaded_items.to_s.rjust(@item_count.to_s.size), @item_count]
|
79
|
-
current = @curr_item ? "#{@curr_item[:name]}... " : ''
|
80
|
-
prev = @prev_item ? '%s (%sms)' % [@prev_item[:name], ('%.2f' % @prev_item[:time]).rjust(7)] : ''
|
23
|
+
def item_started(type, name)
|
24
|
+
@curr_item = {:type => type, :name => name}
|
25
|
+
|
26
|
+
self.render_progress
|
27
|
+
end
|
81
28
|
|
82
|
-
|
83
|
-
|
29
|
+
def item_finished(type, name, time)
|
30
|
+
@registry[type][name] = {:time => time}
|
31
|
+
|
32
|
+
@loaded_items += 1
|
33
|
+
|
34
|
+
@prev_item = {:type => type, :name => name, :time => time}
|
35
|
+
@curr_item = nil if @curr_item && @curr_item[:name] == @prev_item[:name] && @curr_item[:type] == @prev_item[:type]
|
36
|
+
|
37
|
+
self.render_progress
|
38
|
+
end
|
84
39
|
|
85
|
-
|
86
|
-
|
40
|
+
def start!
|
41
|
+
# No-op for now.
|
42
|
+
end
|
87
43
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
44
|
+
def tty_width
|
45
|
+
`tput cols`.to_i || 80
|
46
|
+
end
|
47
|
+
|
48
|
+
def bar(width)
|
49
|
+
inner_size = width - 2
|
50
|
+
|
51
|
+
fill_size = ((@loaded_items.to_f / @item_count.to_f) * inner_size).to_i
|
52
|
+
fill = '#' * fill_size
|
53
|
+
empty = ' ' * (inner_size - fill_size)
|
54
|
+
|
55
|
+
return "[#{fill}#{empty}]"
|
56
|
+
end
|
57
|
+
|
58
|
+
def render_progress
|
59
|
+
unless $stdout.tty?
|
60
|
+
puts '(%s/%d) %s' % [@loaded_items.to_s.rjust(@item_count.to_s.size), @item_count, message]
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
# Do nothing if we don't have any items to load
|
65
|
+
return if @item_count == 0
|
66
|
+
|
67
|
+
width = self.tty_width
|
68
|
+
|
69
|
+
print "\r\e[A\r\e[A" if @outputted_once
|
70
|
+
@outputted_once = true
|
71
|
+
|
72
|
+
# Output components:
|
73
|
+
# [#######################################]
|
74
|
+
# (##/##) <current>... <prev> (####.##ms)
|
75
|
+
#
|
76
|
+
# Skip the current if there isn't enough room
|
77
|
+
count = '(%s/%d) ' % [@loaded_items.to_s.rjust(@item_count.to_s.size), @item_count]
|
78
|
+
current = @curr_item ? "#{@curr_item[:name]}... " : ''
|
79
|
+
prev = @prev_item ? '%s (%sms)' % [@prev_item[:name], ('%.2f' % @prev_item[:time]).rjust(7)] : ''
|
80
|
+
|
81
|
+
# Align the bottom row
|
82
|
+
space_for_current = width - (count.length + prev.length)
|
83
|
+
|
84
|
+
# Render the progress
|
85
|
+
puts self.bar(width)
|
86
|
+
|
87
|
+
if space_for_current >= current.length
|
88
|
+
puts count + current + prev.rjust(width - count.length - current.length)
|
89
|
+
else
|
90
|
+
puts count + prev.rjust(width - count.length)
|
91
|
+
end
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Bumbler
|
2
|
+
module Stats
|
3
|
+
class << self
|
4
|
+
def tracked_items
|
5
|
+
Bumbler::Progress.registry.each do |type, items|
|
6
|
+
puts "Stats for #{type} items:"
|
7
|
+
|
8
|
+
items.to_a.sort_by! {|n,d| d[:time].to_f}.each do |name, info|
|
9
|
+
if info[:time]
|
10
|
+
puts ' %s %s' % [('%.2f' % info[:time]).rjust(8), name]
|
11
|
+
else
|
12
|
+
puts " pending: #{name}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def all_slow_items
|
21
|
+
puts "Slow requires:"
|
22
|
+
Bumbler::Hooks.slow_requires.to_a.sort_by! {|n,t| t}.each do |name, time|
|
23
|
+
puts ' %s %s' % [('%.2f' % time).rjust(8), name]
|
24
|
+
end
|
25
|
+
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: bumbler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ian MacLeod
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-04-
|
13
|
+
date: 2011-04-23 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- lib/bumbler/go.rb
|
35
35
|
- lib/bumbler/hooks.rb
|
36
36
|
- lib/bumbler/progress.rb
|
37
|
+
- lib/bumbler/stats.rb
|
37
38
|
has_rdoc: true
|
38
39
|
homepage: https://github.com/nevir/Bumbler
|
39
40
|
licenses: []
|