gem_footprint_analyzer 0.1.5 → 0.1.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e977cf9fc36c1045294e18296c00c63ab7b7e1ccf482691cb85a231a4e18273
4
- data.tar.gz: d917cd40bb4ca7d2f608928337a880a04ef7bdb71a352fccb7279e46ca40d42b
3
+ metadata.gz: 4ea3f3ac4a4cdc86a560ae500fdbdc59423afe2d28ac9f16f5fdc7efe5d433fc
4
+ data.tar.gz: 5baf82c20f15679d84b56f86e303e81325aadbc4577fdcc6bc36a388d0b92baa
5
5
  SHA512:
6
- metadata.gz: c6c448e829e042a6620f774102cb04a80eca75fd9bc4c3b1593ffcd6b4947088493d51e789f90a298176c3f7c849c924f40f923cd5c923e18aa70b2a7e977fac
7
- data.tar.gz: f16657789a803c5ee278e6af25e5b8505d2c97ebae833ee2b3f5fb13a69febe5b94509459cd6452e6e4cf6192c98ec743c672c20228db80fa5c61cd8d5b7e0b9
6
+ metadata.gz: 54b934c3cb13d9b564f52abc70adc1286c5842acb2e0c25d178131d6d1e653732f9a1d49df807004fc434089e97f119fbf32ddee75af29e488e1df415b58bbfe
7
+ data.tar.gz: 50bb084a66ae63808ee91acde1c257cda615cb28cb2ea2ee71463306e7d8ca2b59a3fcf1e4d09d2fe3ebef8e1c6fb12e4440b4a5c3f523a6108b03bf4d44fbdc
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gem_footprint_analyzer (0.1.5)
4
+ gem_footprint_analyzer (0.1.6)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -10,7 +10,7 @@ GEM
10
10
  diff-lcs (1.3)
11
11
  jaro_winkler (1.5.1)
12
12
  parallel (1.12.1)
13
- parser (2.5.1.2)
13
+ parser (2.5.3.0)
14
14
  ast (~> 2.4.0)
15
15
  powerpack (0.1.2)
16
16
  rainbow (3.0.0)
@@ -36,8 +36,8 @@ GEM
36
36
  rainbow (>= 2.2.2, < 4.0)
37
37
  ruby-progressbar (~> 1.7)
38
38
  unicode-display_width (~> 1.4.0)
39
- rubocop-rspec (1.30.0)
40
- rubocop (>= 0.58.0)
39
+ rubocop-rspec (1.30.1)
40
+ rubocop (>= 0.60.0)
41
41
  ruby-progressbar (1.10.0)
42
42
  unicode-display_width (1.4.0)
43
43
 
@@ -50,7 +50,7 @@ DEPENDENCIES
50
50
  rake (~> 10.0)
51
51
  rspec (~> 3.0)
52
52
  rubocop (~> 0.60.0)
53
- rubocop-rspec
53
+ rubocop-rspec (~> 1.30)
54
54
 
55
55
  BUNDLED WITH
56
56
  1.17.1
data/README.md CHANGED
@@ -10,7 +10,7 @@ Requires Ruby >= 2.2.0, works on Linux and MacOS.
10
10
  Add this line to your application's Gemfile:
11
11
 
12
12
  ```ruby
13
- gem 'gem_footprint_analyzer'
13
+ gem 'gem_footprint_analyzer', require: false
14
14
  ```
15
15
 
16
16
  And then execute:
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby --disable=did_you_mean --disable=gem --disable=rubyopt
1
+ #!/usr/bin/env ruby --disable=gem --disable=rubyopt
2
2
 
3
3
  $LOAD_PATH.unshift(File.join(__dir__, '..', 'lib'))
4
4
  $LOAD_PATH.unshift('./lib') if Dir.exist?('lib')
@@ -7,7 +7,7 @@ require 'gem_footprint_analyzer'
7
7
 
