Class: Ree::Contracts::MethodDecorator

Inherits:
Object
  • Object
show all
Includes:
Args
Defined in:
lib/ree/contracts/method_decorator.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Args

#check_arg, #check_arg_any, #check_arg_array_of, #check_bool, #not_nil

Constructor Details

#initialize(method_name, is_class_method, target) ⇒ MethodDecorator

Returns a new instance of MethodDecorator.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ree/contracts/method_decorator.rb', line 39

def initialize(method_name, is_class_method, target)
  check_arg(method_name, :method_name, Symbol)
  check_bool(is_class_method, :is_class_method)
  check_arg_any(target, :target, [Class, Module])

  engine = Engine.fetch_for(target)

  @method_name = method_name
  @is_class_method = is_class_method
  @target = target
  @contract_definition = engine.fetch_contract
  @errors = engine.fetch_errors
  @doc = engine.fetch_doc
end

Instance Attribute Details

#argsObject (readonly)

[CalledArgsValidator]



36
37
38
# File 'lib/ree/contracts/method_decorator.rb', line 36

def args
  @args
end

#contract_definitionObject (readonly)

[ContractDefinition] definition of contract



33
34
35
# File 'lib/ree/contracts/method_decorator.rb', line 33

def contract_definition
  @contract_definition
end

#docObject (readonly)

[Nilor[String]] rdoc



35
36
37
# File 'lib/ree/contracts/method_decorator.rb', line 35

def doc
  @doc
end

#errorsObject (readonly)

[Nilor[ArrayOf[Class]]] list of thrown errors



34
35
36
# File 'lib/ree/contracts/method_decorator.rb', line 34

def errors
  @errors
end

#is_class_methodObject (readonly)

[Bool]



32
33
34
# File 'lib/ree/contracts/method_decorator.rb', line 32

def is_class_method
  @is_class_method
end

#method_nameObject (readonly)

[Symbol] original method name



30
31
32
# File 'lib/ree/contracts/method_decorator.rb', line 30

def method_name
  @method_name
end

#method_parametersObject (readonly)

[Array] list of original method parameters



31
32
33
# File 'lib/ree/contracts/method_decorator.rb', line 31

def method_parameters
  @method_parameters
end

#return_validatorObject (readonly)

[BaseValidator] validator for return value



37
38
39
# File 'lib/ree/contracts/method_decorator.rb', line 37

def return_validator
  @return_validator
end

#targetObject (readonly)

[Class] class ot superclass of decorated method



29
30
31
# File 'lib/ree/contracts/method_decorator.rb', line 29

def target
  @target
end

Class Method Details

.active?Boolean

Returns:

  • (Boolean)


6
7
8
# File 'lib/ree/contracts/method_decorator.rb', line 6

def active?
  !Ree::Contracts.no_contracts?
end

.add_decorator(decorator) ⇒ Object



14
15
16
# File 'lib/ree/contracts/method_decorator.rb', line 14

def add_decorator(decorator)
  decorators[decorator.id] = decorator
end

.decorator_id(target, method_name, is_class_method) ⇒ Object



10
11
12
# File 'lib/ree/contracts/method_decorator.rb', line 10

def decorator_id(target, method_name, is_class_method)
  "#{target.object_id}#{target.name}#{method_name}#{is_class_method}"
end

.decoratorsObject



22
23
24
# File 'lib/ree/contracts/method_decorator.rb', line 22

def decorators
  @decorators ||= {}
end

.get_decorator(id) ⇒ Object



18
19
20
# File 'lib/ree/contracts/method_decorator.rb', line 18

def get_decorator(id)
  decorators[id]
end

Instance Method Details

#alias_targetObject

Target class to be used for alias method definition



92
93
94
95
96
97
# File 'lib/ree/contracts/method_decorator.rb', line 92

def alias_target
  @alias_target ||= begin
    return Utils.eigenclass_of(target) if is_class_method
    target
  end
end

#call(plugin_mode: true) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/ree/contracts/method_decorator.rb', line 54

def call(plugin_mode: true)
  return nil if Ree::Contracts.no_contracts?
  return nil unless contract_definition

  # Store decorator for runtime lookups (still needed)
  self.class.add_decorator(self)

  # Get method parameters from the method (before aliasing)
  # Note: We get params from the current method, not __ree_original_#{method_name}
  # because the alias hasn't been created yet when plugins are called
  @method_parameters = alias_target
    .instance_method(method_name)
    .parameters
    .freeze

  @args = CalledArgsValidator.new(
    contract_definition,
    method_parameters,
    printed_name
  )

  @return_validator = Validators.fetch_for(contract_definition.return_contract)

  if plugin_mode
    # Plugin mode: Return wrapper lambda for composition
    build_contract_wrapper
  else
    # Legacy mode: Apply wrapper directly (for Contractable standalone usage)
    apply_contract_wrapper_directly
  end
end

#idObject

Unique ID of this Method Decorator



87
88
89
# File 'lib/ree/contracts/method_decorator.rb', line 87

def id
  @id ||= self.class.decorator_id(target, method_name, is_class_method)
end

#validate_and_call(instance, method_alias, args, kwargs, &blk) ⇒ Object

Public method used by legacy contract wrapper (called from class_eval'd method)



100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/ree/contracts/method_decorator.rb', line 100

def validate_and_call(instance, method_alias, args, kwargs, &blk)
  @args.call(args, kwargs, blk)
  result = instance.send(method_alias, *args, **kwargs, &blk)

  unless @return_validator.call(result)
    raise ReturnContractError, "Invalid return value for #{printed_name}\n  #{
      @return_validator.message(result, 'returns', 0).strip
    }"
  end

  result
end