appmap 0.23.0 → 0.25.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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +17 -8
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +19 -0
  6. data/README.md +29 -12
  7. data/Rakefile +3 -3
  8. data/appmap.gemspec +3 -1
  9. data/exe/appmap +6 -18
  10. data/lib/appmap.rb +47 -6
  11. data/lib/appmap/algorithm/prune_class_map.rb +2 -0
  12. data/lib/appmap/algorithm/stats.rb +4 -2
  13. data/lib/appmap/class_map.rb +143 -0
  14. data/lib/appmap/command/record.rb +8 -6
  15. data/lib/appmap/command/stats.rb +2 -0
  16. data/lib/appmap/command/upload.rb +4 -2
  17. data/lib/appmap/event.rb +168 -0
  18. data/lib/appmap/hook.rb +151 -0
  19. data/lib/appmap/middleware/remote_recording.rb +14 -20
  20. data/lib/appmap/rails/action_handler.rb +10 -6
  21. data/lib/appmap/rails/sql_handler.rb +10 -8
  22. data/lib/appmap/railtie.rb +31 -18
  23. data/lib/appmap/rspec.rb +238 -261
  24. data/lib/appmap/trace.rb +88 -0
  25. data/lib/appmap/version.rb +1 -1
  26. data/package-lock.json +90 -92
  27. data/spec/abstract_controller4_base_spec.rb +1 -1
  28. data/spec/abstract_controller_base_spec.rb +7 -3
  29. data/spec/config_spec.rb +25 -0
  30. data/spec/fixtures/hook/attr_accessor.rb +5 -0
  31. data/spec/fixtures/hook/class_method.rb +17 -0
  32. data/spec/fixtures/hook/constructor.rb +7 -0
  33. data/spec/fixtures/hook/exception_method.rb +11 -0
  34. data/spec/fixtures/hook/instance_method.rb +23 -0
  35. data/spec/fixtures/rails4_users_app/app/controllers/api/users_controller.rb +3 -3
  36. data/spec/fixtures/rails4_users_app/config/database.yml +2 -1
  37. data/spec/fixtures/rails4_users_app/docker-compose.yml +2 -0
  38. data/spec/fixtures/rails_users_app/.ruby-version +1 -1
  39. data/spec/fixtures/rails_users_app/app/controllers/api/users_controller.rb +2 -2
  40. data/spec/fixtures/rails_users_app/config/database.yml +2 -1
  41. data/spec/fixtures/rails_users_app/create_app +1 -0
  42. data/spec/fixtures/rails_users_app/docker-compose.yml +4 -0
  43. data/spec/fixtures/rails_users_app/spec/models/user_spec.rb +1 -1
  44. data/spec/hook_spec.rb +357 -0
  45. data/spec/rails_spec_helper.rb +25 -16
  46. data/spec/railtie_spec.rb +1 -1
  47. data/spec/record_sql_rails_pg_spec.rb +1 -2
  48. data/spec/remote_recording_spec.rb +117 -0
  49. data/spec/spec_helper.rb +1 -0
  50. data/test/cli_test.rb +7 -36
  51. data/test/fixtures/cli_record_test/appmap.yml +2 -1
  52. data/test/fixtures/cli_record_test/lib/cli_record_test/main.rb +4 -2
  53. data/test/test_helper.rb +0 -42
  54. metadata +46 -62
  55. data/exe/_appmap-record-self +0 -49
  56. data/lib/appmap/command/inspect.rb +0 -14
  57. data/lib/appmap/config.rb +0 -65
  58. data/lib/appmap/config/directory.rb +0 -65
  59. data/lib/appmap/config/file.rb +0 -13
  60. data/lib/appmap/config/named_function.rb +0 -21
  61. data/lib/appmap/config/package_dir.rb +0 -52
  62. data/lib/appmap/config/path.rb +0 -25
  63. data/lib/appmap/feature.rb +0 -262
  64. data/lib/appmap/inspect.rb +0 -91
  65. data/lib/appmap/inspect/inspector.rb +0 -99
  66. data/lib/appmap/inspect/parse_node.rb +0 -170
  67. data/lib/appmap/inspect/parser.rb +0 -15
  68. data/lib/appmap/parser.rb +0 -60
  69. data/lib/appmap/rspec/parse_node.rb +0 -41
  70. data/lib/appmap/rspec/parser.rb +0 -15
  71. data/lib/appmap/trace/event_handler/rack_handler_webrick.rb +0 -65
  72. data/lib/appmap/trace/tracer.rb +0 -356
  73. data/spec/fixtures/rails_users_app/bin/_appmap-record-self +0 -29
  74. data/spec/rack_handler_webrick_spec.rb +0 -59
  75. data/test/config_test.rb +0 -149
  76. data/test/explict_inspect_test.rb +0 -29
  77. data/test/fixtures/active_record_like/active_record.rb +0 -2
  78. data/test/fixtures/active_record_like/active_record/aggregations.rb +0 -4
  79. data/test/fixtures/active_record_like/active_record/association.rb +0 -4
  80. data/test/fixtures/active_record_like/active_record/associations/join_dependency.rb +0 -6
  81. data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_base.rb +0 -8
  82. data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_part.rb +0 -8
  83. data/test/fixtures/active_record_like/active_record/caps/caps.rb +0 -4
  84. data/test/fixtures/ignore_non_ruby_file/class.rb +0 -3
  85. data/test/fixtures/ignore_non_ruby_file/non-ruby.txt +0 -1
  86. data/test/fixtures/includes_excludes/lib/a/a_1.rb +0 -6
  87. data/test/fixtures/includes_excludes/lib/a/a_2.rb +0 -6
  88. data/test/fixtures/includes_excludes/lib/a/x/x_1.rb +0 -8
  89. data/test/fixtures/includes_excludes/lib/b/b_1.rb +0 -6
  90. data/test/fixtures/includes_excludes/lib/root_1.rb +0 -4
  91. data/test/fixtures/inspect_multiple_subdirs/module_a.rb +0 -2
  92. data/test/fixtures/inspect_multiple_subdirs/module_a/class_a.rb +0 -5
  93. data/test/fixtures/inspect_multiple_subdirs/module_b.rb +0 -2
  94. data/test/fixtures/inspect_multiple_subdirs/module_b/class_b.rb +0 -5
  95. data/test/fixtures/inspect_multiple_subdirs/module_b/class_c.rb +0 -5
  96. data/test/fixtures/inspect_package/module_a/module_b/class_in_module.rb +0 -6
  97. data/test/fixtures/parse_file/defs_static_function.rb +0 -96
  98. data/test/fixtures/parse_file/function_within_class.rb +0 -36
  99. data/test/fixtures/parse_file/include_public_methods.rb +0 -127
  100. data/test/fixtures/parse_file/instance_function.rb +0 -17
  101. data/test/fixtures/parse_file/modules.rb +0 -71
  102. data/test/fixtures/parse_file/sclass_static_function.rb +0 -88
  103. data/test/fixtures/parse_file/toplevel_class.rb +0 -13
  104. data/test/fixtures/parse_file/toplevel_function.rb +0 -14
  105. data/test/fixtures/trace_test/trace_program_1.rb +0 -44
  106. data/test/implicit_inspect_test.rb +0 -33
  107. data/test/include_exclude_test.rb +0 -48
  108. data/test/prerecorded_trace_test.rb +0 -76
  109. data/test/trace_test.rb +0 -92
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'json'
4
-
5
- $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib')
6
-
7
- require 'appmap'
8
- require 'appmap/feature'
9
- require 'shellwords'
10
-
11
- def usage
12
- warn 'Usage: trace-self <trace-file>'
13
- exit 1
14
- end
15
-
16
- trace_file = ARGV.shift || usage
17
- usage unless ARGV.empty?
18
-
19
- replay_events = File.read(trace_file)
20
- .split("\n")
21
- .map(&:strip)
22
- .reject(&:empty?)
23
- .map(&JSON.method(:parse))
24
- .map { |te| te['event'] = te['event'].intern; te }
25
- .map { |te| OpenStruct.new(te) }
26
-
27
- require 'appmap/trace/tracer'
28
-
29
- def method_call_from_event(evt)
30
- AppMap::Trace::MethodCall.new(evt.id, evt.event.intern, evt.defined_class, evt.method_id, evt.path, evt.lineno, evt.static, evt.thread_id, evt.variables)
31
- end
32
-
33
- # _parent_id and _elapsed are ignored since they are already specified in the
34
- # data being replayed.
35
- def method_return_from_event(evt, _parent_id, _elapsed)
36
- AppMap::Trace::MethodReturn.new(evt.id, evt.event.intern, evt.defined_class, evt.method_id, evt.path, evt.lineno, evt.static, evt.thread_id, evt.variables).tap do |mr|
37
- mr.parent_id = evt.parent_id
38
- mr.elapsed = evt.elapsed
39
- end
40
- end
41
-
42
- tracer = AppMap::Trace.tracer
43
- handler = AppMap::Trace::TracePointHandler.new(tracer)
44
- handler.call_constructor = method(:method_call_from_event)
45
- handler.return_constructor = method(:method_return_from_event)
46
-
47
- replay_events.each do |evt|
48
- handler.handle evt
49
- end
@@ -1,14 +0,0 @@
1
- module AppMap
2
- module Command
3
- InspectStruct = Struct.new(:config)
4
-
5
- class Inspect < InspectStruct
6
- def perform
7
- require 'appmap/command/record'
8
-
9
- features = AppMap.inspect(config)
10
- { version: AppMap::APPMAP_FORMAT_VERSION, metadata: AppMap::Command::Record.detect_metadata, classMap: features }
11
- end
12
- end
13
- end
14
- end
data/lib/appmap/config.rb DELETED
@@ -1,65 +0,0 @@
1
- require 'appmap/config/path'
2
- require 'appmap/config/file'
3
- require 'appmap/config/directory'
4
- require 'appmap/config/package_dir'
5
- require 'appmap/config/named_function'
6
-
7
- module AppMap
8
- module Config
9
- class Configuration
10
- attr_reader :name, :packages, :files, :named_functions
11
-
12
- def initialize(name)
13
- @name = name
14
- @packages = []
15
- @files = []
16
- @named_functions = []
17
- end
18
-
19
- def source_locations
20
- packages + files + named_functions
21
- end
22
- end
23
-
24
- class << self
25
- NAMED_FUNCTIONS = [
26
- Config::NamedFunction.new(:rack_handler_webrick, 'rack', 'lib/rack/handler/webrick.rb',
27
- %w[Rack Handler WEBrick], 'service', false)
28
- ].freeze
29
-
30
- # Loads configuration data from a file, specified by the file name.
31
- def load_from_file(config_file_name)
32
- require 'yaml'
33
- load YAML.safe_load(::File.read(config_file_name))
34
- end
35
-
36
- # Loads configuration from a Hash.
37
- def load(config_data)
38
- Configuration.new(config_data['name']).tap do |config|
39
- builders = Hash.new { |_, key| raise "Unknown config type #{key.inspect}" }
40
- builders[:packages] = lambda { |path, options|
41
- AppMap::Config::PackageDir.new(path).tap do |pdir|
42
- pdir.package_name = options['name'] if options['name']
43
- pdir.exclude = options['exclude'] if options['exclude']
44
- end
45
- }
46
- builders[:files] = ->(path, _) { AppMap::Config::File.new(path) }
47
-
48
- %i[packages files].each do |kind|
49
- next unless (members = config_data[kind.to_s])
50
- members.each do |member|
51
- path = member.delete('path')
52
- config.send(kind) << builders[kind].call(path, member)
53
- end
54
- end
55
-
56
- NAMED_FUNCTIONS.each do |dep|
57
- next if config_data['named_functions'] && !config_data['named_functions'].member?(dep.gem_name)
58
-
59
- config.named_functions << dep
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
@@ -1,65 +0,0 @@
1
- module AppMap
2
- module Config
3
- # A normal directory is scanned for AppMap features without interpreting the
4
- # directory as a 'package'.
5
- #
6
- # @appmap
7
- class Directory < Path
8
- # @appmap
9
- def initialize(path)
10
- super
11
- end
12
-
13
- # @appmap
14
- def children
15
- child_files.sort + child_directories.sort
16
- end
17
-
18
- protected
19
-
20
- def ruby_file?(path)
21
- ::File.file?(path) && (path =~ /\.rb$/ || ruby_shebang?(path))
22
- end
23
-
24
- def ruby_shebang?(path)
25
- lines = begin
26
- ::File.read(path).split("\n")
27
- rescue ArgumentError => e
28
- if e.message.index 'invalid byte sequence'
29
- warn "Unable to load file #{path.inspect} : #{e.message}"
30
- return false
31
- end
32
- raise
33
- end
34
- lines[0] && lines[0].index('#!/usr/bin/env ruby') == 0
35
- end
36
-
37
- def child_files
38
- expand_path = ->(fname) { ::File.join(path, fname) }
39
- Dir.new(path).entries.select do |fname|
40
- ::File.file?(expand_path.call(fname)) &&
41
- !::File.symlink?(expand_path.call(fname)) &&
42
- ruby_file?(expand_path.call(fname))
43
- end.select do |fname|
44
- !exclude?(::File.join(path, fname))
45
- end.map do |fname|
46
- File.new(expand_path.call(fname)).tap do |f|
47
- f.mode = mode
48
- end
49
- end
50
- end
51
-
52
- def child_directories
53
- File.new(path).entries.select do |fname|
54
- !%w[. ..].include?(fname) && !::File.directory?(fname)
55
- end.select do |dir|
56
- !exclude?(::File.join(path, dir))
57
- end.map do |dir|
58
- PackageDir.new(dir, [module_name, dir].join('/')).tap do |m|
59
- m.mode = mode
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
@@ -1,13 +0,0 @@
1
- module AppMap
2
- module Config
3
- # Scan a specific file for AppMap features.
4
- #
5
- # @appmap
6
- class File < Path
7
- # @appmap
8
- def initialize(path)
9
- super
10
- end
11
- end
12
- end
13
- end
@@ -1,21 +0,0 @@
1
- module AppMap
2
- module Config
3
- NamedFunctionStruct = Struct.new(:id, :gem_name, :file_path, :class_names, :method_name, :static)
4
-
5
- # Identifies a specific function within a Gem to be instrumented.
6
- #
7
- # * `id` A unique identifier for the named function. This is used to associate custom logic with the
8
- # named function when the trace events are being handled.
9
- # * `gem_name` Name of the Gem.
10
- # * `file_path` Name of the file within the Gem in which the function is located.
11
- # * `class_names` Array of the module/class name scope which contains the function. For example,
12
- # `%w[Rack Handler WEBrick]`.
13
- # * `method_name` Name of the method within the class name scope.
14
- # * `static` Whether it's a static or instance method.
15
- class NamedFunction < NamedFunctionStruct
16
- def children
17
- []
18
- end
19
- end
20
- end
21
- end
@@ -1,52 +0,0 @@
1
- require 'pathname'
2
-
3
- module AppMap
4
- module Config
5
- # Scan a directory for AppMap features, treating it as a package and its
6
- # sub-folders as sub-packages.
7
- #
8
- # @appmap
9
- class PackageDir < Directory
10
- attr_accessor :package_name, :base_path, :exclude
11
-
12
- # @appmap
13
- def initialize(path, package_name = Pathname.new(path || '').basename.to_s)
14
- super(path)
15
-
16
- @package_name = package_name
17
- @base_path = path
18
- @exclude = []
19
- end
20
-
21
- def sub_package_dir(dir)
22
- PackageDir.new(::File.join(path, dir), dir).tap do |m|
23
- m.base_path = base_path
24
- m.exclude = exclude
25
- m.mode = mode
26
- end
27
- end
28
-
29
- def exclude?(path)
30
- relative_path = path.gsub("#{base_path}/", '')
31
- exclude.member?(relative_path)
32
- end
33
-
34
- # @appmap
35
- def children
36
- child_files.sort + child_packages.sort
37
- end
38
-
39
- protected
40
-
41
- def child_packages
42
- ::Dir.new(path).entries.select do |fname|
43
- !%w[. ..].include?(fname) && ::File.directory?(::File.join(path, fname))
44
- end.select do |dir|
45
- !exclude?(::File.join(path, dir))
46
- end.map do |dir|
47
- sub_package_dir(dir)
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,25 +0,0 @@
1
- module AppMap
2
- module Config
3
- PathStruct = Struct.new(:path)
4
-
5
- # Path is an abstract configuration of a file, directory, or package.
6
- class Path < PathStruct
7
- attr_accessor :mode
8
-
9
- def initialize(path)
10
- super(path)
11
-
12
- @mode = :implicit
13
- end
14
-
15
- def <=>(other)
16
- path <=> other.path
17
- end
18
-
19
- # Automatically determined configurations of child file/package paths.
20
- def children
21
- []
22
- end
23
- end
24
- end
25
- end
@@ -1,262 +0,0 @@
1
- module AppMap
2
- # A Feature is a construct within the code that will be observed. Examples features include
3
- # modules, classes and functions.
4
- module Feature
5
- TYPE_MAP = {
6
- 'cls' => 'class'
7
- }.freeze
8
-
9
- class << self
10
- FEATURE_BUILDERS = {
11
- module: ->(_) { Module.new },
12
- class: ->(_) { Cls.new },
13
- function: lambda do |hash|
14
- static = hash.delete('static')
15
- class_name = hash.delete('class_name')
16
- Function.new.tap do |e|
17
- e.static = static
18
- e.class_name = class_name
19
- end
20
- end
21
- }.freeze
22
-
23
- # Deserialize a feature from a Hash. The Hash is typically a deserialized JSON dump of the feature.
24
- def from_hash(hash)
25
- builder = FEATURE_BUILDERS[hash['type'].to_sym]
26
- raise "Unrecognized type of feature: #{type.inspect}" unless builder
27
-
28
- feature = builder.call(hash)
29
- feature.name = hash['name']
30
- feature.location = hash['location']
31
- feature.attributes = hash['attributes'] || {}
32
- feature.children = (hash['children'] || []).map { |child| from_hash(child) }
33
- feature
34
- end
35
- end
36
-
37
- FeatureStruct = Struct.new(:name, :location, :attributes)
38
-
39
- # Base is an abstract base class for features.
40
- class Base < FeatureStruct
41
- class << self
42
- def expand_path(location)
43
- path, lineno = location.split(':')
44
- [ path, lineno ].compact.join(':')
45
- end
46
- end
47
-
48
- attr_reader :parent, :children
49
-
50
- def initialize(name, location, attributes)
51
- super(name, self.class.expand_path(location), attributes)
52
-
53
- @parent = nil
54
- @children = []
55
- end
56
-
57
- def remove_child(child)
58
- # TODO: Encountered this indexing appland with active_dispatch
59
- children.delete(child) or warn "Unable to remove #{name.inspect} from parent" # or raise "No such child : #{child}"
60
- child.instance_variable_set('@parent', nil)
61
- end
62
-
63
- def add_child(child)
64
- @children << child
65
- child.instance_variable_set('@parent', self)
66
- end
67
-
68
- # Gets an array containing the type names which enclose this feature.
69
- def enclosing_type_name
70
- @enclosing_type_name ||= [].tap do |names|
71
- p = self
72
- while (p = p.parent) && p.type?
73
- names << p.name
74
- end
75
- end.reverse
76
- end
77
-
78
- # true iff this feature has an enclosing type. An example of when this is false: when
79
- # the parent of the feature is not a type (e.g. it's a location).
80
- def enclosing_type_name?
81
- !enclosing_type_name.empty?
82
- end
83
-
84
- # The 'include' attribute can indicate which elements of the parse subtree
85
- # to automatically add as features. For example: public_classes, public_modules,
86
- # public_methods.
87
- def include_option
88
- (attributes[:include] || '').split(',')
89
- end
90
-
91
- # yield each function to a block.
92
- def collect_functions(accumulator = [])
93
- accumulator.tap do |_|
94
- accumulator << self if is_a?(Function)
95
- children.each { |child| child.collect_functions(accumulator) }
96
- end
97
- end
98
-
99
- def type?
100
- false
101
- end
102
-
103
- def valid?
104
- !name.blank? && !location.blank?
105
- end
106
-
107
- def to_json(*opts)
108
- to_h.to_json(*opts)
109
- end
110
-
111
- def to_h
112
- super.tap do |map|
113
- map.delete(:parent)
114
- class_name = self.class.name.underscore.split('/')[-1]
115
- map[:type] = TYPE_MAP[class_name] || class_name
116
- map[:children] = @children.map(&:to_h) unless @children.empty?
117
- map.delete(:attributes) if map[:attributes].empty?
118
- end
119
- end
120
-
121
- # Determines if this feature should be dropped from the feature tree.
122
- # A feature is dropped from the feature tree if it doesn't add useful information for the user.
123
- # Performing this operation removes feature nodes that don't add anything useful to the user.
124
- # For example, empty classes.
125
- def prune(parent = nil)
126
- should_prune = prune? && !parent.nil?
127
- parent = self unless should_prune
128
- children.dup.each do |child|
129
- child.prune(parent)
130
- end
131
-
132
- # Perform the prune in post-fix traversal order, otherwise the
133
- # features will get confused about whether they should prune or not.
134
- if should_prune
135
- parent.remove_child(self)
136
- children.each do |child|
137
- parent.add_child(child)
138
- end
139
- end
140
- end
141
-
142
- # Determines if this feature should be re-parented as a child of a different feature.
143
- #
144
- # A feature is re-parented if the enclosing type of the feature has already been defined in the tree.
145
- #
146
- # @param parent the parent of this feature in the compacted tree.
147
- def reparent(parent = nil, features_by_type = {})
148
- # Determine if the enclosing type of the feature is defined.
149
- # Generally, it should be.
150
-
151
- existing_enclosing_type = features_by_type[enclosing_type_name] if enclosing_type_name?
152
- if existing_enclosing_type
153
- parent = existing_enclosing_type
154
- end
155
-
156
- # Determine if this feature is a type which is already defined.
157
- type_exists = true if type? && features_by_type.key?(type_name)
158
-
159
- # If this feature is a type that's already defined, skip over it and
160
- # add the children to the existing feature. Otherwise, clone this feature
161
- # under the parent and use the cloned object as the parent of the compacted
162
- # children.
163
- if type_exists
164
- features_by_type[type_name]
165
- else
166
- clone.tap do |f|
167
- parent.add_child(f) if parent
168
- features_by_type[type_name] = f if type?
169
- end
170
- end.tap do |updated_parent|
171
- children.each do |child|
172
- child.reparent(updated_parent, features_by_type)
173
- end
174
- end
175
- end
176
-
177
- def prune?
178
- false
179
- end
180
-
181
- protected
182
-
183
- def clone
184
- self.class.new(name, location, attributes)
185
- end
186
-
187
- def child_classes
188
- children.select { |c| c.is_a?(Cls) }
189
- end
190
-
191
- def child_nonclasses
192
- children.reject { |c| c.is_a?(Cls) }
193
- end
194
- end
195
-
196
- # Package is a feature which represents the directory containing code.
197
- class Package < Base
198
- # prune a package if it's empty, or if it contains anything but packages.
199
- def prune?
200
- children.empty? || children.any? { |c| !c.is_a?(Package) }
201
- end
202
- end
203
-
204
- # Cls is a feature which represents a code class. A class defines a namespace which contains other
205
- # features (such as member classes and functions), and it also usually encapsulates some data on which
206
- # the member features operate.
207
- class Cls < Base
208
- # prune a class if it's empty.
209
- def prune?
210
- children.empty?
211
- end
212
-
213
- def type?
214
- true
215
- end
216
-
217
- # Gets the type name of this class as an array.
218
- def type_name
219
- @type_name ||= enclosing_type_name + [ name ]
220
- end
221
- end
222
-
223
- # Function is a feature which represents a code function. It can be an instance function or static (aka 'class')
224
- # function. Instance functions operate on the instance data of the class on which they are defined. Static
225
- # functions are used to perform operations which don't have want or need of instance data.
226
- #
227
- # * `handler_id` If provided, identifies a trace handler which can apply specialized logic to the
228
- # event data which is recorded for this function. For example, if the function represents a handler
229
- # method for a web server, the custom handler can inspect and record the HTTP request method and path info.
230
- class Function < Base
231
- attr_accessor :static, :class_name, :handler_id
232
-
233
- alias static? static
234
- def instance?
235
- !static?
236
- end
237
-
238
- # Static functions must have an enclosing class defined in order to be traced.
239
- def valid?
240
- super && (instance? || !class_name.blank?)
241
- end
242
-
243
- def to_h
244
- super.tap do |h|
245
- # Suppress the class name when it can be inferred from the enclosing type.
246
- h[:class_name] = class_name if class_name && class_name != enclosing_type_name.join('::')
247
- h[:static] = static?
248
- end
249
- end
250
-
251
- protected
252
-
253
- def clone
254
- super.tap do |obj|
255
- obj.static = static
256
- obj.class_name = class_name
257
- obj.handler_id = handler_id
258
- end
259
- end
260
- end
261
- end
262
- end