Module: LcpRuby::DynamicReferences::Resolver
- Defined in:
- lib/lcp_ruby/dynamic_references/resolver.rb
Overview
Single source of truth for resolving “magic-token” strings used in YAML/DSL metadata: record-rule scope ‘value:`, page `scope_context:`, condition `value:`, workflow `set_fields:`, and field `default:`.
Grammar (string-only, dotted notation):
current_user → context[:user]
current_user.<method> → context[:user].send(method) if respond_to?
record → context[:record]
record.<field> → context[:record].send(field) if respond_to?
current_date → Date.current
current_datetime → Time.current
current_year → Date.current.year
selection_id → context[:selection_id]
Runtime contract:
* Missing context object (nil user / nil record) → returns nil silently.
* Method/field present in token but missing on a non-nil object →
dev/test raises; production records + returns nil
(per the canonical 3-line idiom in `lib/lcp_ruby.rb#record_error`).
* Unknown root or shape → ParseError (programmer error — caller's bug).
Use ‘Validator.validate_token` at boot-time to catch typos in `current_user.<method>` against the configured user_class.
Defined Under Namespace
Classes: ParseError, ResolutionError
Constant Summary collapse
- MAX_DOT_DEPTH =
1- ROOT_TOKENS =
%w[ current_user record current_date current_datetime current_year selection_id ].freeze
- DOTTABLE_ROOTS =
Roots that take an optional .<method> suffix.
%w[current_user record].freeze
Class Method Summary collapse
-
.parse(token) ⇒ Hash
Pure parser — no context, no side effects.
-
.recognized?(value) ⇒ Boolean
Returns true iff the value is a recognized dynamic-reference string.
-
.resolve(token, context: {}) ⇒ Object?
Resolves a string token to its runtime value.
Class Method Details
.parse(token) ⇒ Hash
Pure parser — no context, no side effects. Used by Validator.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/lcp_ruby/dynamic_references/resolver.rb', line 66 def parse(token) unless token.is_a?(String) && !token.empty? raise ParseError, "dynamic reference must be a non-empty String, got #{token.inspect}" end # split(".", -1) preserves trailing empty segments so "current_user." # parses as ["current_user", ""] and falls into the invalid-method branch. segments = token.split(".", -1) root = segments.first unless ROOT_TOKENS.include?(root) raise ParseError, "unknown dynamic reference root '#{root}' in '#{token}' " \ "(expected one of: #{ROOT_TOKENS.join(', ')})" end if segments.size == 1 { root: root, method: nil } elsif segments.size == 2 unless DOTTABLE_ROOTS.include?(root) raise ParseError, "dynamic reference '#{token}' is not allowed: '#{root}' does not take a .<method> suffix" end method = segments[1] if method.empty? || !method.match?(/\A[a-z_][a-z0-9_]*\z/) raise ParseError, "invalid method name '#{method}' in dynamic reference '#{token}'" end { root: root, method: method } else raise ParseError, "dynamic reference '#{token}' exceeds maximum dot-depth of #{MAX_DOT_DEPTH}" end end |
.recognized?(value) ⇒ Boolean
Returns true iff the value is a recognized dynamic-reference string. Useful for callers that mix literals and dynamic refs in the same slot.
101 102 103 104 105 106 107 |
# File 'lib/lcp_ruby/dynamic_references/resolver.rb', line 101 def recognized?(value) return false unless value.is_a?(String) parse(value) true rescue ParseError false end |
.resolve(token, context: {}) ⇒ Object?
Resolves a string token to its runtime value.
56 57 58 59 |
# File 'lib/lcp_ruby/dynamic_references/resolver.rb', line 56 def resolve(token, context: {}) parsed = parse(token) dispatch(parsed, context) end |