apipony 0.0.4 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +205 -3
- data/Rakefile +3 -7
- data/app/assets/stylesheets/apipony/styles.scss +153 -27
- data/app/views/apipony/application/_attribute.html.erb +42 -0
- data/app/views/apipony/application/_footer.html.erb +8 -0
- data/app/views/apipony/application/_header.html.erb +8 -0
- data/app/views/apipony/application/_method.html.erb +1 -0
- data/app/views/apipony/application/_request.html.erb +37 -0
- data/app/views/apipony/application/_response.html.erb +30 -0
- data/app/views/apipony/application/_sidebar.html.erb +16 -0
- data/app/views/apipony/application/index.html.erb +60 -0
- data/app/views/layouts/apipony/application.html.erb +22 -0
- data/lib/apipony.rb +19 -0
- data/lib/apipony/documentation.rb +24 -0
- data/lib/apipony/endpoint.rb +24 -3
- data/lib/apipony/engine.rb +2 -0
- data/lib/apipony/example_response.rb +9 -0
- data/lib/apipony/parameter.rb +3 -2
- data/lib/apipony/request.rb +17 -4
- data/lib/apipony/response.rb +47 -5
- data/lib/apipony/response_attribute.rb +130 -0
- data/lib/apipony/section.rb +11 -2
- data/lib/apipony/version.rb +3 -1
- metadata +42 -19
- data/app/views/apipony/application/index.html.haml +0 -74
- data/app/views/layouts/apipony/application.html.haml +0 -12
- data/config/initializers/haml.rb +0 -1
@@ -0,0 +1,42 @@
|
|
1
|
+
<li class="attribute-container">
|
2
|
+
<div class="attribute">
|
3
|
+
<div class="attribute-name name">
|
4
|
+
<%= attribute.name %>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="attribute-type type">
|
8
|
+
<% if attribute.is_subtype? %>
|
9
|
+
<%= link_to root_path(:anchor => "subtype-#{attribute.type}") do %>
|
10
|
+
<%= attribute.type %>
|
11
|
+
<% end %>
|
12
|
+
<% else %>
|
13
|
+
<%= attribute.type %>
|
14
|
+
<% end %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<% if attribute.description %>
|
18
|
+
<div class="attribute-description"><%= attribute.description %></div>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<% if attribute.is_enum? %>
|
23
|
+
<ul class="attribute-enum-list">
|
24
|
+
<div class="title">Possible values</div>
|
25
|
+
<% attribute.choices.each do |choice| %>
|
26
|
+
<li class="attribute-enum-member">
|
27
|
+
<div class="attribute-enum-member-name"><%= choice.name %></div>
|
28
|
+
<div class="attribute-enum-member-description"><%= choice.description %></div>
|
29
|
+
</li>
|
30
|
+
<% end %>
|
31
|
+
</ul>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<% if attribute.is_object? %>
|
35
|
+
<ul class="attribute-object-list">
|
36
|
+
<div class="title">Child attributes</div>
|
37
|
+
<%= render partial: "attribute",
|
38
|
+
collection: attribute.attributes,
|
39
|
+
as: :attribute %>
|
40
|
+
</ul>
|
41
|
+
<% end %>
|
42
|
+
</li>
|
@@ -0,0 +1 @@
|
|
1
|
+
<span class="method <%= endpoint.method %>"><%= endpoint.method %></span><%= endpoint.url %>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<div class="request">
|
2
|
+
<h4>Request:</h4>
|
3
|
+
<% if endpoint.request.params %>
|
4
|
+
<div class="panel">
|
5
|
+
<div class="title">Parameters</div>
|
6
|
+
<table class="table">
|
7
|
+
<% endpoint.request.params.each do |param| %>
|
8
|
+
<tr class="parameters">
|
9
|
+
<td class="name">
|
10
|
+
<%= param.name %>
|
11
|
+
</td>
|
12
|
+
<td class="type">
|
13
|
+
<%= param.type %>
|
14
|
+
</td>
|
15
|
+
<td class="required">
|
16
|
+
<% if param.required %>
|
17
|
+
<i class="fa fa-check-square-o" title="Required"></i>
|
18
|
+
<% else %>
|
19
|
+
<i class="fa fa-square-o" title="Optional"></i>
|
20
|
+
<% end %>
|
21
|
+
</td>
|
22
|
+
<td class="example">
|
23
|
+
<%= param.example %>
|
24
|
+
</td>
|
25
|
+
</tr>
|
26
|
+
<% end %>
|
27
|
+
</table>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
<% if endpoint.request.headers %>
|
32
|
+
<div class="panel">
|
33
|
+
<div class="title">Headers</div>
|
34
|
+
<pre class="code"><%= JSON.pretty_generate(endpoint.request.headers) %></pre>
|
35
|
+
</div>
|
36
|
+
<% end %>
|
37
|
+
</div>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<div class="response">
|
2
|
+
<h4>Response: <%= response.status %></h4>
|
3
|
+
<% if response.example %>
|
4
|
+
<div class="response-example">
|
5
|
+
<%# This is kind of fancy for no reason but whatever %>
|
6
|
+
<% if response.example.headers %>
|
7
|
+
<div class="title">Headers</div>
|
8
|
+
<pre class="code hljs json"><%= JSON.pretty_generate(response.example.headers) %></pre>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<% if response.example.body %>
|
12
|
+
<div class="panel">
|
13
|
+
<div class="title">Body</div>
|
14
|
+
<pre class="code hljs json"><%= JSON.pretty_generate(response.example.body) %></pre>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if response.attributes.size > 0 %>
|
21
|
+
<div class="panel">
|
22
|
+
<div class="title">Attributes</div>
|
23
|
+
<div class="table">
|
24
|
+
<div class="attributes">
|
25
|
+
<%= render partial: 'attribute', collection: response.attributes, as: :attribute %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<% @documentation.sections.each do |section| %>
|
2
|
+
<h4>
|
3
|
+
<%= link_to section.title, root_path(:anchor => section.title) %>
|
4
|
+
</h4>
|
5
|
+
<ul>
|
6
|
+
<% section.endpoints.each do |endpoint| %>
|
7
|
+
<li>
|
8
|
+
<h5>
|
9
|
+
<%= link_to root_path(:anchor => endpoint.id) do %>
|
10
|
+
<span class="method <%= endpoint.method %>"><%= endpoint.method %></span><%= endpoint.url %>
|
11
|
+
<% end %>
|
12
|
+
</h5>
|
13
|
+
</li>
|
14
|
+
<% end %>
|
15
|
+
</ul>
|
16
|
+
<% end %>
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<%= render partial: "header" %>
|
2
|
+
|
3
|
+
<main>
|
4
|
+
<div class="container">
|
5
|
+
<div class="row">
|
6
|
+
<div class="sidebar">
|
7
|
+
<%= render partial: "sidebar" %>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="main-col">
|
11
|
+
<% @documentation.sections.each do |section| %>
|
12
|
+
<div class="section">
|
13
|
+
<h2 id="<%= section.title %>"><%= section.title %></h2>
|
14
|
+
<% section.endpoints.each do |endpoint| %>
|
15
|
+
<div class="endpoint" id="<%= endpoint.id %>">
|
16
|
+
<h3><%= render "method", endpoint: endpoint %></h3>
|
17
|
+
|
18
|
+
<% if endpoint.description %>
|
19
|
+
<div class="description">
|
20
|
+
<%= endpoint.description %>
|
21
|
+
</div>
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
<% if endpoint.request || endpoint.response %>
|
25
|
+
<% if endpoint.request %>
|
26
|
+
<%= render "request", endpoint: endpoint %>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
<% if endpoint.response %>
|
30
|
+
<%= render "response", response: endpoint.response %>
|
31
|
+
<% end %>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
|
+
<% end %>
|
35
|
+
</div>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<div id="defined_subtypes">
|
39
|
+
<% @documentation.subtypes.each do |name, type| %>
|
40
|
+
<div class="subtype" id="subtype-<%= name %>">
|
41
|
+
<h4><%= name %></h4>
|
42
|
+
<div class="panel">
|
43
|
+
<div class="title">Attributes</div>
|
44
|
+
<div class="table">
|
45
|
+
<div class="attributes">
|
46
|
+
<%= render partial: "attribute",
|
47
|
+
collection: type.attributes,
|
48
|
+
as: :attribute %>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
<% end %>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
</main>
|
59
|
+
|
60
|
+
<%= render partial: "footer" %>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<%# UTF-8 Character Set %>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
|
6
|
+
<%# HTML Title %>
|
7
|
+
<title><%= @documentation.title %></title>
|
8
|
+
|
9
|
+
<%# Viewport meta tag %>
|
10
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
11
|
+
|
12
|
+
<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/github.min.css'>
|
13
|
+
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'>
|
14
|
+
<%= stylesheet_link_tag 'apipony/application', media: 'all' %>
|
15
|
+
<script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js'></script>
|
16
|
+
<%= javascript_include_tag 'apipony/application' %>
|
17
|
+
<%= csrf_meta_tags %>
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<%= yield %>
|
21
|
+
</body>
|
22
|
+
</html>
|
data/lib/apipony.rb
CHANGED
@@ -7,6 +7,25 @@ require 'apipony/endpoint'
|
|
7
7
|
require 'apipony/response'
|
8
8
|
require 'apipony/request'
|
9
9
|
require 'apipony/parameter'
|
10
|
+
require 'apipony/response_attribute'
|
11
|
+
require 'apipony/example_response'
|
10
12
|
|
11
13
|
module Apipony
|
14
|
+
|
15
|
+
##
|
16
|
+
# This allows you to define a common sub-type of attributes.
|
17
|
+
# A typical example is something like a list of users. If you want to
|
18
|
+
# display all users who created an image, who subscribe to a channel, or who
|
19
|
+
# are in a group, it may be useful if the information on those users is in a
|
20
|
+
# common format. This lets you define one common format, which you can then
|
21
|
+
# merge in to other attributes.
|
22
|
+
# = Example
|
23
|
+
# Apipony.define_attribute_type :user_stub do
|
24
|
+
# attribute :name
|
25
|
+
# attribute :id
|
26
|
+
# end
|
27
|
+
def self.define_attribute_type(name, **params, &block)
|
28
|
+
a = Apipony::ResponseAttribute.new("", **params, &block)
|
29
|
+
Apipony::ResponseAttribute.define_type(name, a)
|
30
|
+
end
|
12
31
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
##
|
2
|
+
# Top-level class for the DSL
|
1
3
|
class Apipony::Documentation
|
2
4
|
class << self
|
3
5
|
attr_accessor :title, :base_url, :sections
|
@@ -9,11 +11,33 @@ class Apipony::Documentation
|
|
9
11
|
|
10
12
|
instance_eval(&block)
|
11
13
|
end
|
14
|
+
##
|
15
|
+
# @return [Hash<String, ApiPony::ResponseAttribute] a hash of each subype.
|
16
|
+
# keys are the names of the subtype, values are the attribute object that
|
17
|
+
# defines it
|
18
|
+
def subtypes
|
19
|
+
Apipony::ResponseAttribute.defined_subtypes
|
20
|
+
end
|
12
21
|
|
22
|
+
##
|
23
|
+
# Start a new section.
|
24
|
+
# Sections are logically separated on the display page.
|
13
25
|
def section(title, &block)
|
14
26
|
@sections << Apipony::Section.new(title, &block)
|
15
27
|
end
|
16
28
|
|
29
|
+
##
|
30
|
+
# Define a new subtype.
|
31
|
+
# A subtype describes a common object used in various places in your Api.
|
32
|
+
# Once defined, setting the `type` of an attribute to this given name will
|
33
|
+
# cause it to reference this subtype in a common location.
|
34
|
+
# @param [String] name what to call this subtype
|
35
|
+
def subtype(name, **params, &block)
|
36
|
+
Apipony.define_attribute_type(name, **params, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Configure API pony with the DSL
|
17
41
|
def config(&block)
|
18
42
|
instance_eval(&block)
|
19
43
|
end
|
data/lib/apipony/endpoint.rb
CHANGED
@@ -1,5 +1,20 @@
|
|
1
|
+
##
|
2
|
+
# Model a response endpoint.
|
1
3
|
class Apipony::Endpoint < Apipony::Base
|
2
|
-
|
4
|
+
##
|
5
|
+
# What HTTP verb to use to access this endpoint
|
6
|
+
attr_accessor :method
|
7
|
+
##
|
8
|
+
# The URl of this endpoint
|
9
|
+
attr_accessor :url
|
10
|
+
|
11
|
+
##
|
12
|
+
# A short description of what this endpoint does and why it may be useful.
|
13
|
+
attr_accessor :description
|
14
|
+
|
15
|
+
##
|
16
|
+
#:nodoc:
|
17
|
+
attr_accessor :response, :request
|
3
18
|
|
4
19
|
def initialize(method, url, &block)
|
5
20
|
@method = method
|
@@ -8,14 +23,20 @@ class Apipony::Endpoint < Apipony::Base
|
|
8
23
|
instance_eval(&block) if block_given?
|
9
24
|
end
|
10
25
|
|
11
|
-
|
12
|
-
|
26
|
+
##
|
27
|
+
# DSL method to start describing a response
|
28
|
+
def response_with(status, **params, &block)
|
29
|
+
@response = Apipony::Response.new(status, **params, &block)
|
13
30
|
end
|
14
31
|
|
32
|
+
##
|
33
|
+
# DSL method to start describind a request
|
15
34
|
def request_with(&block)
|
16
35
|
@request = Apipony::Request.new(&block)
|
17
36
|
end
|
18
37
|
|
38
|
+
##
|
39
|
+
# Create a unique identifier for this endpoint
|
19
40
|
def id
|
20
41
|
File.join(@method, @url)
|
21
42
|
end
|
data/lib/apipony/engine.rb
CHANGED
data/lib/apipony/parameter.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
class Apipony::Parameter < Apipony::Base
|
2
|
-
attr_accessor :name, :type, :example, :required
|
2
|
+
attr_accessor :name, :type, :example, :required, :description
|
3
3
|
|
4
|
-
def initialize(name, example, type, required)
|
4
|
+
def initialize(name, example, type, required, description)
|
5
5
|
@name = name
|
6
6
|
@example = example
|
7
7
|
@type = type
|
8
8
|
@required = required
|
9
|
+
@description = description
|
9
10
|
end
|
10
11
|
end
|
data/lib/apipony/request.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
|
+
##
|
2
|
+
# Model a request that an API user can make.
|
3
|
+
# Includes information about required parameters and required headers
|
1
4
|
class Apipony::Request < Apipony::Base
|
2
|
-
|
5
|
+
##
|
6
|
+
# :nodoc:
|
7
|
+
attr_accessor :params
|
8
|
+
##
|
9
|
+
#:nodoc:
|
10
|
+
attr_accessor :headers
|
3
11
|
|
4
12
|
def initialize(&block)
|
5
13
|
@params = []
|
6
14
|
|
7
15
|
instance_eval(&block)
|
8
16
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
17
|
+
##
|
18
|
+
# Construct a new parameter
|
19
|
+
def param(name,
|
20
|
+
example: '',
|
21
|
+
type: :string,
|
22
|
+
required: false,
|
23
|
+
description: '')
|
24
|
+
@params << Apipony::Parameter.new(name, example, type, required, description)
|
12
25
|
end
|
13
26
|
end
|
data/lib/apipony/response.rb
CHANGED
@@ -1,9 +1,51 @@
|
|
1
|
-
class Apipony::Response
|
2
|
-
attr_accessor :
|
3
|
-
|
4
|
-
def initialize(status, &block)
|
1
|
+
class Apipony::Response
|
2
|
+
attr_accessor :example, :attributes, :status
|
3
|
+
def initialize(status, array: false, &block)
|
5
4
|
@status = status
|
6
|
-
|
5
|
+
@attributes = []
|
6
|
+
@array = array
|
7
7
|
instance_eval(&block) if block_given?
|
8
8
|
end
|
9
|
+
|
10
|
+
def is_array?
|
11
|
+
!! @array
|
12
|
+
end
|
13
|
+
|
14
|
+
def example(&block)
|
15
|
+
if block_given?
|
16
|
+
@example = Apipony::ExampleResponse.new(&block)
|
17
|
+
else
|
18
|
+
find_example
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def attribute(name, **params, &block)
|
23
|
+
if params[:example]
|
24
|
+
@use_attribute_examples = true
|
25
|
+
end
|
26
|
+
@attributes << Apipony::ResponseAttribute.new(name, **params, &block)
|
27
|
+
end
|
28
|
+
private
|
29
|
+
def find_example
|
30
|
+
if @use_attribute_examples
|
31
|
+
build_example_from_attributes
|
32
|
+
end
|
33
|
+
@example
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_example_from_attributes
|
37
|
+
build = Hash.new
|
38
|
+
@attributes.each do |attr|
|
39
|
+
build[attr.name] = attr.example if attr.example
|
40
|
+
end
|
41
|
+
@example ||= Apipony::ExampleResponse.new
|
42
|
+
case @example.body
|
43
|
+
when Hash
|
44
|
+
@example.body.merge! build
|
45
|
+
@example.body = [@example.body] if is_array?
|
46
|
+
when NilClass
|
47
|
+
@example.body = (is_array? ? [build] : build)
|
48
|
+
end
|
49
|
+
@example
|
50
|
+
end
|
9
51
|
end
|