Class: AppMap::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/appmap/config.rb

Defined Under Namespace

Classes: LookupPackage, Package

Constant Summary collapse

OPENSSL_PACKAGES =
->(labels) { Package.build_from_path('openssl', package_name: 'openssl', labels: labels) }
HOOKED_METHODS =

Methods that should always be hooked, with their containing package and labels that should be applied to them.

{
  'ActionView::Renderer' => TargetMethods.new(:render, Package.build_from_gem('actionview', shallow: false, package_name: 'action_view', labels: %w[mvc.view], optional: true).tap do |package|
    package.handler_class = AppMap::Handler::Rails::Template::RenderHandler if package
  end),
  'ActionView::Resolver' => TargetMethods.new(i[find_all find_all_anywhere], Package.build_from_gem('actionview', shallow: false, package_name: 'action_view', labels: %w[mvc.template.resolver], optional: true).tap do |package|
    package.handler_class = AppMap::Handler::Rails::Template::ResolverHandler if package
  end),
  'ActionDispatch::Request::Session' => TargetMethods.new(i[destroy [] dig values []= clear update delete fetch merge], Package.build_from_gem('actionpack', shallow: false, package_name: 'action_dispatch', labels: %w[http.session], optional: true)),
  'ActionDispatch::Cookies::CookieJar' => TargetMethods.new(i[[]= clear update delete recycle], Package.build_from_gem('actionpack', shallow: false, package_name: 'action_dispatch', labels: %w[http.cookie], optional: true)),
  'ActionDispatch::Cookies::EncryptedCookieJar' => TargetMethods.new(i[[]=], Package.build_from_gem('actionpack', shallow: false, package_name: 'action_dispatch', labels: %w[http.cookie crypto.encrypt], optional: true)),
  'CanCan::ControllerAdditions' => TargetMethods.new(i[authorize! can? cannot?], Package.build_from_gem('cancancan', shallow: false, labels: %w[security.authorization], optional: true)),
  'CanCan::Ability' => TargetMethods.new(i[authorize!], Package.build_from_gem('cancancan', shallow: false, labels: %w[security.authorization], optional: true)),
  'ActionController::Instrumentation' => [
    TargetMethods.new(i[process_action send_file send_data redirect_to], Package.build_from_gem('actionpack', shallow: false, package_name: 'action_controller', labels: %w[mvc.controller], optional: true))
  ]
}.freeze
BUILTIN_METHODS =
{
  '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[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml])),
  'JSON::Ext::Parser' => TargetMethods.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json])),
  'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json])),
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, packages, exclude: [], functions: []) ⇒ Config

Returns a new instance of Config.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/appmap/config.rb', line 171

def initialize(name, packages, exclude: [], functions: [])
  @name = name
  @packages = packages
  @hook_paths = packages.map(&:path)
  @exclude = exclude
  @builtin_methods = BUILTIN_METHODS
  @functions = functions
  @hooked_methods = HOOKED_METHODS.dup
  functions.each do |func|
    package_options = {}
    package_options[:labels] = func.labels if func.labels
    @hooked_methods[func.cls] ||= []
    @hooked_methods[func.cls] << TargetMethods.new(func.function_names, Package.build_from_path(func.package, package_options))
  end

  @hooked_methods.each_value do |hooks|
    Array(hooks).each do |hook|
      @hook_paths << hook.package.path if hook.package
    end
  end
end

Instance Attribute Details

#builtin_methodsObject (readonly)

Returns the value of attribute builtin_methods.



169
170
171
# File 'lib/appmap/config.rb', line 169

def builtin_methods
  @builtin_methods
end

#excludeObject (readonly)

Returns the value of attribute exclude.



169
170
171
# File 'lib/appmap/config.rb', line 169

def exclude
  @exclude
end

#hooked_methodsObject (readonly)

Returns the value of attribute hooked_methods.



169
170
171
# File 'lib/appmap/config.rb', line 169

def hooked_methods
  @hooked_methods
end

#nameObject (readonly)

Returns the value of attribute name.



169
170
171
# File 'lib/appmap/config.rb', line 169

def name
  @name
end

#packagesObject (readonly)

Returns the value of attribute packages.



169
170
171
# File 'lib/appmap/config.rb', line 169

def packages
  @packages
end

Class Method Details

.load(config_data) ⇒ Object

Loads configuration from a Hash.



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/appmap/config.rb', line 201

def load(config_data)
  functions = (config_data['functions'] || []).map do |function_data|
    package = function_data['package']
    cls = function_data['class']
    functions = function_data['function'] || function_data['functions']
    raise 'AppMap class configuration should specify package, class and function(s)' 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
  packages = (config_data['packages'] || []).map do |package|
    gem = package['gem']
    path = package['path']
    raise 'AppMap package configuration 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
  exclude = config_data['exclude'] || []
  Config.new config_data['name'], packages, exclude: exclude, functions: functions
end

.load_from_file(config_file_name) ⇒ Object

Loads configuration data from a file, specified by the file name.



195
196
197
198
# File 'lib/appmap/config.rb', line 195

def load_from_file(config_file_name)
  require 'yaml'
  load YAML.safe_load(::File.read(config_file_name))
end

Instance Method Details

#lookup_package(cls, method) ⇒ Object



281
282
283
# File 'lib/appmap/config.rb', line 281

def lookup_package(cls, method)
  LookupPackage.new(self, cls, method).package
end

#never_hook?(cls, method) ⇒ Boolean

Returns:



285
286
287
288
# File 'lib/appmap/config.rb', line 285

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.

Returns:



241
242
243
244
# File 'lib/appmap/config.rb', line 241

def path_enabled?(path)
  path = AppMap::Util.normalize_path(path)
  @hook_paths.find { |hook_path| path.index(hook_path) == 0 }
end

#to_hObject



231
232
233
234
235
236
237
238
# File 'lib/appmap/config.rb', line 231

def to_h
  {
    name: name,
    packages: packages.map(&:to_h),
    functions: @functions.map(&:to_h),
    exclude: exclude
  }
end