8
8
  cli = GemFootprintAnalyzer::CLI.new
9
9
 
10
- t1 = Time.now.to_f
10
+ t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
11
11
  cli.run(ARGV)
12
- t2 = Time.now.to_f
12
+ t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
13
13
  STDERR.puts(format("\nTotal runtime %0.4fs", (t2 - t1)))
@@ -12,11 +12,12 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = %q{A simple tool to analyze footprint of Ruby requires.}
13
13
  spec.homepage = "https://github.com/irvingwashington/gem_footprint_analyzer"
14
14
  spec.license = "MIT"
15
+ spec.required_ruby_version = '>= 2.2.0'
15
16
 
16
17
  # Specify which files should be added to the gem when it is released.
17
18
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
19
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec/|\.)}) }
20
21
  end
21
22
  spec.bindir = "exe"
22
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -26,5 +27,5 @@ Gem::Specification.new do |spec|
26
27
  spec.add_development_dependency "rake", "~> 10.0"
27
28
  spec.add_development_dependency "rspec", "~> 3.0"
28
29
  spec.add_development_dependency "rubocop", "~> 0.60.0"
29
- spec.add_development_dependency "rubocop-rspec"
30
+ spec.add_development_dependency "rubocop-rspec", "~> 1.30"
30
31
  end
@@ -5,16 +5,17 @@ module GemFootprintAnalyzer
5
5
  # Require calls are interwoven with RSS checks done from the parent process, require timing
6
6
  # is gathered in the child process and passed along to the parent.
7
7
  class Analyzer
8
+ # @param fifos [Hash<Symbol>] Hash containing filenames of :child and :parent FIFO files
9
+ def initialize(fifos)
10
+ @fifos = fifos
11
+ end
12
+
8
13
  # @param library [String] name of the library or parameter for the gem method
9
14
  # (ex. 'activerecord', 'activesupport')
10
15
  # @param require_string [String|nil] optional require string, if it differs from the gem name
11
16
  # (ex. 'active_record', 'active_support/time')
12
17
  # @return [Array<Hash>] list of require-data-hashes, first element contains base level RSS,
13
18
  # last element can be treated as a summary as effectively it consists of all the previous.
14
- def initialize(fifos)
15
- @fifos = fifos
16
- end
17
-
18
19
  def test_library(library, require_string = nil)
19
20
  child = ChildProcess.new(library, require_string, fifos)
20
21
  child.start_child
@@ -16,10 +16,7 @@ module GemFootprintAnalyzer
16
16
  # @return [Array<Hash>] Array of hashes that now include average metrics in place of fields
17
17
  # present in {AVERAGED_FIELDS}. The rest of the columns is copied from the first sample.
18
18
  def run
19
- results = []
20
- @runs.times do
21
- results << run_once
22
- end
19
+ results = Array.new(@runs) { run_once }
23
20
  calculate_averages(results)
24
21
  end
25
22
 
@@ -50,8 +47,8 @@ module GemFootprintAnalyzer
50
47
  sum = values.sum.to_f
51
48
  mean = sum / num
52
49
 
53
- stddev = Math.sqrt(values.sum { |v| (v - mean)**2 } / num)
54
- {mean: mean, sttdev: stddev}
50
+ stddev = Math.sqrt(values.sum { |v| (v - mean)**2 } / num).round(4)
51
+ {mean: mean, stddev: stddev}
55
52
  end
56
53
 
57
54
  def initialize_average_with_copied_fields(sample)
@@ -20,7 +20,9 @@ module GemFootprintAnalyzer
20
20
  begin
21
21
  require(require_string)
22
22
  rescue LoadError => e
23
+ warn_about_load_error(e)
23
24
  transport.exit_with_error(e)
25
+ exit 1
24
26
  end
25
27
  transport.done_and_wait_for_ack
26
28
  end
