@@ -15,6 +15,7 @@ class Client
1515 } . freeze
1616 RESULT_TYPE = TYPE_CLASS_MAP . keys . freeze
1717 SUFFIXES = %i[ value details ] . freeze
18+ EMPTY_HINTS = Hooks ::Hints . new . freeze
1819
1920 attr_reader :metadata , :evaluation_context
2021
@@ -40,11 +41,8 @@ def remove_handler(event_type, handler = nil, &block)
4041 RESULT_TYPE . each do |result_type |
4142 SUFFIXES . each do |suffix |
4243 class_eval <<-RUBY , __FILE__ , __LINE__ + 1
43- # def fetch_boolean_details(flag_key:, default_value:, evaluation_context: nil)
44- # result = @provider.fetch_boolean_value(flag_key: flag_key, default_value: default_value, evaluation_context: evaluation_context)
45- # end
46- def fetch_#{ result_type } _#{ suffix } (flag_key:, default_value:, evaluation_context: nil)
47- evaluation_details = fetch_details(type: :#{ result_type } , flag_key:, default_value:, evaluation_context:)
44+ def fetch_#{ result_type } _#{ suffix } (flag_key:, default_value:, evaluation_context: nil, hooks: [], hook_hints: nil)
45+ evaluation_details = fetch_details(type: :#{ result_type } , flag_key:, default_value:, evaluation_context:, invocation_hooks: hooks, hook_hints: hook_hints)
4846 #{ "evaluation_details.value" if suffix == :value }
4947 end
5048 RUBY
@@ -53,20 +51,62 @@ def fetch_#{result_type}_#{suffix}(flag_key:, default_value:, evaluation_context
5351
5452 private
5553
56- def fetch_details ( type :, flag_key :, default_value :, evaluation_context : nil )
54+ def fetch_details ( type :, flag_key :, default_value :, evaluation_context : nil , invocation_hooks : [ ] , hook_hints : nil )
5755 validate_default_value_type ( type , default_value )
5856
59- built_context = EvaluationContextBuilder . new . call ( api_context : OpenFeature ::SDK . evaluation_context , client_context : self . evaluation_context , invocation_context : evaluation_context )
57+ built_context = EvaluationContextBuilder . new . call (
58+ api_context : OpenFeature ::SDK . evaluation_context ,
59+ client_context : self . evaluation_context ,
60+ invocation_context : evaluation_context
61+ )
6062
61- resolution_details = @provider . send ( :"fetch_#{ type } _value" , flag_key :, default_value :, evaluation_context : built_context )
63+ # Assemble ordered hooks: API → Client → Invocation → Provider (spec 4.4.2)
64+ provider_hooks = @provider . respond_to? ( :hooks ) ? Array ( @provider . hooks ) : [ ]
65+ ordered_hooks = [ *OpenFeature ::SDK . hooks , *@hooks , *invocation_hooks , *provider_hooks ]
66+
67+ # Fast path: skip hook ceremony when no hooks are registered
68+ if ordered_hooks . empty?
69+ return evaluate_flag ( type : type , flag_key : flag_key , default_value : default_value , evaluation_context : built_context )
70+ end
71+
72+ hook_context = Hooks ::HookContext . new (
73+ flag_key : flag_key ,
74+ flag_value_type : type ,
75+ default_value : default_value ,
76+ evaluation_context : built_context ,
77+ client_metadata : @metadata ,
78+ provider_metadata : @provider . respond_to? ( :metadata ) ? @provider . metadata : nil
79+ )
80+
81+ hints = if hook_hints . is_a? ( Hooks ::Hints )
82+ hook_hints
83+ elsif hook_hints
84+ Hooks ::Hints . new ( hook_hints )
85+ else
86+ EMPTY_HINTS
87+ end
88+
89+ executor = Hooks ::HookExecutor . new ( logger : OpenFeature ::SDK . configuration . logger )
90+ executor . execute ( ordered_hooks : ordered_hooks , hook_context : hook_context , hints : hints ) do |hctx |
91+ evaluate_flag ( type : type , flag_key : flag_key , default_value : default_value , evaluation_context : hctx . evaluation_context )
92+ end
93+ end
94+
95+ def evaluate_flag ( type :, flag_key :, default_value :, evaluation_context :)
96+ resolution_details = @provider . send (
97+ :"fetch_#{ type } _value" ,
98+ flag_key : flag_key ,
99+ default_value : default_value ,
100+ evaluation_context : evaluation_context
101+ )
62102
63103 if TYPE_CLASS_MAP [ type ] . none? { |klass | resolution_details . value . is_a? ( klass ) }
64104 resolution_details . value = default_value
65105 resolution_details . error_code = Provider ::ErrorCode ::TYPE_MISMATCH
66106 resolution_details . reason = Provider ::Reason ::ERROR
67107 end
68108
69- EvaluationDetails . new ( flag_key :, resolution_details :)
109+ EvaluationDetails . new ( flag_key : flag_key , resolution_details : resolution_details )
70110 end
71111
72112 def validate_default_value_type ( type , default_value )
0 commit comments