Skip to content

Commit af45b1d

Browse files
authored
Merge pull request #1674 from glaucocustodio/implement-as
Add parameter alias
2 parents 12af441 + 1bd516d commit af45b1d

File tree

9 files changed

+94
-10
lines changed

9 files changed

+94
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Your contribution here.
66
* [#1652](https://github.com/ruby-grape/grape/pull/1652): Add the original exception to the error_formatter the original exception - [@dcsg](https://github.com/dcsg).
77
* [#1665](https://github.com/ruby-grape/grape/pull/1665): Make helpers available in subclasses - [@pablonahuelgomez](https://github.com/pablonahuelgomez).
8+
* [#1674](https://github.com/ruby-grape/grape/pull/1674): Add parameter alias (`as`) - [@glaucocustodio](https://github.com/glaucocustodio).
89

910
#### Fixes
1011

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
- [Validation of Nested Parameters](#validation-of-nested-parameters)
4343
- [Dependent Parameters](#dependent-parameters)
4444
- [Group Options](#group-options)
45+
- [Alias](#alias)
4546
- [Built-in Validators](#built-in-validators)
4647
- [Namespace Validation and Coercion](#namespace-validation-and-coercion)
4748
- [Custom Validators](#custom-validators)
@@ -1094,6 +1095,23 @@ params do
10941095
end
10951096
```
10961097

1098+
### Alias
1099+
1100+
You can set an alias for parameters using `as`, which can be useful when refactoring existing APIs:
1101+
1102+
```ruby
1103+
resource :users do
1104+
params do
1105+
requires :email_address, as: :email
1106+
requires :password
1107+
end
1108+
post do
1109+
User.create!(declared(params)) # User takes email and password
1110+
end
1111+
end
1112+
```
1113+
1114+
The value passed to `as` will be the key when calling `params` or `declared(params)`.
10971115

10981116
### Built-in Validators
10991117

lib/grape.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
require 'active_support/core_ext/hash/deep_merge'
1414
require 'active_support/core_ext/hash/reverse_merge'
1515
require 'active_support/core_ext/hash/except'
16+
require 'active_support/core_ext/hash/slice'
1617
require 'active_support/core_ext/hash/conversions'
1718
require 'active_support/dependencies/autoload'
1819
require 'active_support/notifications'
@@ -198,6 +199,7 @@ module ServeFile
198199
require 'grape/validations/validators/base'
199200
require 'grape/validations/attributes_iterator'
200201
require 'grape/validations/validators/allow_blank'
202+
require 'grape/validations/validators/as'
201203
require 'grape/validations/validators/at_least_one_of'
202204
require 'grape/validations/validators/coerce'
203205
require 'grape/validations/validators/default'

lib/grape/dsl/inside_route.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,16 @@ def declared_hash(passed_params, options, declared_params)
5050
# If it is not a Hash then it does not have children.
5151
# Find its value or set it to nil.
5252
if !declared_param.is_a?(Hash)
53-
next unless options[:include_missing] || passed_params.key?(declared_param)
54-
memo[optioned_param_key(declared_param, options)] = passed_params[declared_param]
53+
has_alias = route_setting(:aliased_params) && route_setting(:aliased_params).find { |current| current[declared_param] }
54+
param_alias = has_alias[declared_param] if has_alias
55+
56+
next unless options[:include_missing] || passed_params.key?(declared_param) || param_alias
57+
58+
if param_alias
59+
memo[optioned_param_key(param_alias, options)] = passed_params[param_alias]
60+
else
61+
memo[optioned_param_key(declared_param, options)] = passed_params[declared_param]
62+
end
5563
else
5664
declared_param.each_pair do |declared_parent_param, declared_children_params|
5765
next unless options[:include_missing] || passed_params.key?(declared_parent_param)

lib/grape/dsl/parameters.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ def requires(*attrs, &block)
131131
require_required_and_optional_fields(attrs.first, opts)
132132
else
133133
validate_attributes(attrs, opts, &block)
134-
135-
block_given? ? new_scope(orig_attrs, &block) : push_declared_params(attrs)
134+
block_given? ? new_scope(orig_attrs, &block) : push_declared_params(attrs, opts.slice(:as))
136135
end
137136
end
138137

@@ -158,7 +157,7 @@ def optional(*attrs, &block)
158157
else
159158
validate_attributes(attrs, opts, &block)
160159

161-
block_given? ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs)
160+
block_given? ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, opts.slice(:as))
162161
end
163162
end
164163

lib/grape/validations/params_scope.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,15 @@ def required?
115115

116116
# Adds a parameter declaration to our list of validations.
117117
# @param attrs [Array] (see Grape::DSL::Parameters#requires)
118-
def push_declared_params(attrs)
118+
def push_declared_params(attrs, **opts)
119119
if lateral?
120120
@parent.push_declared_params(attrs)
121121
else
122+
if opts && opts[:as]
123+
@api.route_setting(:aliased_params, @api.route_setting(:aliased_params) || [])
124+
@api.route_setting(:aliased_params) << { attrs.first => opts[:as] }
125+
end
126+
122127
@declared_params.concat attrs
123128
end
124129
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module Grape
2+
module Validations
3+
class AsValidator < Base
4+
def initialize(attrs, options, required, scope, opts = {})
5+
@alias = options
6+
super
7+
end
8+
9+
def validate_param!(attr_name, params)
10+
params[@alias] = params[attr_name]
11+
params.delete(attr_name)
12+
end
13+
end
14+
end
15+
end

spec/grape/dsl/parameters_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def validate_attributes_reader
1515
@validate_attributes
1616
end
1717

18-
def push_declared_params(*args)
18+
def push_declared_params(args, **_opts)
1919
@push_declared_params = args
2020
end
2121

@@ -84,7 +84,7 @@ def extract_message_option(attrs)
8484
subject.requires :id, type: Integer, desc: 'Identity.'
8585

8686
expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.', presence: { value: true, message: nil } }])
87-
expect(subject.push_declared_params_reader).to eq([[:id]])
87+
expect(subject.push_declared_params_reader).to eq([:id])
8888
end
8989
end
9090

@@ -93,7 +93,7 @@ def extract_message_option(attrs)
9393
subject.optional :id, type: Integer, desc: 'Identity.'
9494

9595
expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.' }])
96-
expect(subject.push_declared_params_reader).to eq([[:id]])
96+
expect(subject.push_declared_params_reader).to eq([:id])
9797
end
9898
end
9999

@@ -102,7 +102,7 @@ def extract_message_option(attrs)
102102
subject.with(type: Integer) { subject.optional :id, desc: 'Identity.' }
103103

104104
expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.' }])
105-
expect(subject.push_declared_params_reader).to eq([[:id]])
105+
expect(subject.push_declared_params_reader).to eq([:id])
106106
end
107107
end
108108

spec/grape/validations/params_scope_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,42 @@ def initialize(value)
121121
end
122122
end
123123

124+
context 'param alias' do
125+
it do
126+
subject.params do
127+
requires :foo, as: :bar
128+
optional :super, as: :hiper
129+
end
130+
subject.get('/alias') { "#{declared(params)['bar']}-#{declared(params)['hiper']}" }
131+
get '/alias', foo: 'any', super: 'any2'
132+
133+
expect(last_response.status).to eq(200)
134+
expect(last_response.body).to eq('any-any2')
135+
end
136+
137+
it do
138+
subject.params do
139+
requires :foo, as: :bar, type: String, coerce_with: ->(c) { c.strip }
140+
end
141+
subject.get('/alias-coerced') { "#{params['bar']}-#{params['foo']}" }
142+
get '/alias-coerced', foo: ' there we go '
143+
144+
expect(last_response.status).to eq(200)
145+
expect(last_response.body).to eq('there we go-')
146+
end
147+
148+
it do
149+
subject.params do
150+
requires :foo, as: :bar, allow_blank: false
151+
end
152+
subject.get('/alias-not-blank') {}
153+
get '/alias-not-blank', foo: ''
154+
155+
expect(last_response.status).to eq(400)
156+
expect(last_response.body).to eq('foo is empty')
157+
end
158+
end
159+
124160
context 'array without coerce type explicitly given' do
125161
it 'sets the type based on first element' do
126162
subject.params do

0 commit comments

Comments
 (0)