@@ -52,6 +54,16 @@ module GemFootprintAnalyzer
52
54
  STDOUT.puts(message)
53
55
  STDOUT.flush
54
56
  end
57
+
58
+ def warn_about_load_error(error)
59
+ possible_gem = error.message.split.last
60
+ STDERR.puts "Cannot load '#{possible_gem}', this might be an issue with implicit require in" \
61
+ " the '#{require_string}'"
62
+ STDERR.puts "If '#{possible_gem}' is a gem, you can try adding it to the Gemfile explicitly" \
63
+ ", running `bundle install` and trying again\n\n"
64
+ STDERR.puts error.backtrace
65
+ STDERR.flush
66
+ end
55
67
  end
56
68
  end
57
69
 
@@ -4,6 +4,7 @@ require 'rbconfig'
4
4
  module GemFootprintAnalyzer
5
5
  # A class for starting the child process that does actual requires.
6
6
  class ChildProcess
7
+ LEGACY_RUBY_CMD = [RbConfig.ruby, '--disable=gem'].freeze
7
8
  RUBY_CMD = [RbConfig.ruby, '--disable=did_you_mean', '--disable=gem'].freeze
8
9
 
9
10
  def initialize(library, require_string, fifos)
@@ -15,11 +16,11 @@ module GemFootprintAnalyzer
15
16
 
16
17
  def start_child
17
18
  @child_thread ||= Thread.new do # rubocop:disable Naming/MemoizedInstanceVariableName
18
- Open3.popen3(child_env_vars, *RUBY_CMD, context_file) do |_, stdout, stderr|
19
+ Open3.popen3(child_env_vars, *ruby_command, context_file) do |_, stdout, stderr|
19
20
  @pid = stdout.gets.strip.to_i
20
21
 
21
22
  while (line = stderr.gets)
22
- puts "!! #{line.strip}"
23
+ print "!! #{line}"
23
24
  end
24
25
  end
25
26
  end
@@ -29,7 +30,6 @@ module GemFootprintAnalyzer
29
30
  return unless child_thread
30
31
 
31
32
  sleep 0.01 while @pid.nil?
32
-
33
33
  @pid
34
34
  end
35
35
 
@@ -37,6 +37,14 @@ module GemFootprintAnalyzer
37
37
 
38
38
  attr_reader :require_string, :child_thread, :fifos
39
39
 
40
+ def ruby_command
41
+ if RbConfig::CONFIG['MAJOR'].to_i >= 2 && RbConfig::CONFIG['MINOR'].to_i >= 3
42
+ RUBY_CMD
43
+ else
44
+ LEGACY_RUBY_CMD
45
+ end
46
+ end
47
+
40
48
  def child_env_vars
