Class: AppMap::Config
- Inherits:
-
Object
- Object
- AppMap::Config
- Defined in:
- lib/appmap/config.rb
Defined Under Namespace
Classes: LookupPackage, Package
Constant Summary collapse
- METHOD_HOOKS =
Hook well-known functions. When a function configured here is available in the bundle, it will be hooked with the predefined labels specified here. If any of these hooks are not desired, they can be disabled in the
excludesection of appmap.yml. package_hooks('actionview', [ method_hook('ActionView::Renderer', :render, %w[mvc.view]), method_hook('ActionView::TemplateRenderer', :render, %w[mvc.view]), method_hook('ActionView::PartialRenderer', :render, %w[mvc.view]) ], handler_class: AppMap::Handler::Rails::Template::RenderHandler, package_name: 'action_view' ), package_hooks('actionview', [ method_hook('ActionView::Resolver', %i[find_all find_all_anywhere], %w[mvc.template.resolver]) ], handler_class: AppMap::Handler::Rails::Template::ResolverHandler, package_name: 'action_view' ), package_hooks('actionpack', [ method_hook('ActionDispatch::Request::Session', %i[[] dig values fetch], %w[http.session.read]), method_hook('ActionDispatch::Request::Session', %i[destroy[]= clear update delete merge], %w[http.session.write]), method_hook('ActionDispatch::Cookies::CookieJar', %i[[]= clear update delete recycle], %w[http.session.read]), method_hook('ActionDispatch::Cookies::CookieJar', %i[[]= clear update delete recycle], %w[http.session.write]), method_hook('ActionDispatch::Cookies::EncryptedCookieJar', %i[[]= clear update delete recycle], %w[http.cookie crypto.encrypt]) ], package_name: 'action_dispatch' ), package_hooks('cancancan', [ method_hook('CanCan::ControllerAdditions', %i[authorize! can? cannot?], %w[security.authorization]), method_hook('CanCan::Ability', %i[authorize?], %w[security.authorization]) ] ), package_hooks('actionpack', [ method_hook('ActionController::Instrumentation', %i[process_action send_file send_data redirect_to], %w[mvc.controller]) ], package_name: 'action_controller' ) ].flatten.freeze- OPENSSL_PACKAGES =
->(labels) { Package.build_from_path('openssl', package_name: 'openssl', labels: labels) }
- BUILTIN_HOOKS =
Hook functions which are builtin to Ruby. Because they are builtins, they may be loaded before appmap. Therefore, we can't rely on TracePoint to report the loading of this code.
{ 'OpenSSL::PKey::PKey' => TargetMethods.new(:sign, OPENSSL_PACKAGES.(%w[crypto.pkey])), 'OpenSSL::X509::Request' => TargetMethods.new(%i[sign verify], OPENSSL_PACKAGES.(%w[crypto.x509])), 'OpenSSL::PKCS5' => TargetMethods.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES.(%w[crypto.pkcs5])), 'OpenSSL::Cipher' => [ TargetMethods.new(%i[encrypt], OPENSSL_PACKAGES.(%w[crypto.encrypt])), TargetMethods.new(%i[decrypt], OPENSSL_PACKAGES.(%w[crypto.decrypt])) ], 'ActiveSupport::Callbacks::CallbackSequence' => [ TargetMethods.new(:invoke_before, Package.build_from_gem('activesupport', force: true, package_name: 'active_support', labels: %w[mvc.before_action])), TargetMethods.new(:invoke_after, Package.build_from_gem('activesupport', force: true, package_name: 'active_support', labels: %w[mvc.after_action])), ], 'ActiveSupport::SecurityUtils' => TargetMethods.new(:secure_compare, Package.build_from_gem('activesupport', force: true, package_name: 'active_support/security_utils', labels: %w[crypto.secure_compare])), 'OpenSSL::X509::Certificate' => TargetMethods.new(:sign, OPENSSL_PACKAGES.(%w[crypto.x509])), 'Net::HTTP' => TargetMethods.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http]).tap do |package| package.handler_class = AppMap::Handler::NetHTTP end), 'Net::SMTP' => TargetMethods.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.email.smtp])), 'Net::POP3' => TargetMethods.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.email.pop])), # This is happening: Method send_command not found on Net::IMAP # 'Net::IMAP' => TargetMethods.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.email.imap])), # 'Marshal' => TargetMethods.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal])), 'Psych' => [ TargetMethods.new(%i[load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml.parse])), TargetMethods.new(%i[dump dump_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml.generate])), ], 'JSON::Ext::Parser' => TargetMethods.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.parse])), 'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.generate])), }.freeze
Instance Attribute Summary collapse
-
#appmap_dir ⇒ Object
readonly
Returns the value of attribute appmap_dir.
-
#builtin_hooks ⇒ Object
readonly
Returns the value of attribute builtin_hooks.
-
#exclude ⇒ Object
readonly
Returns the value of attribute exclude.
-
#hooked_methods ⇒ Object
readonly
Returns the value of attribute hooked_methods.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#packages ⇒ Object
readonly
Returns the value of attribute packages.
-
#swagger_config ⇒ Object
readonly
Returns the value of attribute swagger_config.
Class Method Summary collapse
-
.load(config_data) ⇒ Object
Loads configuration from a Hash.
-
.load_from_file(config_file_name) ⇒ Object
Loads configuration data from a file, specified by the file name.
- .method_hook(cls, method_names, labels) ⇒ Object
- .package_hooks(gem_name, methods, handler_class: nil, package_name: nil) ⇒ Object
Instance Method Summary collapse
-
#initialize(name, packages: [], swagger_config: Swagger::Configuration.new, exclude: [], functions: []) ⇒ Config
constructor
A new instance of Config.
- #lookup_package(cls, method) ⇒ Object
- #never_hook?(cls, method) ⇒ Boolean
-
#path_enabled?(path) ⇒ Boolean
Determines if methods defined in a file path should possibly be hooked.
- #to_h ⇒ Object
Constructor Details
#initialize(name, packages: [], swagger_config: Swagger::Configuration.new, exclude: [], functions: []) ⇒ Config
Returns a new instance of Config.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/appmap/config.rb', line 233 def initialize(name, packages: [], swagger_config: Swagger::Configuration.new, exclude: [], functions: []) @name = name @appmap_dir = AppMap::DEFAULT_APPMAP_DIR @packages = packages @swagger_config = swagger_config @hook_paths = Set.new(packages.map(&:path)) @exclude = exclude @builtin_hooks = BUILTIN_HOOKS @functions = functions @hooked_methods = METHOD_HOOKS.each_with_object(Hash.new { |h,k| h[k] = [] }) do |cls_target_methods, hooked_methods| hooked_methods[cls_target_methods.cls] << cls_target_methods.target_methods end functions.each do |func| = {} [:labels] = func.labels if func.labels @hooked_methods[func.cls] << TargetMethods.new(func.function_names, Package.build_from_path(func.package, )) end @hooked_methods.each_value do |hooks| Array(hooks).each do |hook| @hook_paths << hook.package.path end end end |
Instance Attribute Details
#appmap_dir ⇒ Object (readonly)
Returns the value of attribute appmap_dir.
231 232 233 |
# File 'lib/appmap/config.rb', line 231 def appmap_dir @appmap_dir end |
#builtin_hooks ⇒ Object (readonly)
Returns the value of attribute builtin_hooks.
231 232 233 |
# File 'lib/appmap/config.rb', line 231 def builtin_hooks @builtin_hooks end |
#exclude ⇒ Object (readonly)
Returns the value of attribute exclude.
231 232 233 |
# File 'lib/appmap/config.rb', line 231 def exclude @exclude end |
#hooked_methods ⇒ Object (readonly)
Returns the value of attribute hooked_methods.
231 232 233 |
# File 'lib/appmap/config.rb', line 231 def hooked_methods @hooked_methods end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
231 232 233 |
# File 'lib/appmap/config.rb', line 231 def name @name end |
#packages ⇒ Object (readonly)
Returns the value of attribute packages.
231 232 233 |
# File 'lib/appmap/config.rb', line 231 def packages @packages end |
#swagger_config ⇒ Object (readonly)
Returns the value of attribute swagger_config.
231 232 233 |
# File 'lib/appmap/config.rb', line 231 def swagger_config @swagger_config end |
Class Method Details
.load(config_data) ⇒ Object
Loads configuration from a Hash.
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/appmap/config.rb', line 316 def load(config_data) name = config_data['name'] || Service::Guesser.guess_name config_params = { exclude: config_data['exclude'] }.compact if config_data['functions'] config_params[:functions] = config_data['functions'].map do |function_data| package = function_data['package'] cls = function_data['class'] functions = function_data['function'] || function_data['functions'] raise %q(AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions') unless package && cls && functions functions = Array(functions).map(&:to_sym) labels = function_data['label'] || function_data['labels'] labels = Array(labels).map(&:to_s) if labels Function.new(package, cls, labels, functions) end end config_params[:packages] = \ if config_data['packages'] config_data['packages'].map do |package| gem = package['gem'] path = package['path'] raise %q(AppMap config 'package' element should specify 'gem' or 'path', not both) if gem && path if gem shallow = package['shallow'] # shallow is true by default for gems shallow = true if shallow.nil? Package.build_from_gem(gem, package_name: package['package'], exclude: package['exclude'] || [], shallow: shallow) else Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow']) end end.compact else Array(Service::Guesser.guess_paths).map do |path| Package.build_from_path(path) end end if config_data['swagger'] swagger_config = Swagger::Configuration.load(config_data['swagger']) config_params[:swagger_config] = swagger_config end Config.new name, config_params end |
.load_from_file(config_file_name) ⇒ Object
Loads configuration data from a file, specified by the file name.
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/appmap/config.rb', line 266 def load_from_file(config_file_name) logo = lambda do Util.color(<<~LOGO, :magenta) ___ __ ___ / _ | ___ ___ / |/ /__ ____ / __ |/ _ \\/ _ \\/ /|_/ / _ `/ _ \\ /_/ |_/ .__/ .__/_/ /_/\\_,_/ .__/ /_/ /_/ /_/ LOGO end config_present = true if File.exists?(config_file_name) config_data = if config_present YAML.safe_load(::File.read(config_file_name)) else warn logo.() warn '' warn Util.color(%Q|NOTICE: The AppMap config file #{config_file_name} was not found!|, :magenta, bold: true) warn '' warn Util.color(<<~MISSING_FILE_MSG, :magenta) AppMap uses this file to customize its behavior. For example, you can use the 'packages' setting to indicate which local file paths and dependency gems you want to include in the AppMap. Since you haven't provided specific settings, the appmap gem will try and guess some reasonable defaults. To suppress this message, create the file: #{Pathname.new(config_file_name).} Here are the default settings that will be used in the meantime. You can copy and paste this example to start your appmap.yml. MISSING_FILE_MSG {} end load(config_data).tap do |config| config_yaml = { 'name' => config.name, 'packages' => config.packages.select{|p| p.path}.map do |pkg| { 'path' => pkg.path } end, 'exclude' => [] }.compact unless config_present warn Util.color(YAML.dump(config_yaml), :magenta) warn logo.() end end end |
.method_hook(cls, method_names, labels) ⇒ Object
148 149 150 |
# File 'lib/appmap/config.rb', line 148 def method_hook(cls, method_names, labels) MethodHook.new(cls, method_names, labels) end |
.package_hooks(gem_name, methods, handler_class: nil, package_name: nil) ⇒ Object
138 139 140 141 142 143 144 145 146 |
# File 'lib/appmap/config.rb', line 138 def package_hooks(gem_name, methods, handler_class: nil, package_name: nil) Array(methods).map do |method| package = Package.build_from_gem(gem_name, package_name: package_name, labels: method.labels, shallow: false, optional: true) next unless package package.handler_class = handler_class if handler_class ClassTargetMethods.new(method.cls, TargetMethods.new(Array(method.method_names), package)) end.compact end |
Instance Method Details
#lookup_package(cls, method) ⇒ Object
417 418 419 |
# File 'lib/appmap/config.rb', line 417 def lookup_package(cls, method) LookupPackage.new(self, cls, method).package end |
#never_hook?(cls, method) ⇒ Boolean
421 422 423 424 |
# File 'lib/appmap/config.rb', line 421 def never_hook?(cls, method) _, separator, = ::AppMap::Hook.qualify_method_name(method) return true if exclude.member?(cls.name) || exclude.member?([ cls.name, separator, method.name ].join) end |
#path_enabled?(path) ⇒ Boolean
Determines if methods defined in a file path should possibly be hooked.
377 378 379 380 |
# File 'lib/appmap/config.rb', line 377 def path_enabled?(path) path = AppMap::Util.normalize_path(path) @hook_paths.find { |hook_path| path.index(hook_path) == 0 } end |
#to_h ⇒ Object
367 368 369 370 371 372 373 374 |
# File 'lib/appmap/config.rb', line 367 def to_h { name: name, packages: packages.map(&:to_h), functions: @functions.map(&:to_h), exclude: exclude }.compact end |