Class: Henitai::Subject

Inherits:
Object
  • Object
show all
Defined in:
lib/henitai/subject.rb,
sig/henitai.rbs

Overview

Represents an addressable unit of source code to be mutated.

Subjects are expressed using a compact syntax:

Foo::Bar#instance_method    — specific instance method
Foo::Bar.class_method       — specific class method
Foo::Bar*                   — all methods on Foo::Bar
Foo*                        — all methods in the Foo namespace

The Subject is resolved from the AST before mutation begins. Test selection uses longest-prefix matching against example group descriptions in the test suite.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(expression: nil, namespace: nil, method_name: nil, method_type: :instance, source_location: nil, ast_node: nil) ⇒ Subject

The explicit keyword API matches the public call sites and keeps the metadata fields discoverable without a single options hash. rubocop:disable Metrics/ParameterLists

Parameters:

  • namespace (String) (defaults to: nil)

    fully-qualified module/class name

  • method_name (String) (defaults to: nil)

    method name (nil for wildcard subjects)

  • method_type (Symbol) (defaults to: :instance)

    :instance or :class

  • source_location (Hash) (defaults to: nil)

    file/range metadata for the subject source

  • expression: (String) (defaults to: nil)
  • namespace: (String) (defaults to: nil)
  • method_name: (String) (defaults to: nil)
  • method_type: (Symbol) (defaults to: :instance)
  • source_location: (Hash[Symbol, untyped]) (defaults to: nil)
  • ast_node: (Object) (defaults to: nil)


31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/henitai/subject.rb', line 31

def initialize(expression: nil, namespace: nil, method_name: nil,
               method_type: :instance, source_location: nil, ast_node: nil)
  if expression
    parse_expression(expression)
  else
    @namespace   = namespace
    @method_name = method_name
    @method_type = method_type
  end
  @source_file  = source_location&.fetch(:file, nil)
  @source_range = source_location&.fetch(:range, nil)
  @ast_node = ast_node
end

Instance Attribute Details

#ast_nodeObject (readonly)

Returns the value of attribute ast_node.



16
17
18
# File 'lib/henitai/subject.rb', line 16

def ast_node
  @ast_node
end

#method_nameString? (readonly)

Returns the value of attribute method_name.

Returns:

  • (String, nil)


16
17
18
# File 'lib/henitai/subject.rb', line 16

def method_name
  @method_name
end

#method_typeSymbol (readonly)

Returns the value of attribute method_type.

Returns:

  • (Symbol)


16
17
18
# File 'lib/henitai/subject.rb', line 16

def method_type
  @method_type
end

#namespaceString? (readonly)

Returns the value of attribute namespace.

Returns:

  • (String, nil)


16
17
18
# File 'lib/henitai/subject.rb', line 16

def namespace
  @namespace
end

#source_fileString? (readonly)

Returns the value of attribute source_file.

Returns:

  • (String, nil)


16
17
18
# File 'lib/henitai/subject.rb', line 16

def source_file
  @source_file
end

#source_rangeRange[Integer]? (readonly)

Returns the value of attribute source_range.

Returns:

  • (Range[Integer], nil)


16
17
18
# File 'lib/henitai/subject.rb', line 16

def source_range
  @source_range
end

Class Method Details

.parse(expression) ⇒ Subject

Parameters:

  • expression (String)

    subject expression, e.g. "Foo::Bar#method"

  • (String)

Returns:



20
21
22
# File 'lib/henitai/subject.rb', line 20

def self.parse(expression)
  new(expression:)
end

Instance Method Details

#expressionString

Full addressable expression, e.g. "Foo::Bar#method"

Returns:

  • (String)


47
48
49
50
# File 'lib/henitai/subject.rb', line 47

def expression
  sep = @method_type == :class ? "." : "#"
  @method_name ? "#{@namespace}#{sep}#{@method_name}" : "#{@namespace}*"
end

#parse_expression(expr) ⇒ void

This method returns an undefined value.

Parameters:

  • (String)


58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/henitai/subject.rb', line 58

def parse_expression(expr)
  if (m = expr.match(/\A(.+?)([#.])(\w+)\z/))
    @namespace   = m[1]
    @method_type = m[2] == "." ? :class : :instance
    @method_name = m[3]
  else
    # Wildcard: "Foo*" or "Foo::Bar*"
    @namespace   = expr.delete_suffix("*")
    @method_name = nil
    @method_type = :instance
  end
end

#wildcard?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/henitai/subject.rb', line 52

def wildcard?
  @method_name.nil?
end