-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Refactor: Extract declared method into separate DSL module #2637
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
Open
ericproulx
wants to merge
1
commit into
master
Choose a base branch
from
refactor/declared_availability
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+153
−144
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Grape | ||
| module DSL | ||
| module Declared | ||
| # Denotes a situation where a DSL method has been invoked in a | ||
| # filter which it should not yet be available in | ||
| class MethodNotYetAvailable < StandardError | ||
| def initialize(msg = '#declared is not available prior to parameter validation') | ||
| super | ||
| end | ||
| end | ||
|
|
||
| # A filtering method that will return a hash | ||
| # consisting only of keys that have been declared by a | ||
| # `params` statement against the current/target endpoint or parent | ||
| # namespaces. | ||
| # @param params [Hash] The initial hash to filter. Usually this will just be `params` | ||
| # @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces` | ||
| # options. `:include_parent_namespaces` defaults to true, hence must be set to false if | ||
| # you want only to return params declared against the current/target endpoint. | ||
| def declared(passed_params, options = {}, declared_params = nil, params_nested_path = []) | ||
| raise MethodNotYetAvailable unless before_filter_passed | ||
|
|
||
| options.reverse_merge!(include_missing: true, include_parent_namespaces: true, evaluate_given: false) | ||
| declared_params ||= optioned_declared_params(options[:include_parent_namespaces]) | ||
|
|
||
| res = if passed_params.is_a?(Array) | ||
| declared_array(passed_params, options, declared_params, params_nested_path) | ||
| else | ||
| declared_hash(passed_params, options, declared_params, params_nested_path) | ||
| end | ||
|
|
||
| if (key_maps = inheritable_setting.namespace_stackable[:contract_key_map]) | ||
| key_maps.each { |key_map| key_map.write(passed_params, res) } | ||
| end | ||
|
|
||
| res | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def declared_array(passed_params, options, declared_params, params_nested_path) | ||
| passed_params.map do |passed_param| | ||
| declared(passed_param || {}, options, declared_params, params_nested_path) | ||
| end | ||
| end | ||
|
|
||
| def declared_hash(passed_params, options, declared_params, params_nested_path) | ||
| declared_params.each_with_object(passed_params.class.new) do |declared_param_attr, memo| | ||
| next if options[:evaluate_given] && !declared_param_attr.scope.attr_meets_dependency?(passed_params) | ||
|
|
||
| declared_hash_attr(passed_params, options, declared_param_attr.key, params_nested_path, memo) | ||
| end | ||
| end | ||
|
|
||
| def declared_hash_attr(passed_params, options, declared_param, params_nested_path, memo) | ||
| renamed_params = inheritable_setting.route[:renamed_params] || {} | ||
| if declared_param.is_a?(Hash) | ||
| declared_param.each_pair do |declared_parent_param, declared_children_params| | ||
| params_nested_path_dup = params_nested_path.dup | ||
| params_nested_path_dup << declared_parent_param.to_s | ||
| next unless options[:include_missing] || passed_params.key?(declared_parent_param) | ||
|
|
||
| rename_path = params_nested_path + [declared_parent_param.to_s] | ||
| renamed_param_name = renamed_params[rename_path] | ||
|
|
||
| memo_key = optioned_param_key(renamed_param_name || declared_parent_param, options) | ||
| passed_children_params = passed_params[declared_parent_param] || passed_params.class.new | ||
|
|
||
| memo[memo_key] = handle_passed_param(params_nested_path_dup, has_passed_children: passed_children_params.any?) do | ||
| declared(passed_children_params, options, declared_children_params, params_nested_path_dup) | ||
| end | ||
| end | ||
| else | ||
| # If it is not a Hash then it does not have children. | ||
| # Find its value or set it to nil. | ||
| return unless options[:include_missing] || passed_params.try(:key?, declared_param) | ||
|
|
||
| rename_path = params_nested_path + [declared_param.to_s] | ||
| renamed_param_name = renamed_params[rename_path] | ||
|
|
||
| memo_key = optioned_param_key(renamed_param_name || declared_param, options) | ||
| passed_param = passed_params[declared_param] | ||
|
|
||
| params_nested_path_dup = params_nested_path.dup | ||
| params_nested_path_dup << declared_param.to_s | ||
| memo[memo_key] = passed_param || handle_passed_param(params_nested_path_dup) do | ||
| passed_param | ||
| end | ||
| end | ||
| end | ||
|
|
||
| def handle_passed_param(params_nested_path, has_passed_children: false, &_block) | ||
| return yield if has_passed_children | ||
|
|
||
| key = params_nested_path[0] | ||
| key += "[#{params_nested_path[1..].join('][')}]" if params_nested_path.size > 1 | ||
|
|
||
| route_options_params = options[:route_options][:params] || {} | ||
| type = route_options_params.dig(key, :type) | ||
| has_children = route_options_params.keys.any? { |k| k != key && k.start_with?("#{key}[") } | ||
|
|
||
| if type == 'Hash' && !has_children | ||
| {} | ||
| elsif type == 'Array' || (type&.start_with?('[') && type.exclude?(',')) | ||
| [] | ||
| elsif type == 'Set' || type&.start_with?('#<Set') | ||
| Set.new | ||
| else | ||
| yield | ||
| end | ||
| end | ||
|
|
||
| def optioned_param_key(declared_param, options) | ||
| options[:stringify] ? declared_param.to_s : declared_param.to_sym | ||
| end | ||
|
|
||
| def optioned_declared_params(include_parent_namespaces) | ||
| declared_params = if include_parent_namespaces | ||
| # Declared params including parent namespaces | ||
| inheritable_setting.route[:declared_params] | ||
| else | ||
| # Declared params at current namespace | ||
| inheritable_setting.namespace_stackable[:declared_params].last || [] | ||
| end | ||
|
|
||
| raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params | ||
|
|
||
| declared_params | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Should we be tracking filters than ran more generically in
run_filters?Uh oh!
There was an error while loading. Please reload this page.
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.
It is tracked through instrumentation but feels over engineered for 1 case
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.
Still I don't like this function and I will revisit it. There's some pluralization and dynamic define_method. I think it could be simplified but I'll do it after this merge if you don't mind