Class: Rigor::Plugin::ProtocolContract

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/plugin/protocol_contract.rb

Overview

ADR-28 declaration: “every instance/singleton method named ‘method_name`, defined in a source file matching `path_glob`, is implicitly required to satisfy the declared parameter + return-type protocol.”

Authored on a plugin manifest:

manifest(
  id: "web",
  version: "0.1.0",
  protocol_contracts: [
    Rigor::Plugin::ProtocolContract.new(
      path_glob: "lib/controller/**/*.rb",
      method_name: :get,
      param_types: [{ index: 0, type_name: "Rack::Request" }],
      return_type_name: "Rack::Response"
    )
  ]
)

The contract drives two distinct engine behaviours (ADR-28 § “provide-and-check”):

  • provide — when the inference engine binds the parameter list of a matching ‘def`, Inference::MethodParameterBinder substitutes the declared `param_types` for the usual `Dynamic` fallback, so the method body is analysed as if the parameter carried its protocol type.

  • check — the contributing plugin’s ‘#diagnostics_for_file` hook confirms the method exists and its inferred return type conforms to `return_type_name`.

## Fields

  • ‘path_glob` — `File.fnmatch` glob (String) selecting the source files the contract applies to, relative to the analysed project root (e.g. `“lib/controller/*/.rb”`).

  • ‘method_name` — Symbol; the instance (or singleton) method the contract constrains.

  • ‘singleton` — Boolean; `true` constrains `def self.<name>`, `false` (default) constrains instance methods.

  • ‘param_types` — Array of `ParamType` (positional index →fully-qualified type name). The type names resolve against the analysed project’s environment lazily, at consumption time, so the contract value object stays independent of environment construction order.

  • ‘return_type_name` — fully-qualified type name (String) the method’s inferred return type must conform to.

  • ‘severity` — Symbol diagnostic severity for contract violations (`:error` default).

## Ractor-shareability

Every field is frozen at construction (ADR-15 Phase 1); the nested ‘ParamType` is a frozen `Data`. `Ractor.shareable?` returns true after `#initialize`, so the contract survives `Plugin::Registry.materialize` into a worker Ractor.

Defined Under Namespace

Classes: ParamType

Constant Summary collapse

VALID_SEVERITIES =
%i[error warning info].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path_glob:, method_name:, return_type_name: nil, param_types: [], singleton: false, severity: :error) ⇒ ProtocolContract

Returns a new instance of ProtocolContract.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/rigor/plugin/protocol_contract.rb', line 72

def initialize(path_glob:, method_name:, return_type_name: nil, param_types: [], singleton: false,
               severity: :error)
  validate_path_glob!(path_glob)
  validate_method_name!(method_name)
  validate_return_type_name!(return_type_name)
  validate_severity!(severity)

  @path_glob = path_glob.dup.freeze
  @method_name = method_name.to_sym
  @singleton = singleton ? true : false
  @param_types = coerce_param_types(param_types)
  @return_type_name = return_type_name.nil? ? nil : return_type_name.dup.freeze
  @severity = severity.to_sym
  freeze
end

Instance Attribute Details

#method_nameObject (readonly)

Returns the value of attribute method_name.



70
71
72
# File 'lib/rigor/plugin/protocol_contract.rb', line 70

def method_name
  @method_name
end

#param_typesObject (readonly)

Returns the value of attribute param_types.



70
71
72
# File 'lib/rigor/plugin/protocol_contract.rb', line 70

def param_types
  @param_types
end

#path_globObject (readonly)

Returns the value of attribute path_glob.



70
71
72
# File 'lib/rigor/plugin/protocol_contract.rb', line 70

def path_glob
  @path_glob
end

#return_type_nameObject (readonly)

Returns the value of attribute return_type_name.



70
71
72
# File 'lib/rigor/plugin/protocol_contract.rb', line 70

def return_type_name
  @return_type_name
end

#severityObject (readonly)

Returns the value of attribute severity.



70
71
72
# File 'lib/rigor/plugin/protocol_contract.rb', line 70

def severity
  @severity
end

#singletonObject (readonly)

Returns the value of attribute singleton.



70
71
72
# File 'lib/rigor/plugin/protocol_contract.rb', line 70

def singleton
  @singleton
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



113
114
115
# File 'lib/rigor/plugin/protocol_contract.rb', line 113

def ==(other)
  other.is_a?(ProtocolContract) && to_h == other.to_h
end

#hashObject



118
119
120
# File 'lib/rigor/plugin/protocol_contract.rb', line 118

def hash
  to_h.hash
end

#to_hObject



102
103
104
105
106
107
108
109
110
111
# File 'lib/rigor/plugin/protocol_contract.rb', line 102

def to_h
  {
    "path_glob" => path_glob,
    "method_name" => method_name.to_s,
    "singleton" => singleton,
    "param_types" => param_types.map { |pt| { "index" => pt.index, "type_name" => pt.type_name } },
    "return_type_name" => return_type_name,
    "severity" => severity.to_s
  }
end

#with_path_glob(glob) ⇒ Object

Returns a copy with ‘path_glob` replaced. Plugins use this to honour a per-project config override of the convention path without rebuilding the whole contract by hand.



91
92
93
94
95
96
97
98
99
100
# File 'lib/rigor/plugin/protocol_contract.rb', line 91

def with_path_glob(glob)
  ProtocolContract.new(
    path_glob: glob,
    method_name: method_name,
    return_type_name: return_type_name,
    param_types: param_types.map { |pt| { index: pt.index, type_name: pt.type_name } },
    singleton: singleton,
    severity: severity
  )
end