appmap 0.36.0 → 0.37.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: 102dab80f2d8cffd51fd1377583a1b063078cd77ab7ad4ffdcd20867a084b11b
4
- data.tar.gz: cbb87144593416f84099cf8a9d8ac18f18d9763dce28277320ae6a99ff59ec58
3
+ metadata.gz: fba24ecdb42c951741292378419eef430a56cc16816df46a14b2ae3e318f8f94
4
+ data.tar.gz: 67ff27467f72322c5a8514c90b0a6039e3fd1268dba103dea3b832eda95ac317
5
5
  SHA512:
6
- metadata.gz: a24e2ecb22a654d11e91ef28a7fdd961c178144f474a36e26c70e8e243bac18c13e3c5ff606f5ed2a058ddf6d98f9dbd0678a0f986ddfae4f18ed7524c27d41d
7
- data.tar.gz: 712cb7e18026aa80135af7ce54a7a8c412362355c0eb6b151a0f05354cf991ebe9402e298d9646b9fab2e5d673d3338a773a0b1b1d0e1846ec3ed970074a1f77
6
+ metadata.gz: 1823304c2733b46470481fd731a1f4650a80e769d96b47dbcdb0e5763e59930d6754068b4cf0729f9f748c55109575f3c8146b09030741fccfafd06cd45669b9
7
+ data.tar.gz: bbebe31041ed1720dc54b9522e4c6baac3b27d8d77be0622315657d0fa5b355c3e12474aa756811a6d747b563999c6e76206805c1392a015f92c6b863e8eeda0
data/.gitignore CHANGED
@@ -14,4 +14,5 @@ Gemfile.lock
14
14
  appmap.json
15
15
  .vscode
16
16
  .byebug_history
17
- /lib/appmap/appmap.bundle
17
+ /lib/appmap/appmap.bundle
18
+ *.so
@@ -1,3 +1,6 @@
1
+ AllCops:
2
+ NewCops: enable
3
+
1
4
  Layout/SpaceInsideArrayLiteralBrackets:
2
5
  Enabled: false
3
6
 
@@ -1,3 +1,6 @@
1
+ # v0.37.0
2
+ * Capture method source and comment.
3
+
1
4
  # v0.36.0
2
5
  * *appmap.yml* package definition may specify `gem`.
