diff --git a/README.md b/README.md
index d57ea27..c9eb069 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,7 @@ class MyApp < Sinatra::Base
notes 'Simple route that gets an ID and returns a string'
produces 'text/plain'
status_codes [200]
+ parameter :id, description: 'some ID'
get '/:id' do |id|
"Received #{id}"
end
diff --git a/etc/css/base.css b/etc/css/base.css
index 546069a..15610a8 100644
--- a/etc/css/base.css
+++ b/etc/css/base.css
@@ -283,3 +283,20 @@ article.example
color:#555;
margin:1em;
}
+
+.required_parameter
+{
+ color:#a41e22;
+ font-size:0.7em;
+}
+
+.parameter_type
+{
+ font-size:0.8em;
+ font-weight:400;
+}
+
+.parameter_in
+{
+ color:#555;
+}
\ No newline at end of file
diff --git a/etc/partial.html.erb b/etc/partial.html.erb
index 9d98fcd..bd0bed7 100644
--- a/etc/partial.html.erb
+++ b/etc/partial.html.erb
@@ -48,12 +48,25 @@
Parameter |
+ Description |
- <% route[:parameters].each do |param| %>
+ <% route[:parameters].each do |param_name, param_options| %>
- <%= param %> |
+
+ <%= param_name %>
+ <% if param_options[:required] %>
+ *required
+ <% end %>
+ <% if param_options[:type] %>
+ <%= param_options[:type] %>
+ <% end %>
+ <% if param_options[:in] %>
+ (<%= param_options[:in] %>)
+ <% end %>
+ |
+ <%= param_options[:description] %> |
<% end %>
diff --git a/lib/doc_my_routes/doc/hash_helpers.rb b/lib/doc_my_routes/doc/hash_helpers.rb
new file mode 100644
index 0000000..8163701
--- /dev/null
+++ b/lib/doc_my_routes/doc/hash_helpers.rb
@@ -0,0 +1,13 @@
+# Define error classes
+module DocMyRoutes
+ module HashHelpers
+ def deep_merge(first, second)
+ merger = proc { |key, f, s| f.is_a?(Hash) && s.is_a?(Hash) ? f.merge(s, &merger) : s }
+ first.merge(second, &merger)
+ end
+
+ def array_to_hash_keys(arr, default_value = {})
+ {}.tap { |hash| arr.each { |key| hash[key] = default_value } }
+ end
+ end
+end
diff --git a/lib/doc_my_routes/doc/mixins/annotatable.rb b/lib/doc_my_routes/doc/mixins/annotatable.rb
index 299e02a..e77dc75 100644
--- a/lib/doc_my_routes/doc/mixins/annotatable.rb
+++ b/lib/doc_my_routes/doc/mixins/annotatable.rb
@@ -87,6 +87,10 @@ def notes_ref(value)
route_documentation.notes_ref = value
end
+ def parameter(value, options = {})
+ route_documentation.add_parameter(value, options)
+ end
+
private
def track_route(resource, verb, route_pattern, conditions)
diff --git a/lib/doc_my_routes/doc/route.rb b/lib/doc_my_routes/doc/route.rb
index c2c48df..ac996b4 100644
--- a/lib/doc_my_routes/doc/route.rb
+++ b/lib/doc_my_routes/doc/route.rb
@@ -1,6 +1,7 @@
# encoding: utf-8
require 'forwardable'
+require_relative 'hash_helpers'
module DocMyRoutes
# Simple object representing a route
@@ -23,11 +24,11 @@ def initialize(resource, verb, route_pattern, conditions, documentation)
end
def to_hash
- {
+ deep_merge({
http_method: verb,
parameters: param_info,
path: path
- }.merge(documentation.to_hash)
+ }, documentation.to_hash)
end
def path
@@ -60,13 +61,15 @@ def to_s
#
# Try to extract parameters from the route definition otherwise
def param_info
- if conditions[:parameters]
- conditions[:parameters]
- else
- route_pattern.split('/').map do |part|
- part.start_with?(':') ? part[1..-1].to_sym : nil
- end.compact
- end
+ path_parameters_array = route_pattern.split('/').map do |part|
+ part.start_with?(':') ? part[1..-1].to_sym : nil
+ end.compact
+
+ path_parameters = HashHelpers.array_to_hash_keys(path_parameters_array,
+ { in: :path, required: true })
+ condition_parameters = HashHelpers.array_to_hash_keys(conditions[:parameters] || [])
+
+ HashHelpers.deep_merge(condition_parameters, path_parameters)
end
end
end
diff --git a/lib/doc_my_routes/doc/route_documentation.rb b/lib/doc_my_routes/doc/route_documentation.rb
index efc084c..f5aadb5 100644
--- a/lib/doc_my_routes/doc/route_documentation.rb
+++ b/lib/doc_my_routes/doc/route_documentation.rb
@@ -3,12 +3,13 @@ module DocMyRoutes
class RouteDocumentation
attr_accessor :summary, :notes, :status_codes, :examples_regex, :hidden,
:produces, :notes_ref
- attr_reader :examples
+ attr_reader :examples, :parameters
def initialize
@status_codes = { 200 => DocMyRoutes::StatusCodeInfo::STATUS_CODES[200] }
@hidden = false
@produces = []
+ @parameters = {}
end
# A route documentation object MUST have a summary, otherwise is not
@@ -25,6 +26,7 @@ def to_hash
examples_regex: examples_regex,
produces: produces,
examples: examples,
+ parameters: parameters,
hidden: hidden?
}
end
@@ -33,6 +35,10 @@ def produces=(values)
@produces = values.flatten.compact
end
+ def add_parameter(name, options)
+ @parameters[name] = options
+ end
+
def status_codes=(route_status_codes)
@status_codes = Hash[route_status_codes.map do |code|
[code, DocMyRoutes::StatusCodeInfo::STATUS_CODES[code]]
diff --git a/lib/doc_my_routes/version.rb b/lib/doc_my_routes/version.rb
index 261c05b..f283fbe 100644
--- a/lib/doc_my_routes/version.rb
+++ b/lib/doc_my_routes/version.rb
@@ -1,4 +1,4 @@
# DocMyRoutes version
module DocMyRoutes
- VERSION = '0.11.1'
+ VERSION = '0.12.0'
end
diff --git a/spec/unit/doc_helpers_spec.rb b/spec/unit/doc_helpers_spec.rb
index 0c664e5..f00d1b2 100644
--- a/spec/unit/doc_helpers_spec.rb
+++ b/spec/unit/doc_helpers_spec.rb
@@ -11,6 +11,7 @@
DEFAULT_NOTES = 'Example notes'
DEFAULT_NOTES_FILE = 'etc/example_notes.txt'
DEFAULT_STATUS_CODES = [200, 401]
+ DEFAULT_PARAMETER_OPTIONS = { type: :integer, description: 'example parameter' }
def mock_notes_file(content, path = 'notes.txt')
mock_notes_path = File.expand_path(path)
@@ -25,6 +26,7 @@ def mock_notes_file(content, path = 'notes.txt')
doc.summary = DEFAULT_SUMMARY
doc.notes = DEFAULT_NOTES
doc.status_codes = DEFAULT_STATUS_CODES
+ doc.add_parameter(:example_id, DEFAULT_PARAMETER_OPTIONS)
doc
end
@@ -34,9 +36,7 @@ def mock_notes_file(content, path = 'notes.txt')
"Actual object #{actual} doesn't have documentation"
actual_docs = actual.documentation
- actual_docs.summary == expected.summary && \
- actual_docs.notes == expected.notes && \
- actual_docs.status_codes == expected.status_codes
+ actual_docs.to_hash == expected.to_hash
end
description { "The route #{actual} doesn't match #{expected}" }
end
@@ -63,7 +63,7 @@ def mock_notes_file(content, path = 'notes.txt')
DocMyRoutes::RouteCollection.routes.clear
end
- context 'with summary, notes and status codes for a GET verb' do
+ context 'with summary, notes, parameter and status codes for a GET verb' do
subject do
key = DocMyRoutes::RouteCollection.routes.keys[0]
DocMyRoutes::RouteCollection.routes[key]
@@ -75,13 +75,16 @@ def doc_route
summary DEFAULT_SUMMARY
notes DEFAULT_NOTES
status_codes DEFAULT_STATUS_CODES
+ parameter :example_id, DEFAULT_PARAMETER_OPTIONS
get '/api/example' do
end
}
end
+
+ it_behaves_like 'a correctly tracked route'
end
- context 'with a summary, notes in an external file and status codes' do
+ context 'with a summary, notes in an external file, parameter and status codes' do
def doc_route
mock_notes_file(DEFAULT_NOTES, DEFAULT_NOTES_FILE)
# Test class with only two routes GET and HEAD
@@ -89,6 +92,7 @@ def doc_route
summary DEFAULT_SUMMARY
notes_ref DEFAULT_NOTES_FILE
status_codes DEFAULT_STATUS_CODES
+ parameter :example_id, DEFAULT_PARAMETER_OPTIONS
post '/api/example_notes_file' do
end
}