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
exclude
section 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.
Class Method Summary collapse
- .guess_name ⇒ Object
- .guess_paths ⇒ Object
-
.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: [], 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: [], exclude: [], functions: []) ⇒ Config
Returns a new instance of Config.
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/appmap/config.rb', line 229 def initialize(name, packages: [], exclude: [], functions: []) @name = name @appmap_dir = AppMap::DEFAULT_APPMAP_DIR @packages = packages @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.
227 228 229 |
# File 'lib/appmap/config.rb', line 227 def appmap_dir @appmap_dir end |
#builtin_hooks ⇒ Object (readonly)
Returns the value of attribute builtin_hooks.
227 228 229 |
# File 'lib/appmap/config.rb', line 227 def builtin_hooks @builtin_hooks end |
#exclude ⇒ Object (readonly)
Returns the value of attribute exclude.
227 228 229 |
# File 'lib/appmap/config.rb', line 227 def exclude @exclude end |
#hooked_methods ⇒ Object (readonly)
Returns the value of attribute hooked_methods.
227 228 229 |
# File 'lib/appmap/config.rb', line 227 def hooked_methods @hooked_methods end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
227 228 229 |
# File 'lib/appmap/config.rb', line 227 def name @name end |
#packages ⇒ Object (readonly)
Returns the value of attribute packages.
227 228 229 |
# File 'lib/appmap/config.rb', line 227 def packages @packages end |
Class Method Details
.guess_name ⇒ Object
355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/appmap/config.rb', line 355 def guess_name reponame = lambda do next unless File.directory?('.git') repo_name = `git config --get remote.origin.url`.strip repo_name.split('/').last.split('.').first unless repo_name == '' end dirname = -> { Dir.pwd.split('/').last } reponame.() || dirname.() end |
.guess_paths ⇒ Object
367 368 369 370 371 372 373 |
# File 'lib/appmap/config.rb', line 367 def guess_paths if defined?(::Rails) %w[app/controllers app/models] elsif File.directory?('lib') %w[lib] end end |
.load(config_data) ⇒ Object
Loads configuration from a Hash.
310 311 312 313 314 315 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 |
# File 'lib/appmap/config.rb', line 310 def load(config_data) name = config_data['name'] || 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, exclude: package['exclude'] || [], shallow: shallow) else Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow']) end end.compact else Array(guess_paths).map do |path| Package.build_from_path(path) end 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.
260 261 262 263 264 265 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 |
# File 'lib/appmap/config.rb', line 260 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
144 145 146 |
# File 'lib/appmap/config.rb', line 144 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
134 135 136 137 138 139 140 141 142 |
# File 'lib/appmap/config.rb', line 134 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
426 427 428 |
# File 'lib/appmap/config.rb', line 426 def lookup_package(cls, method) LookupPackage.new(self, cls, method).package end |
#never_hook?(cls, method) ⇒ Boolean
430 431 432 433 |
# File 'lib/appmap/config.rb', line 430 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.
386 387 388 389 |
# File 'lib/appmap/config.rb', line 386 def path_enabled?(path) path = AppMap::Util.normalize_path(path) @hook_paths.find { |hook_path| path.index(hook_path) == 0 } end |
#to_h ⇒ Object
376 377 378 379 380 381 382 383 |
# File 'lib/appmap/config.rb', line 376 def to_h { name: name, packages: packages.map(&:to_h), functions: @functions.map(&:to_h), exclude: exclude }.compact end |