41
49
  {
42
50
  'require_string' => require_string,
@@ -3,7 +3,7 @@ module GemFootprintAnalyzer
3
3
  # Provides File#mkfifo, missing in Ruby 2.2.0
4
4
  module File
5
5
  def mkfifo(name)
6
- system("mkfifo #{name}")
6
+ system("mkfifo #{name}") || fail('Failed to make FIFO special file')
7
7
  end
8
8
  end
9
9
  end
@@ -11,7 +11,8 @@ module GemFootprintAnalyzer
11
11
  # Displays explanatory words for text formatter results
12
12
  def info
13
13
  lines = []
14
- lines << "GemFootprintAnalyzer (#{GemFootprintAnalyzer::VERSION})\n"
14
+ lines << "GemFootprintAnalyzer (#{GemFootprintAnalyzer::VERSION})"
15
+ lines << (debug? ? "(#{File.expand_path(File.join(File.dirname(__FILE__), '..'))})\n" : '')
15
16
  lines << "Analyze results (average measured from #{@options[:runs]} run(s))"
16
17
  lines << 'time is the amount of time given require has taken to complete'
17
18
  lines << 'RSS is total memory increase up to the point after the require'
@@ -23,6 +24,12 @@ module GemFootprintAnalyzer
23
24
  def dash(length)
24
25
  '-' * length
25
26
  end
27
+
28
+ private
29
+
30
+ def debug?
31
+ @options[:debug]
32
+ end
26
33
  end
27
34
  end
28
35
  end
@@ -7,9 +7,9 @@ module GemFootprintAnalyzer
7
7
  INDENT = ' '.freeze
8
8
  # Formatter helper class representing a single results require entry.
9
9
  class Entry
10
- def initialize(entry_hash, _options = {})
10
+ def initialize(entry_hash, options = {})
11
11
  @entry_hash = entry_hash
12
- @options = {}
12
+ @options = options
13
13
  end
14
14
 
15
15
  def name
@@ -39,7 +39,7 @@ module GemFootprintAnalyzer
39
39
  def debug_parent
40
40
  return unless @options[:debug]
41
41
 
42
- "(#{parent})"
42
+ " (#{parent})"
43
43
  end
44
44
  end
45
45
 
@@ -53,12 +53,13 @@ module GemFootprintAnalyzer
53
53
  private
54
54
 
55
55
  def formatted_entries(entries)
56
- indent_levels, max_indent = count_indent_levels(entries)
57
- max_name_length = entries.map { |e| e.name.length }.max
56
+ max_indent = max_indent(entries)
57
+ max_name_length = entries.map { |e| e.formatted_name.length }.max
58
58
  ljust_value = max_name_length + max_indent + 1
59
59
 
60
60
  lines = entries.reverse.map do |entry|
61
- format_entry(entry, indent_levels, ljust_value)
61
+ indent_level = count_indent_level(entry)
62
+ format_entry(entry, indent_level, ljust_value)
62
63
  end
63
64
 
64
65
  (legend(ljust_value) + lines).join("\n")
@@ -71,8 +72,8 @@ module GemFootprintAnalyzer
71
72
  ]
72
73
  end
73
74
 
74
- def format_entry(entry, indent_levels, ljust_value)
75
- indent = INDENT * indent_levels[entry.name]
75
+ def format_entry(entry, indent_level, ljust_value)
76
+ indent = INDENT * indent_level
76
77
  time = format('%5dms', entry.time)
77
78
  rss = format('%7dKB', entry.rss)
78
79
 
@@ -85,6 +86,22 @@ module GemFootprintAnalyzer
85
86
  end
86
87
  end
87
88
 
89
+ def max_indent(entries)
90
+ entries.reverse_each { |entry| count_indent_level(entry) }
91
+ max_indent_level = @indent_levels.map(&:values).flatten.max
92
+ @indent_levels = []
93
+ max_indent_level * INDENT.size
94
+ end
95
+
96
+ def count_indent_level(entry)
97
+ @indent_levels ||= []
98
+ parent_hash = @indent_levels.find { |elem| elem.key?(entry.parent) }
99
+ parent_level = parent_hash ? parent_hash.values.first : -1
100
+ indent_level = parent_level + 1
101
+ @indent_levels.unshift(entry.name => indent_level)
102
+ indent_level
103
+ end
104
+
88
105
  def count_indent_levels(entries)
89
106
  root = entries.last
90
107
  indent_levels = {root.name => 0}
