Skip to content

Commit 0aaae49

Browse files
committed
feat: Add swagger rake task
Also incorporates work to reduce depenency on activesupport, by providing simple implementations of functions such as blank?, classify, and deep_dup
1 parent 5872c63 commit 0aaae49

36 files changed

+3014
-127
lines changed

lib/appmap.rb

+28-110
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,37 @@
11
# frozen_string_literal: true
22

3-
begin
4-
require 'active_support'
5-
require 'active_support/core_ext'
6-
rescue NameError
7-
warn 'active_support is not available. AppMap execution will continue optimistically without it...'
8-
end
3+
# This is the file that's loaded when you require 'appmap'.
4+
# If you require this file, we assume that you want to automatically activate all
5+
# the AppMap functionality that seems suitable for your project.
6+
# For example, if your project is a Rails project, the Railtie will be loaded.
7+
# If your bundle includes rspec, the appmap/rspec will be loaded.
8+
#
9+
# If you don't want this "all-in" behavior, then you can use the 'require' option
10+
# in your Gemfile to selectively activate just the AppMap features that you want. Then
11+
# you can manually configure/require other features, elsewhere in your code.
12+
# Note that you should always require 'appmap/agent' as early as possible, so that it can
13+
# observe and hook as much code loading as possible.
14+
#
15+
# Modules that you can load independently include:
16+
# - appmap/agent
17+
# - appmap/railtie
18+
# - appmap/rspec
19+
# - appmap/minitest
20+
# - appmap/swagger (Rake task)
921

1022
require 'appmap/version'
11-
require 'appmap/util'
12-
require 'appmap/hook'
13-
require 'appmap/config'
14-
require 'appmap/trace'
15-
require 'appmap/class_map'
16-
require 'appmap/metadata'
17-
require 'appmap/open'
18-
19-
# load extension
20-
require 'appmap/appmap'
21-
22-
module AppMap
23-
class << self
24-
@configuration = nil
25-
@configuration_file_path = nil
26-
27-
# Gets the configuration. If there is no configuration, the default
28-
# configuration is initialized.
29-
def configuration
30-
@configuration ||= initialize_configuration
31-
end
32-
33-
# Sets the configuration. This is only expected to happen once per
34-
# Ruby process.
35-
def configuration=(config)
36-
warn 'AppMap is already configured' if @configuration && config
37-
38-
@configuration = config
39-
end
40-
41-
def default_config_file_path
42-
ENV['APPMAP_CONFIG_FILE'] || 'appmap.yml'
43-
end
44-
45-
# Configures AppMap for recording. Default behavior is to configure from
46-
# APPMAP_CONFIG_FILE, or 'appmap.yml'. If no config file is available, a
47-
# configuration will be automatically generated and used - and the user is prompted
48-
# to create the config file.
49-
#
50-
# This method also activates the code hooks which record function calls as trace events.
51-
# Call this function before the program code is loaded by the Ruby VM, otherwise
52-
# the load events won't be seen and the hooks won't activate.
53-
def initialize_configuration(config_file_path = default_config_file_path)
54-
Util.startup_message "Configuring AppMap from path #{config_file_path}"
55-
Config.load_from_file(config_file_path).tap do |configuration|
56-
self.configuration = configuration
57-
Hook.new(configuration).enable
58-
end
59-
end
60-
61-
def info(msg)
62-
if defined?(::Rails) && defined?(::Rails.logger)
63-
::Rails.logger.info msg
64-
else
65-
warn msg
66-
end
67-
end
68-
69-
# Used to start tracing, stop tracing, and record events.
70-
def tracing
71-
@tracing ||= Trace::Tracing.new
72-
end
73-
74-
# Records the events which occur while processing a block,
75-
# and returns an AppMap as a Hash.
76-
def record
77-
tracer = tracing.trace
78-
begin
79-
yield
80-
ensure
81-
tracing.delete(tracer)
82-
end
83-
84-
events = [].tap do |event_list|
85-
event_list << tracer.next_event.to_h while tracer.event?
86-
end
87-
{
88-
'version' => AppMap::APPMAP_FORMAT_VERSION,
89-
'metadata' => detect_metadata,
90-
'classMap' => class_map(tracer.event_methods),
91-
'events' => events
92-
}
93-
end
94-
95-
# Uploads an AppMap to the AppLand website and displays it.
96-
def open(appmap = nil, &block)
97-
appmap ||= AppMap.record(&block)
98-
AppMap::Open.new(appmap).perform
99-
end
100-
101-
# Builds a class map from a config and a list of Ruby methods.
102-
def class_map(methods)
103-
ClassMap.build_from_methods(methods)
104-
end
105-
106-
# Returns default metadata detected from the Ruby system and from the
107-
# filesystem.
108-
def detect_metadata
109-
@metadata ||= Metadata.detect.freeze
110-
@metadata.deep_dup
111-
end
112-
end
113-
end
23+
require 'appmap/agent'
11424

