appmap 0.54.0 → 0.55.0

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: a00ec7ef6ec83aebf28d11c8004c89885ae32562ef6384c6c7880ca09d380027
4
- data.tar.gz: a16129c01467f043eb18786ea8f3cae2df65226b924e6f85b40614f7d563e8a1
3
+ metadata.gz: dfd518bd44a86f1270fe90e70c397e213763dd582260ace7c5a01898b94030d2
4
+ data.tar.gz: cf449bff5700a1bfd5efabda40ccd59c5281d595aa34ca551d6a3b4b1f89f5f8
5
5
  SHA512:
6
- metadata.gz: daeec49a1a7e46fa5d5f572755725d766a2c37e00f8013d93ac1cde6d7dcff504633b4ca74cbbc94c9a3d355bc221d795970e0af393be29557f03d8fbe06114a
7
- data.tar.gz: f1c8b85f2e9224604f3138c208dbcff0dbb939aa1038834a8ca761b290b5f07086023857881dd4c14c37644a9ae403108ba059c4a337796ea7ecab0e6b2826bb
6
+ metadata.gz: 1824e49817ccceca6571fb2cb6f4a3033fb4873bb184577ec3989af3d74d09aecbf7685f9ce9f7efeb2a43e811f9477b0cfc5c6f6778ba3ec8d141c37560112c
7
+ data.tar.gz: da796d7072dab9b505f6fe1220ae3987e0577be458dfb1253b3d4b860df41d7a4856640629dd300ebcb6588f350f4f55af2b867bf730d0a65e90788c0f2eeb0d
data/.travis.yml CHANGED
@@ -1,5 +1,10 @@
1
1
  language: ruby
2
2
 
3
+ rbenv:
4
+ - 2.5
5
+ - 2.6
6
+ - 2.7
7
+
3
8
  addons:
4
9
  apt:
5
10
  packages:
@@ -29,28 +34,29 @@ before_install:
29
34
  - |
30
35
  nvm install --lts \
31
36
  && nvm use --lts
32
-
37
+
33
38
  # GEM_ALTERNATIVE_NAME only needed for deployment
34
39
  jobs:
35
40
  include:
36
41
  - stage: test
37
42
  script:
43
+ - set -e
38
44
  - mkdir tmp
39
- - GEM_ALTERNATIVE_NAME='' bundle exec rake test
45
+ - npm i -g yarn
46
+ - GEM_ALTERNATIVE_NAME='' bundle exec rake gem:build test
40
47
 
41
-
42
48
  before_deploy:
43
49
  - |
44
- nvm install --lts \
45
- && nvm use --lts \
46
- && npm i -g \
47
- semantic-release \
48
- @semantic-release/git \
49
- @semantic-release/changelog \
50
- semantic-release-rubygem
50
+ npm i -g \
51
+ yarn \
52
+ semantic-release \
53
+ @semantic-release/git \
54
+ @semantic-release/changelog \
55
+ semantic-release-rubygem
51
56
 
52
57
  deploy:
53
58
  - provider: script
54
59
  script: ./release.sh
55
60
  on:
56
61
  branch: master