3
6
  * Skip loading the railtie if `APPMAP_INITIALIZE` environment variable
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  - [About](#about)
2
3
  - [Installation](#installation)
3
4
  - [Configuration](#configuration)
@@ -13,7 +14,7 @@
13
14
  - [Using fixture apps](#using-fixture-apps)
14
15
  - [`test/fixtures`](#testfixtures)
15
16
  - [`spec/fixtures`](#specfixtures)
16
- - [Build status](#build-status)
17
+
17
18
 
18
19
  # About
19
20
 
@@ -27,8 +28,9 @@ granular than a full debug trace. It's designed to be optimal for understanding
27
28
  There are several ways to record AppMaps of your Ruby program using the `appmap` gem:
28
29
 
29
30
  * Run your RSpec tests with the environment variable `APPMAP=true`. An AppMap will be generated for each spec.
30
- * Run your application server with AppMap remote recording enabled, and use the AppMap.
31
- browser extension to start, stop, and upload recordings.
31
+ * Run your application server with AppMap remote recording enabled, and use the [AppLand
32
+ browser extension](https://github.com/applandinc/appland-browser-extension) to start,
33
+ stop, and upload recordings.
32
34
  * Run the command `appmap record <program>` to record the entire execution of a program.
33
35
 
34
36
  Once you have recorded some AppMaps (for example, by running RSpec tests), you use the `appland upload` command
@@ -250,11 +252,11 @@ end
250
252
  $ bundle exec rails server
251
253
  ```
252
254
 
253
- 4. Open the AppApp browser extension and push `Start`.
255
+ 4. Open the AppLand browser extension and push `Start`.
254
256
 
255
257
  5. Use your app. For example, perform a login flow, or run through a manual UI test.
256
258
 
257
- 6. Open the AppApp browser extension and push `Stop`. The recording will be transferred to the AppLand website and opened in your browser.
259
+ 6. Open the AppLand browser extension and push `Stop`. The recording will be transferred to the AppLand website and opened in your browser.
258
260
 
259
261
  ## Ruby on Rails
260
262
 
@@ -267,6 +269,7 @@ Note that using this method is kind of a blunt instrument. Recording RSpecs and
267
269
  For instructions on uploading, see the documentation of the [AppLand CLI](https://github.com/applandinc/appland-cli).
268
270
 
269
271
  # Development
272
+ [![Build Status](https://travis-ci.org/applandinc/appmap-ruby.svg?branch=master)](https://travis-ci.org/applandinc/appmap-ruby)
270
273
 
271
274
  ## Running tests
272
275
 
@@ -291,7 +294,7 @@ $ bundle exec rake compile
291
294
  ### `test/fixtures`
292
295
 
293
296
  The fixture apps in `test/fixtures` are plain Ruby projects that exercise the basic functionality of the
294
- `appmap` gem. To develop in a fixture, simple enter the fixture directory and `bundle`.
297
+ `appmap` gem. To develop in a fixture, simply enter the fixture directory and `bundle`.
295
298
 
296
299
  ### `spec/fixtures`
297
300
 
@@ -341,5 +344,3 @@ Finished in 0.07357 seconds (files took 2.1 seconds to load)
341
344
  4 examples, 0 failures
342
345
  ```
343
346
 
344
- # Build status
345
- [![Build Status](https://travis-ci.org/applandinc/appmap-ruby.svg?branch=master)](https://travis-ci.org/applandinc/appmap-ruby)
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ $: << File.join(__dir__, 'lib')
1
2
  require 'appmap/version'
2
3
  GEM_VERSION = AppMap::VERSION
3
4
 
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency 'activesupport'
28
28
  spec.add_dependency 'faraday'
29
29
  spec.add_dependency 'gli'
30
+ spec.add_dependency 'method_source'
30
31
  spec.add_dependency 'parser'
31
32
  spec.add_dependency 'rack'
32
33
 
@@ -83,8 +83,8 @@ module AppMap
83
83
  end
84
84
 
85
85
  # Builds a class map from a config and a list of Ruby methods.
86
- def class_map(methods)
87
- ClassMap.build_from_methods(methods)
86
+ def class_map(methods, options = {})
87
+ ClassMap.build_from_methods(methods, options)
88
88
  end
89
89
 
90
90
  # Returns default metadata detected from the Ruby system and from the
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'method_source'
4
+
3
5
  module AppMap
4
6
  class ClassMap
5
7
  module HasChildren
@@ -48,7 +50,7 @@ module AppMap
48
50
  end
49
51
  end
50
52
  Function = Struct.new(:name) do
51
- attr_accessor :static, :location, :labels
53
+ attr_accessor :static, :location, :labels, :comment, :source
52
54
 
53
55
  def type
54
56
  'function'
@@ -60,24 +62,26 @@ module AppMap
60
62
  type: type,
61
63
  location: location,
62
64
  static: static,
63
- labels: labels
65
+ labels: labels,
66
+ comment: comment,
67
+ source: source
64
68
  }.delete_if { |_, v| v.nil? || v == [] }
65
69
  end
66
70
  end
67
71
  end
68
72
 
69
73
  class << self
70
- def build_from_methods(methods)
74
+ def build_from_methods(methods, options = {})
71
75
  root = Types::Root.new
72
76
  methods.each do |method|
73
- add_function root, method
77
+ add_function root, method, options
74
78
  end
75
79
  root.children.map(&:to_h)
76
80
  end
77
81
 
78
82
  protected
79
83
 
80
- def add_function(root, method)
84
+ def add_function(root, method, include_source: true)
81
85
  package = method.package
82
86
  static = method.static
83
87
 
@@ -109,6 +113,16 @@ module AppMap
109
113
  [ method.defined_class, static ? '.' : '#', method.name ].join
110
114
  end
111
115
 
116
+ if include_source
117
+ begin
118
+ function_info[:source] = method.source
119
+ comment = method.comment || ''
120
+ function_info[:comment] = comment unless comment.empty?
121
+ rescue MethodSource::SourceNotFoundError
122
+ # pass
123
+ end
124
+ end
125
+
112
126
  function_info[:labels] = package.labels if package.labels
113
127
  object_infos << function_info
114
128
 
@@ -148,7 +148,7 @@ module AppMap
148
148
 
149
149
  AppMap::RSpec.add_event_methods @trace.event_methods
150
150
 
151
- class_map = AppMap.class_map(@trace.event_methods)
151
+ class_map = AppMap.class_map(@trace.event_methods, include_source: false)
152
152
 
153
153
  description = []
154
154
  scope = ScopeExample.new(example)
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.36.0'
6
+ VERSION = '0.37.0'
7
7
 
8
- APPMAP_FORMAT_VERSION = '1.2'
8
+ APPMAP_FORMAT_VERSION = '1.3'
9
9
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'AppMap::ClassMap' do
6
+ describe '.build_from_methods' do
7
+ it 'includes source code if available' do
8
+ map = AppMap.class_map([scoped_method(method(:test_method))])
9
+ function = dig_map(map, 5)[0]
10
+ expect(function[:source]).to include 'test method body'
11
+ expect(function[:comment]).to include 'test method comment'
12
+ end
13
+
14
+ it 'can omit source code even if available' do
15
+ map = AppMap.class_map([scoped_method((method :test_method))], include_source: false)
16
+ function = dig_map(map, 5)[0]
17
+ expect(function).to_not include(:source)
18
+ expect(function).to_not include(:comment)
19
+ end
20
+
21
+ # test method comment
22
+ def test_method
23
+ 'test method body'
24
+ end
25
+
26
+ def scoped_method(method)
27
+ AppMap::Trace::ScopedMethod.new AppMap::Config::Package.new, method.receiver.class.name, method, false
28
+ end
29
+
30
+ def dig_map(map, depth)
31
+ return map if depth == 0
32
+
33
+ dig_map map[0][:children], depth - 1
34
+ end
35
+ end
36
+ end
@@ -112,6 +112,10 @@ describe 'AppMap class Hooking', docker: false do
112
112
  :type: function
113
113
  :location: spec/fixtures/hook/instance_method.rb:8
114
114
  :static: false
115
+ :source: |2
116
+ def say_default
117
+ 'default'
118
+ end
115
119
  YAML
116
120
  end
117
121
 
@@ -713,6 +717,10 @@ describe 'AppMap class Hooking', docker: false do
713
717
  :type: function
714
718
  :location: spec/fixtures/hook/compare.rb:4
715
719
  :static: true
720
+ :source: |2
721
+ def self.compare(s1, s2)
722
+ ActiveSupport::SecurityUtils.secure_compare(s1, s2)
723
+ end
716
724
  - :name: active_support
717
725
  :type: package
718
726
  :children:
@@ -729,6 +737,15 @@ describe 'AppMap class Hooking', docker: false do
729
737
  :labels:
730
738
  - security
731
739
  - crypto
740
+ :comment: |
741
+ # Constant time string comparison, for variable length strings.
742
+ #
743
+ # The values are first processed by SHA256, so that we don't leak length info
744
+ # via timing attacks.
745
+ :source: |2
746
+ def secure_compare(a, b)
747
+ fixed_length_secure_compare(::Digest::SHA256.digest(a), ::Digest::SHA256.digest(b)) && a == b
748
+ end
732
749
  - :name: openssl
733
750
  :type: package
734
751
  :children:
@@ -0,0 +1,55 @@
1
+ [
2
+ {
3
+ "name": "lib",
4
+ "type": "package",
5
+ "children": [
6
+ {
7
+ "name": "Example",
8
+ "type": "class",
9
+ "children": [
10
+ {
11
+ "name": "sign",
12
+ "type": "function",
13
+ "location": "lib/openssl_key_sign.rb:10",
14
+ "static": true,
15
+ "source": " def Example.sign\n key = OpenSSL::PKey::RSA.new 2048\n\n document = 'the document'\n\n digest = OpenSSL::Digest::SHA256.new\n key.sign digest, document\n end\n"
16
+ }
17
+ ]
18
+ }
19
+ ]
20
+ },
21
+ {
22
+ "name": "openssl",
23
+ "type": "package",
24
+ "children": [
25
+ {
26
+ "name": "OpenSSL",
27
+ "type": "class",
28
+ "children": [
29
+ {
30
+ "name": "PKey",
31
+ "type": "class",
32
+ "children": [
33
+ {
34
+ "name": "PKey",
35
+ "type": "class",
36
+ "children": [
37
+ {
38
+ "name": "sign",
39
+ "type": "function",
40
+ "location": "OpenSSL::PKey::PKey#sign",
41
+ "static": false,
42
+ "labels": [
43
+ "security",
44
+ "crypto"
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ }
51
+ ]
52
+ }
53
+ ]
54
+ }
55
+ ]
@@ -0,0 +1,58 @@
1
+ [
2
+ {
3
+ "id": 1,
4
+ "event": "call",
5
+ "defined_class": "Example",
6
+ "method_id": "sign",
7
+ "path": "lib/openssl_key_sign.rb",
8
+ "lineno": 10,
9
+ "static": true,
10
+ "parameters": [
11
+
12
+ ],
13
+ "receiver": {
14
+ "class": "Module"
15
+ }
16
+ },
17
+ {
18
+ "id": 2,
19
+ "event": "call",
20
+ "defined_class": "OpenSSL::PKey::PKey",
21
+ "method_id": "sign",
22
+ "path": "OpenSSL::PKey::PKey#sign",
23
+ "static": false,
24
+ "parameters": [
25
+ {
26
+ "name": "arg",
27
+ "class": "OpenSSL::Digest::SHA256",
28
+ "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
29
+ "kind": "req"
30
+ },
31
+ {
32
+ "name": "arg",
33
+ "class": "String",
34
+ "value": "the document",
35
+ "kind": "req"
36
+ }
37
+ ],
38
+ "receiver": {
39
+ "class": "OpenSSL::PKey::RSA"
40
+ }
41
+ },
42
+ {
43
+ "id": 3,
44
+ "event": "return",
45
+ "parent_id": 2,
46
+ "return_value": {
47
+ "class": "String"
48
+ }
49
+ },
50
+ {
51
+ "id": 4,
52
+ "event": "return",
53
+ "parent_id": 1,
54
+ "return_value": {
55
+ "class": "String"
56
+ }
57
+ }
58
+ ]
@@ -18,6 +18,10 @@ class OpenSSLTest < Minitest::Test
18
18
  end
19
19
  end
20
20
 
21
+ def expectation(name)
22
+ File.read File.join __dir__, 'expectations', name
23
+ end
24
+
21
25
  def test_key_sign
22
26
  perform_test 'key_sign' do
23
27
  appmap_file = 'appmap.json'
@@ -26,62 +30,7 @@ class OpenSSLTest < Minitest::Test
26
30
  appmap = JSON.parse(File.read(appmap_file))
27
31
  assert_equal AppMap::APPMAP_FORMAT_VERSION, appmap['version']
28
32
  assert_equal [ { 'recorder' => 'lib/openssl_key_sign.rb' } ], appmap['metadata']
29
- assert_equal JSON.parse(<<~JSON), appmap['classMap']
30
- [
31
- {
32
- "name": "lib",
33
- "type": "package",
34
- "children": [
35
- {
36
- "name": "Example",
37
- "type": "class",
38
- "children": [
39
- {
40
- "name": "sign",
41
- "type": "function",
42
- "location": "lib/openssl_key_sign.rb:10",
43
- "static": true
44
- }
45
- ]
46
- }
47
- ]
48
- },
49
- {
50
- "name": "openssl",
51
- "type": "package",
52
- "children": [
53
- {
54
- "name": "OpenSSL",
55
- "type": "class",
56
- "children": [
57
- {
58
- "name": "PKey",
59
- "type": "class",
60
- "children": [
61
- {
62
- "name": "PKey",
63
- "type": "class",
64
- "children": [
65
- {
66
- "name": "sign",
67
- "type": "function",
68
- "location": "OpenSSL::PKey::PKey#sign",
69
- "static": false,
70
- "labels": [
71
- "security",
72
- "crypto"
73
- ]
74
- }
75
- ]
76
- }
77
- ]
78
- }
79
- ]
80
- }
81
- ]
82
- }
83
- ]
84
- JSON
33
+ assert_equal JSON.parse(expectation('openssl_test_key_sign1.json')), appmap['classMap']
85
34
  sanitized_events = appmap['events'].map(&:deep_symbolize_keys).map(&AppMap::Util.method(:sanitize_event)).map do |event|
86
35
  delete_value = ->(obj) { (obj || {}).delete(:value) }
87
36
  delete_value.call(event[:receiver])
@@ -89,66 +38,10 @@ class OpenSSLTest < Minitest::Test
89
38
  event
90
39
  end
91
40
 
92
- diff = Diffy::Diff.new(<<~JSON.strip, JSON.pretty_generate(sanitized_events).strip)
93
- [
94
- {
95
- "id": 1,
96
- "event": "call",
97
- "defined_class": "Example",
98
- "method_id": "sign",
99
- "path": "lib/openssl_key_sign.rb",
100
- "lineno": 10,
101
- "static": true,
102
- "parameters": [
103
-
104
- ],
105
- "receiver": {
106
- "class": "Module"
107
- }
108
- },
109
- {
110
- "id": 2,
111
- "event": "call",
112
- "defined_class": "OpenSSL::PKey::PKey",
113
- "method_id": "sign",
114
- "path": "OpenSSL::PKey::PKey#sign",
115
- "static": false,
116
- "parameters": [
117
- {
118
- "name": "arg",
119
- "class": "OpenSSL::Digest::SHA256",
120
- "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
121
- "kind": "req"
122
- },
123
- {
124
- "name": "arg",
125
- "class": "String",
126
- "value": "the document",
127
- "kind": "req"
128
- }
129
- ],
130
- "receiver": {
131
- "class": "OpenSSL::PKey::RSA"
132
- }
133
- },
134
- {
135
- "id": 3,
136
- "event": "return",
137
- "parent_id": 2,
138
- "return_value": {
139
- "class": "String"
140
- }
141
- },
142
- {
143
- "id": 4,
144
- "event": "return",
145
- "parent_id": 1,
146
- "return_value": {
147
- "class": "String"
148
- }
149
- }
150
- ]
151
- JSON
41
+ diff = Diffy::Diff.new(
42
+ expectation('openssl_test_key_sign2.json').strip,
43
+ JSON.pretty_generate(sanitized_events).strip
44
+ )
152
45
  assert_equal '', diff.to_s
153
46
  end
154
47
  end
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.36.0
4
+ version: 0.37.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: 2020-10-05 00:00:00.000000000 Z
11
+ date: 2020-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: method_source
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: parser
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -366,6 +380,7 @@ files:
366
380
  - package.json
367
381
  - spec/abstract_controller4_base_spec.rb
368
382
  - spec/abstract_controller_base_spec.rb
383
+ - spec/class_map_spec.rb
369
384
  - spec/config_spec.rb
370
385
  - spec/fixtures/hook/attr_accessor.rb
371
386
  - spec/fixtures/hook/compare.rb
@@ -528,6 +543,8 @@ files:
528
543
  - spec/util_spec.rb
529
544
  - test/cli_test.rb
530
545
  - test/cucumber_test.rb
546
+ - test/expectations/openssl_test_key_sign1.json
547
+ - test/expectations/openssl_test_key_sign2.json
531
548
  - test/fixtures/cli_record_test/appmap.yml
532
549
  - test/fixtures/cli_record_test/lib/cli_record_test/main.rb
533
550
  - test/fixtures/cucumber4_recorder/Gemfile