11525
lambda do
11626
Initializer = Struct.new(:class_name, :module_name, :gem_module_name)
11727

11828
INITIALIZERS = {
11929
'Rails::Railtie' => Initializer.new('AppMap::Railtie', 'appmap/railtie', 'railtie'),
12030
'RSpec' => Initializer.new('AppMap::RSpec', 'appmap/rspec', 'rspec-core'),
121-
'Minitest::Unit::TestCase' => Initializer.new('AppMap::Minitest', 'appmap/minitest', 'minitest')
31+
'Minitest::Unit::TestCase' => Initializer.new('AppMap::Minitest', 'appmap/minitest', 'minitest'),
32+
'Rake' => [
33+
Initializer.new('AppMap::Swagger', 'appmap/swagger', 'rake')
34+
]
12235
}
12336

12437
TracePoint.new(:class) do |tp|
@@ -155,6 +68,11 @@ def detect_metadata
15568
if defined?(::Minitest)
15669
require 'appmap/minitest'
15770
end
71+
72+
if defined?(::Rake)
73+
require 'appmap/swagger'
74+
end
75+
15876
end.call
15977

16078
AppMap.initialize_configuration if ENV['APPMAP'] == 'true'

lib/appmap/agent.rb

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
require_relative 'version'
2+
require_relative 'hook'
3+
require_relative 'config'
4+
require_relative 'trace'
5+
require_relative 'class_map'
6+
require_relative 'metadata'
7+
require_relative 'util'
8+
require_relative 'open'
9+
10+
# load extension
11+
require_relative 'appmap'
12+
13+
module AppMap
14+
class << self
15+
@configuration = nil
16+
@configuration_file_path = nil
17+
18+
# Gets the configuration. If there is no configuration, the default
19+
# configuration is initialized.
20+
def configuration
21+
@configuration ||= initialize_configuration
22+
end
23+
24+
# Sets the configuration. This is only expected to happen once per
25+
# Ruby process.
26+
def configuration=(config)
27+
warn 'AppMap is already configured' if @configuration && config
28+
29+
@configuration = config
30+
end
31+
32+
def default_config_file_path
33+
ENV['APPMAP_CONFIG_FILE'] || 'appmap.yml'
34+
end
35+
36+
# Configures AppMap for recording. Default behavior is to configure from
37+
# APPMAP_CONFIG_FILE, or 'appmap.yml'. If no config file is available, a
38+
# configuration will be automatically generated and used - and the user is prompted
39+
# to create the config file.
40+
#
41+
# This method also activates the code hooks which record function calls as trace events.
42+
# Call this function before the program code is loaded by the Ruby VM, otherwise
43+
# the load events won't be seen and the hooks won't activate.
44+
def initialize_configuration(config_file_path = default_config_file_path)
45+
Util.startup_message "Configuring AppMap from path #{config_file_path}"
46+
Config.load_from_file(config_file_path).tap do |configuration|
47+
self.configuration = configuration
48+
Hook.new(configuration).enable
49+
end
50+
end
51+
52+
def info(msg)
53+
if defined?(::Rails) && defined?(::Rails.logger)
54+
::Rails.logger.info msg
55+
else
56+
warn msg
57+
end
58+
end
59+
60+
def config_message(msg)
61+
logger = if defined?(::Rails) && ::Rails.logger
62+
::Rails.logger
63+
elsif ENV['DEBUG'] == 'true'
64+
method(:warn)
65+
else
66+
->(msg) { }
67+
end
68+
logger.call(msg)
69+
end
70+
71+
# Used to start tracing, stop tracing, and record events.
72+
def tracing
73+
@tracing ||= Trace::Tracing.new
74+
end
75+
76+
# Records the events which occur while processing a block,
77+
# and returns an AppMap as a Hash.
78+
def record
79+
tracer = tracing.trace
80+
begin
81+
yield
82+
ensure
83+
tracing.delete(tracer)
84+
end
85+
86+
events = [].tap do |event_list|
87+
event_list << tracer.next_event.to_h while tracer.event?
88+
end
89+
{
90+
'version' => AppMap::APPMAP_FORMAT_VERSION,
91+
'metadata' => detect_metadata,
92+
'classMap' => class_map(tracer.event_methods),
93+
'events' => events
94+
}
95+
end
96+
97+
# Uploads an AppMap to the AppLand website and displays it.
98+
def open(appmap = nil, &block)
99+
appmap ||= AppMap.record(&block)
100+
AppMap::Open.new(appmap).perform
101+
end
102+
103+
# Builds a class map from a config and a list of Ruby methods.
104+
def class_map(methods)
105+
ClassMap.build_from_methods(methods)
106+
end
107+
108+
# Returns default metadata detected from the Ruby system and from the
109+
# filesystem.
110+
def detect_metadata
111+
@metadata ||= Metadata.detect.freeze
112+
Util.deep_dup(@metadata)
113+
end
114+
end
115+
end