62
+ condition: "$TRAVIS_RUBY_VERSION = 2.7"
data/CHANGELOG.md CHANGED
@@ -1,3 +1,48 @@
1
+ # [0.55.0](https://github.com/applandinc/appmap-ruby/compare/v0.54.4...v0.55.0) (2021-06-28)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Avoid calling == ([f30ed9f](https://github.com/applandinc/appmap-ruby/commit/f30ed9f309753252df35e372d925db3b914260d4))
7
+ * Log dynamic loading of appmap helpers at info level ([15dcd3c](https://github.com/applandinc/appmap-ruby/commit/15dcd3c913fa1c32aea034b28ddae59668efa217))
8
+ * Remove dynamic loading of rake and rspec helpers ([6790970](https://github.com/applandinc/appmap-ruby/commit/67909702f3c8a52081ef1e23a87c292908883334))
9
+
10
+
11
+ ### Features
12
+
13
+ * APPMAP_PROFILE_DISPLAY_STRING and APPMAP_OBJECT_STRING ([3f5daa8](https://github.com/applandinc/appmap-ruby/commit/3f5daa890bfbfd39b7f825794d0c43da509b3201))
14
+ * Package name to require can be specified when hooking a gem ([fcc5eb6](https://github.com/applandinc/appmap-ruby/commit/fcc5eb691a0330444560eb4c2afe7fc3c4c8afa8))
15
+ * Profile packaging hooking ([c020a31](https://github.com/applandinc/appmap-ruby/commit/c020a312f4545348ec7cc302443269c57a7fc026))
16
+
17
+ ## [0.54.4](https://github.com/applandinc/appmap-ruby/compare/v0.54.3...v0.54.4) (2021-06-27)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * Only allow trace_end once per location ([10e48cf](https://github.com/applandinc/appmap-ruby/commit/10e48cf855907f9029479b4b7b63bc4d25d664ab))
23
+
24
+ ## [0.54.3](https://github.com/applandinc/appmap-ruby/compare/v0.54.2...v0.54.3) (2021-06-25)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * Get deployment working with packaging of NodeJS code ([733c5b8](https://github.com/applandinc/appmap-ruby/commit/733c5b85ec1a0c17ada81be524fa572f78f52500))
30
+
31
+ ## [0.54.2](https://github.com/applandinc/appmap-ruby/compare/v0.54.1...v0.54.2) (2021-06-25)
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * Require appmap/railtie if Rails is defined ([66b4cbd](https://github.com/applandinc/appmap-ruby/commit/66b4cbd4d418695b0e69150d253dfd5a6f9096cf))
37
+
38
+ ## [0.54.1](https://github.com/applandinc/appmap-ruby/compare/v0.54.0...v0.54.1) (2021-06-25)
39
+
40
+
41
+ ### Bug Fixes
42
+
43
+ * Add missing imports and remove deprecation warnings ([f1cb087](https://github.com/applandinc/appmap-ruby/commit/f1cb087f80cad88093227ebf8b4a4cd574853667))
44
+ * Workaround Ruby bug in 2.7.3 with kwrest ([26e34ca](https://github.com/applandinc/appmap-ruby/commit/26e34ca421fdae6602b27fee5653c8fe26b3793b))
45
+
1
46
  # [0.54.0](https://github.com/applandinc/appmap-ruby/compare/v0.53.0...v0.54.0) (2021-06-24)
2
47
 
3
48
 
data/Rakefile CHANGED
@@ -6,8 +6,7 @@ require 'rake/testtask'
6
6
  require 'rdoc/task'
7
7
 
8
8
  require 'open3'
9
-
10
- require "rake/extensiontask"
9
+ require 'rake/extensiontask'
11
10
 
12
11
  desc 'build the native extension'
13
12
  Rake::ExtensionTask.new("appmap") do |ext|
@@ -26,7 +25,7 @@ namespace 'gem' do
26
25
  # ~/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/bundler-2.1.4/lib/bundler/gem_helper.rb:39:in `install'
27
26
  def build_gem
28
27
  # Ensure that NPM packages are installed before building.
29
- sh('yarn install --prod'.shellsplit)
28
+ sh('yarn install --prod')
30
29
 
31
30
  default_build_gem
32
31
  end
@@ -34,7 +33,17 @@ namespace 'gem' do
34
33
  end
35
34
  end
36
35
 
37
- RUBY_VERSIONS=%w[2.5 2.6 2.7]
36
+ RUBY_VERSIONS=%w[2.5 2.6 2.7].select do |version|
37
+ travis_ruby_version = ENV['TRAVIS_RUBY_VERSION']
38
+ next true unless travis_ruby_version
39
+
40
+ if travis_ruby_version.index(version) == 0
41
+ warn "Testing Ruby version #{version}, since it matches TRAVIS_RUBY_VERSION=#{travis_ruby_version}"
42
+ next true
43
+ end
44
+
45
+ false
46
+ end
38
47
  FIXTURE_APPS=%w[rack_users_app rails6_users_app rails5_users_app]
39
48
 
40
49
  def run_cmd(*cmd)
data/appmap.gemspec CHANGED
@@ -54,4 +54,5 @@ Gem::Specification.new do |spec|
54
54
  spec.add_development_dependency 'webdrivers', '~> 4.0'
55
55
  spec.add_development_dependency 'timecop'
56
56
  spec.add_development_dependency 'hashie'
57
+ spec.add_development_dependency 'webrick'
57
58
  end
data/lib/appmap.rb CHANGED
@@ -26,12 +26,11 @@ lambda do
26
26
  Initializer = Struct.new(:class_name, :module_name, :gem_module_name)
27
27
 
28
28
  INITIALIZERS = {
29
- 'Rails::Railtie' => Initializer.new('AppMap::Railtie', 'appmap/railtie', 'railtie'),
30
- 'RSpec' => Initializer.new('AppMap::RSpec', 'appmap/rspec', 'rspec-core'),
29
+ # In a Rails app, Rails is always defined by the time the other gems are loaded. Therefore, we
30
+ # don't try and trap the loading of Rails itself here.
31
+ # Emperically, Rake and RSpec are also defined before appmap is loaded whenever a Rake task or
32
+ # RSpec tests are being run. Therefore, the only hook we need here is Minitest.
31
33
  'Minitest::Unit::TestCase' => Initializer.new('AppMap::Minitest', 'appmap/minitest', 'minitest'),
32
- 'Rake' => [
33
- Initializer.new('AppMap::Swagger', 'appmap/swagger', 'rake')
34
- ]
35
34
  }
36
35
 
37
36
  TracePoint.new(:class) do |tp|
@@ -57,7 +56,7 @@ lambda do
57
56
  end
58
57
  end.enable
59
58
 
60
- if defined?(::Rails::Railtie)
59
+ if defined?(::Rails)
61
60
  require 'appmap/railtie'
62
61
  end
63
62
 
@@ -68,7 +67,7 @@ lambda do
68
67
  if defined?(::Minitest)
69
68
  require 'appmap/minitest'
70
69
  end
71
-
70
+
72
71
  if defined?(::Rake)
73
72
  require 'appmap/swagger'
74
73
  end
data/lib/appmap/config.rb CHANGED
@@ -343,7 +343,7 @@ module AppMap
343
343
  shallow = package['shallow']
344
344
  # shallow is true by default for gems
345
345
  shallow = true if shallow.nil?
346
- Package.build_from_gem(gem, exclude: package['exclude'] || [], shallow: shallow)
346
+ Package.build_from_gem(gem, package_name: package['package'], exclude: package['exclude'] || [], shallow: shallow)
347
347
  else
348
348
  Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
349
349
  end
data/lib/appmap/event.rb CHANGED
@@ -29,11 +29,31 @@ module AppMap
29
29
 
30
30
  # Gets a display string for a value. This is not meant to be a machine deserializable value.
31
31
  def display_string(value)
32
- return nil unless value
32
+ return nil if value.equal?(nil)
33
33
 
34
+ # With setting APPMAP_PROFILE_DISPLAY_STRING, stringifying this class is shown to take 9 seconds(!) of a 17 second test run.
35
+ return nil if best_class_name(value) == 'ActiveSupport::Callbacks::Filters::Environment'
36
+
37
+ if @times.nil? && ENV['APPMAP_PROFILE_DISPLAY_STRING'] == 'true'
38
+ @times = Hash.new {|memo,key| memo[key] = 0}
39
+ Thread.new do
40
+ sleep 0.5
41
+ while true
42
+ warn @times.to_a.sort{|a,b| b[1] <=> a[1]}[0..9].join("\n")
43
+ sleep 3
44
+ end
45
+ end
46
+ end
47
+
48
+ start = Time.now
34
49
  value_string = custom_display_string(value) || default_display_string(value)
35
50
 
36
- (value_string||'')[0...LIMIT].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
51
+ if @times
52
+ elapsed = Time.now - start
53
+ @times[best_class_name(value)] += elapsed
54
+ end
55
+
56
+ encode_dislay_string(value_string)
37
57
  end
38
58
 
39
59
  def object_properties(hash_like)
@@ -57,8 +77,16 @@ module AppMap
57
77
  value_cls.name
58
78
  end
59
79
 
80
+ def encode_dislay_string(value)
81
+ (value||'')[0...LIMIT].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
82
+ end
83
+
60
84
  def custom_display_string(value)
61
85
  case value
86
+ when NilClass, TrueClass, FalseClass, Numeric, Time, Date
87
+ value.to_s
88
+ when String
89
+ value[0...LIMIT].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
62
90
  when File
63
91
  "#{value.class}[path=#{value.path}]"
64
92
  when Net::HTTP
@@ -71,6 +99,8 @@ module AppMap
71
99
  end
72
100
 
73
101
  def default_display_string(value)
102
+ return nil if ENV['APPMAP_OBJECT_STRING'] == 'false'
103
+
74
104
  last_resort_string = lambda do
75
105
  warn "AppMap encountered an error inspecting a #{value.class.name}: #{$!.message}"
76
106
  '*Error inspecting variable*'
@@ -82,7 +82,7 @@ module AppMap
82
82
  def request_headers(request)
83
83
  {}.tap do |headers|
84
84
  request.each_header do |k,v|
85
- key = [ 'HTTP', k.underscore.upcase ].join('_')
85
+ key = [ 'HTTP', Util.underscore(k).upcase ].join('_')
86
86
  headers[key] = v
87
87
  end
88
88
  end
@@ -146,7 +146,8 @@ module AppMap
146
146
  class RenderHandler
147
147
  class << self
148
148
  def handle_call(defined_class, hook_method, receiver, args)
149
- context, options = args
149
+ # context, options
150
+ _, options = args
150
151
 
151
152
  warn "Renderer: #{options}" if LOG
152
153
 
data/lib/appmap/hook.rb CHANGED
@@ -9,6 +9,7 @@ module AppMap
9
9
 
10
10
  OBJECT_INSTANCE_METHODS = %i[! != !~ <=> == === =~ __id__ __send__ class clone define_singleton_method display dup enum_for eql? equal? extend freeze frozen? hash inspect instance_eval instance_exec instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables is_a? itself kind_of? method methods nil? object_id private_methods protected_methods public_method public_methods public_send remove_instance_variable respond_to? send singleton_class singleton_method singleton_methods taint tainted? tap then to_enum to_s to_h to_a trust untaint untrust untrusted? yield_self].freeze
11
11
  OBJECT_STATIC_METHODS = %i[! != !~ < <= <=> == === =~ > >= __id__ __send__ alias_method allocate ancestors attr attr_accessor attr_reader attr_writer autoload autoload? class class_eval class_exec class_variable_defined? class_variable_get class_variable_set class_variables clone const_defined? const_get const_missing const_set constants define_method define_singleton_method deprecate_constant display dup enum_for eql? equal? extend freeze frozen? hash include include? included_modules inspect instance_eval instance_exec instance_method instance_methods instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables is_a? itself kind_of? method method_defined? methods module_eval module_exec name new nil? object_id prepend private_class_method private_constant private_instance_methods private_method_defined? private_methods protected_instance_methods protected_method_defined? protected_methods public_class_method public_constant public_instance_method public_instance_methods public_method public_method_defined? public_methods public_send remove_class_variable remove_instance_variable remove_method respond_to? send singleton_class singleton_class? singleton_method singleton_methods superclass taint tainted? tap then to_enum to_s trust undef_method untaint untrust untrusted? yield_self].freeze
12
+ SLOW_PACKAGE_THRESHOLD = 0.05
12
13
 
13
14
  @unbound_method_arity = ::UnboundMethod.instance_method(:arity)
14
15
  @method_arity = ::Method.instance_method(:arity)
@@ -37,8 +38,6 @@ module AppMap
37
38
  def initialize(config)
38
39
  @config = config
39
40
  @trace_enabled = []
40
- # Paths that are known to be non-tracing
41
- @notrace_paths = Set.new
42
41
  end
43
42
 
44
43
  # Observe class loading and hook all methods which match the config.
@@ -47,6 +46,32 @@ module AppMap
47
46
 
48
47
  hook_builtins
49
48
 
49
+ # Paths that are known to be non-tracing.
50
+ @notrace_paths = Set.new
51
+ # Locations that have already been visited.
52
+ @trace_locations = Set.new
53
+ @module_load_times = Hash.new {|memo,k| memo[k] = 0}
54
+ @slow_packages = Set.new
55
+
56
+ if ENV['APPMAP_PROFILE_HOOK'] == 'true'
57
+ Thread.new do
58
+ sleep 1
59
+ while true
60
+ @module_load_times
61
+ .keys
62
+ .select { |key| !@slow_packages.member?(key) }
63
+ .each do |key|
64
+ elapsed = @module_load_times[key]
65
+ if elapsed >= SLOW_PACKAGE_THRESHOLD
66
+ @slow_packages.add(key)
67
+ warn "AppMap: Package #{key} took #{@module_load_times[key]} seconds to hook"
68
+ end
69
+ end
70
+ sleep 5
71
+ end
72
+ end
73
+ end
74
+
50
75
  @trace_end = TracePoint.new(:end, &method(:trace_end))
51
76
  @trace_end.enable(&block)
52
77
  end
@@ -100,7 +125,8 @@ module AppMap
100
125
 
101
126
  def trace_end(trace_point)
102
127
  location = trace_location(trace_point)
103
- warn "Class or module ends at location #{trace_location(trace_point)}" if Hook::LOG || Hook::LOG_HOOK
128
+ warn "Class or module ends at location #{location}" if Hook::LOG || Hook::LOG_HOOK
129
+ return unless @trace_locations.add?(location)
104
130
 
105
131
  path = trace_point.path
106
132
  enabled = !@notrace_paths.member?(path) && config.path_enabled?(path)
@@ -153,6 +179,7 @@ module AppMap
153
179
  end
154
180
  end
155
181
 
182
+ start = Time.now
156
183
  instance_methods.each(&hook.(cls))
157
184
  begin
158
185
  # NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
@@ -162,6 +189,11 @@ module AppMap
162
189
  # uninitialized constant Faraday::Connection
163
190
  warn "NameError in #{__FILE__}: #{$!.message}"
164
191
  end
192
+ elapsed = Time.now - start
193
+ if location.index(Bundler.bundle_path.to_s) == 0
194
+ package_tokens = location[Bundler.bundle_path.to_s.length + 1..-1].split('/')
195
+ @module_load_times[package_tokens[1]] += elapsed
196
+ end
165
197
  end
166
198
  end
167
199
  end
@@ -18,6 +18,8 @@ module AppMap
18
18
  TIME_NOW = Time.method(:now)
19
19
  private_constant :TIME_NOW
20
20
 
21
+ ARRAY_OF_EMPTY_HASH = [{}.freeze].freeze
22
+
21
23
  def initialize(hook_package, hook_class, hook_method)
22
24
  @hook_package = hook_package
23
25
  @hook_class = hook_class
@@ -45,42 +47,53 @@ module AppMap
45
47
  after_hook = self.method(:after_hook)
46
48
  with_disabled_hook = self.method(:with_disabled_hook)
47
49
 
48
- hook_method_def = nil
49
- hook_class.instance_eval do
50
- hook_method_def = Proc.new do |*args, &block|
51
- instance_method = hook_method.bind(self).to_proc
52
- call_instance_method = -> { instance_method.call(*args, &block) }
50
+ hook_method_def = Proc.new do |*args, &block|
51
+ instance_method = hook_method.bind(self).to_proc
52
+
53
+ is_array_containing_empty_hash = ->(obj) {
54
+ obj.is_a?(Array) && obj.length == 1 && obj[0].is_a?(Hash) && obj[0].size == 0
55
+ }
53
56
 
54
- # We may not have gotten the class for the method during
55
- # initialization (e.g. for a singleton method on an embedded
56
- # struct), so make sure we have it now.
57
- defined_class, = Hook.qualify_method_name(hook_method) unless defined_class
57
+ call_instance_method = -> {
58
+ # https://github.com/applandinc/appmap-ruby/issues/153
59
+ if Util.ruby_minor_version >= 2.7 && is_array_containing_empty_hash.(args) && hook_method.arity == 1
60
+ instance_method.call({}, &block)
61
+ else
62
+ instance_method.call(*args, &block)
63
+ end
64
+ }
58
65
 
59
- reentrant = Thread.current[HOOK_DISABLE_KEY]
60
- disabled_by_shallow_flag = \
61
- -> { hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package }
66
+ # We may not have gotten the class for the method during
67
+ # initialization (e.g. for a singleton method on an embedded
68
+ # struct), so make sure we have it now.
69
+ defined_class, = Hook.qualify_method_name(hook_method) unless defined_class
62
70
 
63
- enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
71
+ reentrant = Thread.current[HOOK_DISABLE_KEY]
72
+ disabled_by_shallow_flag = \
73
+ -> { hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package }
64
74
 
65
- return call_instance_method.call unless enabled
75
+ enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
66
76
 
67
- call_event, start_time = with_disabled_hook.call do
68
- before_hook.call(self, defined_class, args)
69
- end
70
- return_value = nil
71
- exception = nil
72
- begin
73
- return_value = call_instance_method.call
74
- rescue
75
- exception = $ERROR_INFO
76
- raise
77
- ensure
78
- with_disabled_hook.call do
79
- after_hook.call(self, call_event, start_time, return_value, exception) if call_event
80
- end
77
+ return call_instance_method.call unless enabled
78
+
79
+ call_event, start_time = with_disabled_hook.call do
80
+ before_hook.call(self, defined_class, args)
81
+ end
82
+ return_value = nil
83
+ exception = nil
84
+ begin
85
+ return_value = call_instance_method.call
86
+ rescue
87
+ exception = $ERROR_INFO
88
+ raise
89
+ ensure
90
+ with_disabled_hook.call do
91
+ after_hook.call(self, call_event, start_time, return_value, exception) if call_event
81
92
  end
82
93
  end
83
94
  end
95
+ hook_method_def = hook_method_def.ruby2_keywords if hook_method_def.respond_to?(:ruby2_keywords)
96
+
84
97
  hook_class.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def)
85
98
  end
86
99
 
data/lib/appmap/trace.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'delegate'
4
+
3
5
  module AppMap
4
6
  module Trace
5
7
  class RubyMethod < SimpleDelegator
data/lib/appmap/util.rb CHANGED
@@ -124,7 +124,7 @@ module AppMap
124
124
  path = path.split('(.')[0]
125
125
  tokens = path.split('/')
126
126
  tokens.map do |token|
127
- token.gsub /^:(.*)/, '{\1}'
127
+ token.gsub(/^:(.*)/, '{\1}')
128
128
  end.join('/')
129
129
  end
130
130
 
@@ -154,6 +154,18 @@ module AppMap
154
154
  word.split(/[\-_]/).map(&:capitalize).join
155
155
  end
156
156
 
157
+ # https://api.rubyonrails.org/v6.1.3.2/classes/ActiveSupport/Inflector.html#method-i-underscore
158
+ def underscore(camel_cased_word)
159
+ return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
160
+ word = camel_cased_word.to_s.gsub("::", "/")
161
+ # word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
162
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
163
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
164
+ word.tr!("-", "_")
165
+ word.downcase!
166
+ word
167
+ end
168
+
157
169
  def deep_dup(hash)
158
170
  # This is a simple way to avoid the need for deep_dup from activesupport.
159
171
  Marshal.load(Marshal.dump(hash))
@@ -173,11 +185,15 @@ module AppMap
173
185
 
174
186
  def startup_message(msg)
175
187
  if defined?(::Rails) && defined?(::Rails.logger) && ::Rails.logger
176
- ::Rails.logger.debug msg
188
+ ::Rails.logger.info msg
177
189
  elsif ENV['DEBUG'] == 'true'
178
190
  warn msg
179
191
  end
180
192
  end
193
+
194
+ def ruby_minor_version
195
+ @ruby_minor_version ||= RUBY_VERSION.split('.')[0..1].join('.').to_f
196
+ end
181
197
  end
182
198
  end
183
199
  end
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.54.0'
6
+ VERSION = '0.55.0'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.5.1'
9
9
 
data/release.sh CHANGED
@@ -14,4 +14,3 @@ fi
14
14
 
15
15
  set -x
16
16
  exec semantic-release $RELEASE_FLAGS
17
-
@@ -0,0 +1,11 @@
1
+ class Kwargs
2
+ class << self
3
+ def no_kwargs(args)
4
+ args
5
+ end
6
+
7
+ def has_kwrest_calls_no_kwargs(args, **kwargs)
8
+ no_kwargs(**kwargs)
9
+ end
10
+ end
11
+ end
data/spec/hook_spec.rb CHANGED
@@ -970,7 +970,7 @@ describe 'AppMap class Hooking', docker: false do
970
970
  tz = ENV['TZ']
971
971
  ENV['TZ'] = 'UTC'
972
972
  Timecop.freeze(Time.utc('2020-01-01')) do
973
- expect(Time).to receive(:now).exactly(3).times.and_call_original
973
+ expect(Time).to receive(:now).at_least(3).times.and_call_original
974
974
  expect(InstanceMethod.new.say_the_time).to be
975
975
  end
976
976
  ensure
@@ -985,4 +985,13 @@ describe 'AppMap class Hooking', docker: false do
985
985
  expect(InstanceMethod.new.method(:say_echo).arity).to be(1)
986
986
  end
987
987
  end
988
+
989
+ describe 'kwargs handling' do
990
+ # https://github.com/applandinc/appmap-ruby/issues/153
991
+ it 'empty hash for **kwrest can be proxied as a regular function argument', github_issue: 153 do
992
+ invoke_test_file 'spec/fixtures/hook/kwargs.rb' do
993
+ expect(Kwargs.has_kwrest_calls_no_kwargs(nil, {})).to eq({})
994
+ end
995
+ end
996
+ end
988
997
  end
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'diffy'
3
3
  require 'rack'
4
+ require 'webrick'
4
5
  require 'rack/handler/webrick'
5
6
 
6
7
  class HelloWorldApp
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.54.0
4
+ version: 0.55.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Gilpin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-24 00:00:00.000000000 Z
11
+ date: 2021-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -276,6 +276,20 @@ dependencies:
276
276
  - - ">="
277
277
  - !ruby/object:Gem::Version
278
278
  version: '0'
279
+ - !ruby/object:Gem::Dependency
280
+ name: webrick
281
+ requirement: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - ">="
284
+ - !ruby/object:Gem::Version
285
+ version: '0'
286
+ type: :development
287
+ prerelease: false
288
+ version_requirements: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - ">="
291
+ - !ruby/object:Gem::Version
292
+ version: '0'
279
293
  description:
280
294
  email:
281
295
  - kgilpin@gmail.com
@@ -364,6 +378,7 @@ files:
364
378
  - spec/fixtures/hook/exception_method.rb
365
379
  - spec/fixtures/hook/exclude.rb
366
380
  - spec/fixtures/hook/instance_method.rb
381
+ - spec/fixtures/hook/kwargs.rb
367
382
  - spec/fixtures/hook/labels.rb
368
383
  - spec/fixtures/hook/method_named_call.rb
369
384
  - spec/fixtures/hook/revoke_api_key.appmap.json