appmap 0.54.0 → 0.55.0

Sign up to get free protection for your applications and to get access to all the features.
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