@@ -15,11 +15,15 @@ module GemFootprintAnalyzer
15
15
  full_path.sub(%r{\A#{load_path}/}, '')
16
16
  end
17
17
 
18
- def first_foreign_caller(caller)
19
- ffc = caller.find do |c|
20
- GemFootprintAnalyzer::RequireSpy.relative_path(c) !~ /gem_footprint_analyzer/
18
+ def without_extension(name)
19
+ name.sub(/\.rb\z/, '')
20
+ end
21
+
22
+ def first_foreign_caller(caller_list)
23
+ ffc = caller_list.find do |c|
24
+ relative_path(c) !~ /gem_footprint_analyzer/
21
25
  end
22
- GemFootprintAnalyzer::RequireSpy.relative_path(ffc).sub(/\.rb\z/, '') if ffc
26
+ without_extension(relative_path(ffc)) if ffc
23
27
  end
24
28
 
25
29
  def spy_require(transport)
@@ -37,9 +41,9 @@ module GemFootprintAnalyzer
37
41
 
38
42
  def define_timed_exec
39
43
  Kernel.send :define_method, :timed_exec do |&block|
40
- start_time = Time.now.to_f
44
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
41
45
  block.call
42
- (Time.now.to_f - start_time).round(4)
46
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time).round(4)
43
47
  end
44
48
  end
45
49
 
@@ -53,7 +57,8 @@ module GemFootprintAnalyzer
53
57
  t = timed_exec { result = regular_require(name) }
54
58
 
55
59
  first_foreign_caller = GemFootprintAnalyzer::RequireSpy.first_foreign_caller(caller)
56
- transport.report_require(name, first_foreign_caller || '', t)
60
+ bare_name = GemFootprintAnalyzer::RequireSpy.without_extension(name)
61
+ transport.report_require(bare_name, first_foreign_caller || '', t)
57
62
  result
58
63
  end
59
64
  end
@@ -9,7 +9,7 @@ module GemFootprintAnalyzer
9
9
  @write_stream = write_stream
10
10
  end
11
11
 
12
- # @return [Array] A tuple with command and *payload
12
+ # @return [Array|nil] A tuple with command and *payload or nil when command not recognized
13
13
  def read_one_command
14
14
  case read_raw_command
15
15
  when /\A(done|ack|start|ready)\z/
@@ -1,3 +1,3 @@
1
1
  module GemFootprintAnalyzer
2
- VERSION = '0.1.5'.freeze
2
+ VERSION = '0.1.6'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_footprint_analyzer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciek Dubiński
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-03 00:00:00.000000000 Z
11
+ date: 2018-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: rubocop-rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '1.30'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: '1.30'
83
83
  description:
84
84
  email:
85
85
  - maciek@dubinski.net
@@ -88,11 +88,6 @@ executables:
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
- - ".gitignore"
92
- - ".rspec"
93
- - ".rubocop"
94
- - ".rubocop.yml"
95
- - ".travis.yml"
96
91
  - CODE_OF_CONDUCT.md
97
92
  - Gemfile
98
93
  - Gemfile.lock
@@ -128,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
123
  requirements:
129
124
  - - ">="
130
125
  - !ruby/object:Gem::Version
131
- version: '0'
126
+ version: 2.2.0
132
127
  required_rubygems_version: !ruby/object:Gem::Requirement
133
128
  requirements:
134
129
  - - ">="
data/.gitignore DELETED
@@ -1,11 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/.rubocop DELETED
File without changes
@@ -1,22 +0,0 @@
1
- require: rubocop-rspec
2
- AllCops:
3
- Include:
4
- - 'lib/gem_footprint_analyzer/**/*.rb'
5
- - 'exe/*'
6
- - 'spec/**/*'
7
- TargetRubyVersion: 2.2
8
-
9
- Metrics/LineLength:
10
- Max: 100
11
-
12
- Style/RegexpLiteral:
13
- EnforcedStyle: slashes
14
-
15
- Layout/SpaceInsideHashLiteralBraces:
16
- EnforcedStyle: no_space
17
-
18
- Style/SignalException:
19
- EnforcedStyle: semantic
20
-
21
- RSpec/NestedGroups:
22
- Max: 5
@@ -1,14 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 2.5.3
7
- - 2.2
8
- before_install:
9
- - gem install bundler -v 1.17.1
10
- script:
11
- - bundle exec rubocop
12
- - bundle exec rspec
13
- - bundle exec analyze_requires -d rubocop
14
- - bundle exec analyze_requires net/http