lib/appmap/class_map.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,15 @@ def add_function(root, method)
111111
end
112112

113113
comment = method.comment
114-
function_info[:comment] = comment unless comment.blank?
114+
function_info[:comment] = comment unless Util.blank?(comment)
115115

116116
function_info[:labels] = parse_labels(comment) + (method.labels || [])
117117
object_infos << function_info
118118

119119
parent = root
120120
object_infos.each do |info|
121121
parent = find_or_create parent.children, info do
122-
Types.const_get(info[:type].classify).new(info[:name].to_s).tap do |type|
122+
Types.const_get(Util.classify(info[:type])).new(info[:name].to_s).tap do |type|
123123
info.keys.tap do |keys|
124124
keys.delete(:name)
125125
keys.delete(:type)

lib/appmap/config.rb

+11-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
require 'appmap/handler/net_http'
66
require 'appmap/handler/rails/template'
77
require 'appmap/service/guesser'
8+
require 'appmap/swagger/configuration'
89

910
module AppMap
1011
class Config
@@ -81,8 +82,8 @@ def to_h
8182
package_name: package_name,
8283
gem: gem,
8384
handler_class: handler_class.name,
84-
exclude: exclude.blank? ? nil : exclude,
85-
labels: labels.blank? ? nil : labels,
85+
exclude: Util.blank?(exclude) ? nil : exclude,
86+
labels: Util.blank?(labels) ? nil : labels,
8687
shallow: shallow
8788
}.compact
8889
end
@@ -226,15 +227,17 @@ def method_hook(cls, method_names, labels)
226227
'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.generate])),
227228
}.freeze
228229

229-
attr_reader :name, :appmap_dir, :packages, :exclude, :hooked_methods, :builtin_hooks
230+
attr_reader :name, :appmap_dir, :packages, :exclude, :swagger_config, :hooked_methods, :builtin_hooks
230231

231232
def initialize(name,
232233
packages: [],
234+
swagger_config: Swagger::Configuration.new,
233235
exclude: [],
234236
functions: [])
235237
@name = name
236238
@appmap_dir = AppMap::DEFAULT_APPMAP_DIR
237239
@packages = packages
240+
@swagger_config = swagger_config
238241
@hook_paths = Set.new(packages.map(&:path))
239242
@exclude = exclude
240243
@builtin_hooks = BUILTIN_HOOKS
@@ -351,6 +354,11 @@ def load(config_data)
351354
end
352355
end
353356

357+
if config_data['swagger']
358+
swagger_config = Swagger::Configuration.load(config_data['swagger'])
359+
config_params[:swagger_config] = swagger_config
360+
end
361+
354362
Config.new name, config_params
355363
end
356364
end

lib/appmap/handler/net_http.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require 'appmap/event'
4+
require 'appmap/util'
45

56
module AppMap
67
module Handler
@@ -38,7 +39,7 @@ def to_h
3839
headers: headers
3940
}.compact
4041

41-
unless params.blank?
42+
unless Util.blank?(params)
4243
h[:message] = params.keys.map do |key|
4344
val = params[key]
4445
{

lib/appmap/handler/rails/request_handler.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require 'appmap/event'
44
require 'appmap/hook'
5+
require 'appmap/util'
56

67
module AppMap
78
module Handler
@@ -40,7 +41,7 @@ def to_h
4041
headers: headers,
4142
}.compact
4243

43-
unless params.blank?
44+
unless Util.blank?(params)
4445
h[:message] = params.keys.map do |key|
4546
val = params[key]
4647
{

lib/appmap/metadata.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require 'appmap/util'
4+
35
module AppMap
46
module Metadata
57
class << self
@@ -40,9 +42,9 @@ def git_metadata
4042
git_sha = `git rev-parse HEAD`.strip
4143
git_status = `git status -s`.split("\n").map(&:strip)
4244
git_last_annotated_tag = `git describe --abbrev=0 2>/dev/null`.strip
43-
git_last_annotated_tag = nil if git_last_annotated_tag.blank?
45+
git_last_annotated_tag = nil if Util.blank?(git_last_annotated_tag)
4446
git_last_tag = `git describe --abbrev=0 --tags 2>/dev/null`.strip
45-
git_last_tag = nil if git_last_tag.blank?
47+
git_last_tag = nil if Util.blank?(git_last_tag)
4648
git_commits_since_last_annotated_tag = `git describe`.strip =~ /-(\d+)-(\w+)$/[1] rescue 0 if git_last_annotated_tag
4749
git_commits_since_last_tag = `git describe --tags`.strip =~ /-(\d+)-(\w+)$/[1] rescue 0 if git_last_tag
4850

0 commit comments

Comments
 (0)