Skip to content
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

Custom parameter method #312

Merged
merged 3 commits into from
Dec 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,12 @@ Special values:

* `:required => true` Will display a red '*' to show it's required
* `:scope => :the_scope` Will scope parameters in the hash, scoping can be nested. See example
* `:method => :method_name` Will use specified method as a parameter value

The value of scoped parameters can be set with both scoped (`let(:order_item_item_id)`) and unscoped (`let(:item_id)`) methods. It always searches for the scoped method first and falls back to the unscoped method.
Retrieving of parameter value goes through several steps:
1. if `method` option is defined and test case responds to this method then this method is used;
2. if test case responds to scoped method then this method is used;
3. overwise unscoped method is used.
Copy link
Contributor

@rafaelsales rafaelsales Jul 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oestrich @sineed This fallback won't work for some scenarios.

Example:

get '/foo' do
  parameter :status, "Blah", method: :status_param # this param is not required

  example 'meh' do
    expect(status).to eq 200
  end
end

Even before entering the example block, rspec api documentation calls status to prepare the request, because status_param is not defined. When it calls status, it fails with No response yet. Request a page first, because the status method is only supposed to be called after a request is done.

I don't see a reason to have this fallback mechanism. If the programmer specifies a different method name for a param, just use it if present, if not, do not use the param at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your example is working as intended. If instead the example is an example_request then we hit your failure, which does exist. Status is a weird method to override, and we've always had an issue with it.

The problem line seems like it would be this one. I will look at a PR if you want to fix it, otherwise I don't see why you would change the method and not define it.

Copy link
Contributor

@rafaelsales rafaelsales Jul 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oestrich The status was just an example, but this issue happens to other reserved words on the context of a test example as well. I created a PR: #352
Thanks


