-
Notifications
You must be signed in to change notification settings - Fork 0
Barn 77 allow context overrides in helper methods #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
278c40b
c271a17
b560c84
5b61af8
cd43b78
1074915
ddcc955
8522639
4f58e13
4e522b8
34f4af4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| PATH | ||
| remote: . | ||
| specs: | ||
| parameter_substitution (2.1.0) | ||
| parameter_substitution (3.0.0) | ||
| activesupport (>= 6.0) | ||
| builder (~> 3.2) | ||
| invoca-utils (~> 0.3) | ||
|
|
@@ -22,7 +22,7 @@ | |
| tzinfo (~> 2.0) | ||
| appraisal (2.5.0) | ||
| bundler | ||
| rake | ||
|
Check failure on line 25 in Gemfile.lock
|
||
| thor (>= 0.14.0) | ||
| appraisal-matrix (0.2.0) | ||
| appraisal (~> 2.2) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| # See lib/parameter_substitution/readme.md | ||
|
|
||
| require 'active_support/all' | ||
| require 'set' | ||
| require "parameter_substitution/context" | ||
| require "parameter_substitution/parse_error" | ||
| require "parameter_substitution/parser" | ||
|
|
@@ -63,27 +64,65 @@ def configure | |
| ParameterSubstitution.config = config | ||
| end | ||
|
|
||
| def find_tokens(string_with_tokens, mapping: {}, parameter_start: "<", parameter_end: ">") | ||
| parse_expression(context_from_string(string_with_tokens, mapping, parameter_start: parameter_start, parameter_end: parameter_end)).substitution_parameter_names | ||
| def find_tokens(string_with_tokens, mapping: {}, context_overrides: {}) | ||
| context = build_context(string_with_tokens, mapping, context_overrides) | ||
| parse_expression(context).substitution_parameter_names | ||
| end | ||
|
|
||
| def find_formatters(string_with_tokens, mapping: {}, parameter_start: "<", parameter_end: ">") | ||
| parse_expression(context_from_string(string_with_tokens, mapping, parameter_start: parameter_start, parameter_end: parameter_end)).method_names | ||
| def find_formatters(string_with_tokens, mapping: {}, context_overrides: {}) | ||
| context = build_context(string_with_tokens, mapping, context_overrides) | ||
| parse_expression(context).method_names | ||
| end | ||
|
|
||
| def find_warnings(string_with_tokens, mapping: {}, parameter_start: "<", parameter_end: ">") | ||
| parse_expression(context_from_string(string_with_tokens, mapping, parameter_start: parameter_start, parameter_end: parameter_end)).parameter_and_method_warnings || [] | ||
| def find_warnings(string_with_tokens, mapping: {}, context_overrides: {}) | ||
| context = build_context(string_with_tokens, mapping, context_overrides) | ||
| parse_expression(context).parameter_and_method_warnings || [] | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def context_from_string(string_with_tokens, mapping, parameter_start: "<", parameter_end: ">") | ||
| ParameterSubstitution::Context.new( | ||
| VALID_CONTEXT_OVERRIDE_KEYS = %i[ | ||
| required_parameters | ||
| parameter_start | ||
| parameter_end | ||
| destination_encoding | ||
| allow_unknown_replacement_parameters | ||
| allow_nil | ||
| allow_unmatched_parameter_end | ||
| ].to_set | ||
| private_constant :VALID_CONTEXT_OVERRIDE_KEYS | ||
|
|
||
| # Build context with optional overrides | ||
| # @param [String] string_with_tokens The input string containing tokens | ||
| # @param [Hash] mapping The mapping of parameters to values | ||
| # @param [Hash] context_overrides Optional overrides for context attributes | ||
| # @return [ParameterSubstitution::Context] The constructed context | ||
| # @raise [ArgumentError] if context_overrides contains invalid keys | ||
| def build_context(string_with_tokens, mapping, context_overrides) | ||
| validate_context_overrides!(context_overrides) | ||
|
|
||
| base_options = { | ||
| input: string_with_tokens, | ||
| mapping:, | ||
| parameter_start:, | ||
| parameter_end: | ||
| ) | ||
| mapping: mapping | ||
| } | ||
|
|
||
| symbolized_overrides = context_overrides.transform_keys(&:to_sym) | ||
|
|
||
| ParameterSubstitution::Context.new(**symbolized_overrides.merge(base_options)) | ||
| end | ||
|
|
||
| # @param [Hash] context_overrides The overrides to validate | ||
| # @raise [ArgumentError] if context_overrides contains invalid keys | ||
| def validate_context_overrides!(context_overrides) | ||
| return if context_overrides.empty? | ||
|
|
||
| invalid_keys = context_overrides.keys.reject { |key| VALID_CONTEXT_OVERRIDE_KEYS.include?(key.to_sym) } | ||
|
||
|
|
||
| if invalid_keys.any? | ||
| invalid_keys_list = invalid_keys.join(", ") | ||
| valid_keys_list = VALID_CONTEXT_OVERRIDE_KEYS.sort.join(", ") | ||
| raise ArgumentError, "Invalid context_overrides keys: #{invalid_keys_list}. Valid keys are: #{valid_keys_list}" | ||
| end | ||
| end | ||
|
|
||
| def parse_expression(context) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class ParameterSubstitution | ||
| VERSION = "2.1.0" | ||
| VERSION = "3.0.0" | ||
| end |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -73,6 +73,44 @@ def self.find(_key); end | |||||||||||||||||||||||||||||||||||||||||||||
| let(:expression) { "<call.start_time.blank_if_nil><do_a_barrel_roll.downcase>" } | ||||||||||||||||||||||||||||||||||||||||||||||
| let(:mapping) { { 'call.start_time' => 'hello' } } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| shared_examples "passes context_overrides with symbolized keys to Context" do | ||||||||||||||||||||||||||||||||||||||||||||||
| it "calls Context.new with the expected overrides" do | ||||||||||||||||||||||||||||||||||||||||||||||
| allow(ParameterSubstitution::Context).to receive(:new).and_call_original | ||||||||||||||||||||||||||||||||||||||||||||||
| subject | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(ParameterSubstitution::Context).to have_received(:new).once.with( | ||||||||||||||||||||||||||||||||||||||||||||||
| hash_including(test_context_overrides.transform_keys(&:to_sym).merge( | ||||||||||||||||||||||||||||||||||||||||||||||
| input: test_expression, | ||||||||||||||||||||||||||||||||||||||||||||||
| mapping: test_mapping | ||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| shared_examples "validates context_overrides" do | ||||||||||||||||||||||||||||||||||||||||||||||
| context 'when context_overrides attempts to override base options' do | ||||||||||||||||||||||||||||||||||||||||||||||
| context "when input is provided in context_overrides" do | ||||||||||||||||||||||||||||||||||||||||||||||
| let(:test_context_overrides) { { input: "<different>" } } | ||||||||||||||||||||||||||||||||||||||||||||||
| it "raises error" do | ||||||||||||||||||||||||||||||||||||||||||||||
| expect { subject }.to raise_error(ArgumentError, /Invalid context_overrides keys: input/) | ||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| context "when mapping is provided in context_overrides" do | ||||||||||||||||||||||||||||||||||||||||||||||
| let(:test_context_overrides) { { mapping: { 'different' => 'value' } } } | ||||||||||||||||||||||||||||||||||||||||||||||
| it "raises error" do | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+90
to
+100
|
||||||||||||||||||||||||||||||||||||||||||||||
| context 'when context_overrides attempts to override base options' do | |
| context "when input is provided in context_overrides" do | |
| let(:test_context_overrides) { { input: "<different>" } } | |
| it "raises error" do | |
| expect { subject }.to raise_error(ArgumentError, /Invalid context_overrides keys: input/) | |
| end | |
| end | |
| context "when mapping is provided in context_overrides" do | |
| let(:test_context_overrides) { { mapping: { 'different' => 'value' } } } | |
| it "raises error" do | |
| context 'when context_overrides contains disallowed keys' do | |
| context "when input is provided in context_overrides" do | |
| let(:test_context_overrides) { { input: "<different>" } } | |
| it "raises error for invalid key" do | |
| expect { subject }.to raise_error(ArgumentError, /Invalid context_overrides keys: input/) | |
| end | |
| end | |
| context "when mapping is provided in context_overrides" do | |
| let(:test_context_overrides) { { mapping: { 'different' => 'value' } } } | |
| it "raises error for invalid key" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cgaroutte Nice use of shared examples here!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation allows 'input' and 'mapping' keys to pass through if they're present in context_overrides, but these should be rejected since they're provided as base_options. Add explicit validation to reject these reserved keys before checking against VALID_CONTEXT_OVERRIDE_KEYS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 They aren't included in VALID_CONTEXT_OVERRIDE_KEYS though so they're going to be rejected regardless...I don't think the extra complexity is warranted here for a slightly more specific error message.