```ruby
resource "Orders" do
Expand All @@ -428,10 +432,13 @@ resource "Orders" do
post "/orders" do
parameter :name, "Order Name", :required => true, :scope => :order
parameter :item, "Order items", :scope => :order
parameter :item_id, "Item id", :scope => [:order, :item]
parameter :item_id, "Item id", :scope => [:order, :item], method: :custom_item_id

let(:name) { "My Order" } # OR let(:order_name) { "My Order" }
let(:item_id) { 1 } # OR let(:order_item_item_id) { 1 }
let(:name) { "My Order" }
# OR let(:order_name) { "My Order" }
let(:item_id) { 1 }
# OR let(:custom_item_id) { 1 }
# OR let(:order_item_item_id) { 1 }

example "Creating an order" do
params.should eq({
Expand Down
36 changes: 2 additions & 34 deletions lib/rspec_api_documentation/dsl/endpoint.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'rspec/core/formatters/base_formatter'
require 'rack/utils'
require 'rack/test/utils'
require 'rspec_api_documentation/dsl/endpoint/params'

module RspecApiDocumentation::DSL
# DSL methods available inside the RSpec example.
Expand Down Expand Up @@ -63,11 +64,7 @@ def query_string
end

def params
parameters = example.metadata.fetch(:parameters, {}).inject({}) do |hash, param|
set_param(hash, param)
end
parameters.deep_merge!(extra_params)
parameters
Params.new(self, example, extra_params).call
end

def header(name, value)
Expand Down Expand Up @@ -99,14 +96,6 @@ def status
rspec_api_documentation_client.status
end

def in_path?(param)
path_params.include?(param)
end

def path_params
example.metadata[:route].scan(/:(\w+)/).flatten
end

def path
example.metadata[:route].gsub(/:(\w+)/) do |match|
if extra_params.keys.include?($1)
Expand Down Expand Up @@ -146,26 +135,5 @@ def delete_extra_param(key)
@extra_params.delete(key.to_sym) || @extra_params.delete(key.to_s)
end

def set_param(hash, param)
key = param[:name]

keys = [param[:scope], key].flatten.compact
method_name = keys.join('_')

return hash if in_path?(method_name)

unless respond_to?(method_name)
method_name = key
return hash unless respond_to?(method_name)
end

hash.deep_merge(build_param_hash(keys, method_name))
end

def build_param_hash(keys, method_name)
value = keys[1] ? build_param_hash(keys[1..-1], method_name) : send(method_name)
{ keys[0].to_s => value }
end

end
end
30 changes: 30 additions & 0 deletions lib/rspec_api_documentation/dsl/endpoint/params.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require 'rspec_api_documentation/dsl/endpoint/set_param'

module RspecApiDocumentation
module DSL
module Endpoint
class Params
attr_reader :example_group, :example

def initialize(example_group, example, extra_params)
@example_group = example_group
@example = example
@extra_params = extra_params
end

def call
parameters = example.metadata.fetch(:parameters, {}).inject({}) do |hash, param|
SetParam.new(self, hash, param).call
end
parameters.deep_merge!(extra_params)
parameters
end

private

attr_reader :extra_params

end
end
end
end
62 changes: 62 additions & 0 deletions lib/rspec_api_documentation/dsl/endpoint/set_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
module RspecApiDocumentation
module DSL
module Endpoint
class SetParam
def initialize(parent, hash, param)
@parent = parent
@hash = hash
@param = param
end

def call
return hash if path_params.include?(path_name)
return hash unless method_name

hash.deep_merge build_param_hash(key_scope || [key])
end

private

attr_reader :parent, :hash, :param
delegate :example_group, :example, to: :parent

def key
@key ||= param[:name]
end

def key_scope
@key_scope ||= param[:scope] && Array(param[:scope]).dup.push(key)
end

def scoped_key
@scoped_key ||= key_scope && key_scope.join('_')
end

def custom_method_name
param[:method]
end

def path_name
scoped_key || key
end

def path_params
example.metadata[:route].scan(/:(\w+)/).flatten
end

def method_name
@method_name ||= begin
[custom_method_name, scoped_key, key].find do |name|
name && example_group.respond_to?(name)
end
end
end

def build_param_hash(keys)
value = keys[1] ? build_param_hash(keys[1..-1]) : example_group.send(method_name)
{ keys[0].to_s => value }
end
end
end
end
end
21 changes: 16 additions & 5 deletions spec/dsl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
post "/orders" do
parameter :type, "The type of drink you want.", :required => true
parameter :size, "The size of drink you want.", :required => true
parameter :note, "Any additional notes about your order."
parameter :name, :scope => :order
parameter :note, "Any additional notes about your order.", method: :custom_note
parameter :name, :scope => :order, method: :custom_order_name

response_field :type, "The type of drink you ordered.", :scope => :order
response_field :size, "The size of drink you ordered.", :scope => :order
Expand All @@ -71,6 +71,12 @@
let(:type) { "coffee" }
let(:size) { "medium" }

let(:note) { "Made in Brazil" }
let(:custom_note) { "Made in India" }

let(:order_name) { "Nescoffee" }
let(:custom_order_name) { "Jakobz" }

describe "example metadata" do
subject { |example| example.metadata }

Expand All @@ -79,8 +85,8 @@
[
{ :name => "type", :description => "The type of drink you want.", :required => true },
{ :name => "size", :description => "The size of drink you want.", :required => true },
{ :name => "note", :description => "Any additional notes about your order." },
{ :name => "name", :description => "Order name", :scope => :order},
{ :name => "note", :description => "Any additional notes about your order.", method: :custom_note },
{ :name => "name", :description => "Order name", :scope => :order, method: :custom_order_name },
]
)
end
Expand All @@ -103,7 +109,12 @@

describe "params" do
it "should equal the assigned parameter values" do
expect(params).to eq("type" => "coffee", "size" => "medium")
expect(params).to eq({
"type" => "coffee",
"size" => "medium",
"note" => "Made in India",
"order" => { "name" => "Jakobz" }
})
end
end
end
Expand Down