rails_api_explorer 0.0.1.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +23 -0
- data/app/assets/javascripts/api_explorer/application.js +15 -0
- data/app/assets/javascripts/api_explorer/explorer.js +146 -0
- data/app/assets/javascripts/api_explorer/scroll.js +38 -0
- data/app/assets/stylesheets/api_explorer/application.css +15 -0
- data/app/assets/stylesheets/api_explorer/explorer.css +70 -0
- data/app/controllers/api_explorer/application_controller.rb +5 -0
- data/app/controllers/api_explorer/explorer_controller.rb +9 -0
- data/app/helpers/api_explorer/application_helper.rb +4 -0
- data/app/helpers/api_explorer/explorer_helper.rb +4 -0
- data/app/views/api_explorer/explorer/_content_node.html.erb +12 -0
- data/app/views/api_explorer/explorer/_node.html.erb +17 -0
- data/app/views/api_explorer/explorer/_params.html.erb +14 -0
- data/app/views/api_explorer/explorer/_request.html.erb +39 -0
- data/app/views/api_explorer/explorer/_shared.html.erb +12 -0
- data/app/views/api_explorer/explorer/form/_headers.html.erb +25 -0
- data/app/views/api_explorer/explorer/form/_param.html.erb +28 -0
- data/app/views/api_explorer/explorer/form/_params.html.erb +7 -0
- data/app/views/api_explorer/explorer/form/_url_params.html.erb +13 -0
- data/app/views/api_explorer/explorer/index.html.erb +20 -0
- data/app/views/api_explorer/explorer/perform_request.html.erb +1 -0
- data/config/routes.rb +4 -0
- data/lib/api_explorer/description.rb +7 -0
- data/lib/api_explorer/dsl/base_proxy.rb +13 -0
- data/lib/api_explorer/dsl/description_proxy.rb +7 -0
- data/lib/api_explorer/dsl/group_proxy.rb +44 -0
- data/lib/api_explorer/dsl/request_proxy.rb +41 -0
- data/lib/api_explorer/dsl.rb +4 -0
- data/lib/api_explorer/engine.rb +5 -0
- data/lib/api_explorer/group.rb +18 -0
- data/lib/api_explorer/header.rb +10 -0
- data/lib/api_explorer/node.rb +30 -0
- data/lib/api_explorer/parameter.rb +16 -0
- data/lib/api_explorer/request.rb +37 -0
- data/lib/api_explorer/version.rb +3 -0
- data/lib/api_explorer.rb +19 -0
- data/lib/rails_api_explorer.rb +1 -0
- data/lib/tasks/api_explorer_tasks.rake +4 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 072d32c87b27d505bb62ce22a1d491668db4e48d
|
4
|
+
data.tar.gz: ae39b35ef35b5ddf691b3a3dfcd3fcfa8e5f1841
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f447e761837f2647ab0bd4dc022d6f044185ffed149547581c5e14973878cf56c2637943cf75fddda2375034fd97e2dd298995be235ea2b77235661d94bfc079
|
7
|
+
data.tar.gz: 976d4f7a37d9fe5d215a0ed0896cae9f6e58f038015dda0f7b22e7be61da777b36aeca76903b07aa6a9cc0058a9b4abe9716dab7ba0a8fc7acdd6271d07094a6
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'ApiExplorer'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require_tree .
|
@@ -0,0 +1,146 @@
|
|
1
|
+
$(function() {
|
2
|
+
$(".hidden").hide().removeClass("hidden");
|
3
|
+
|
4
|
+
window.responses = {};
|
5
|
+
|
6
|
+
$(".shared-input.header").change(function() {
|
7
|
+
updateShareds();
|
8
|
+
});
|
9
|
+
$(".shared-link").click(function() {
|
10
|
+
$($(this).attr("href")).find("input").focus();
|
11
|
+
});
|
12
|
+
|
13
|
+
function updateShareds() {
|
14
|
+
$(".shared-input.header").each(function() {
|
15
|
+
name = $(this).attr("data-name");
|
16
|
+
$(".shared.header[data-name=" + name + "]").val($(this).val());
|
17
|
+
});
|
18
|
+
}
|
19
|
+
|
20
|
+
function setValuesFromRequest(request) {
|
21
|
+
$("[data-source-request='" + request + "']").each(function(input) {
|
22
|
+
v = eval("responses['" + request + "']" + $(this).attr("data-source-accessor"));
|
23
|
+
$(this).val(v);
|
24
|
+
});
|
25
|
+
updateShareds();
|
26
|
+
}
|
27
|
+
|
28
|
+
// Enable / disable params
|
29
|
+
$(".param input").prop("disabled", false); // Firefox autocomplete workaround
|
30
|
+
$(".send-toggle").click(function(event) {
|
31
|
+
event.preventDefault();
|
32
|
+
|
33
|
+
var setState = function(elements, state) {
|
34
|
+
elements.each(function(i, el) {
|
35
|
+
$(el).attr("data-send", state ? "true" : "false");
|
36
|
+
$(el).find("input").first().prop("disabled", !state);
|
37
|
+
$(el).find(".send-toggle").first().text(state ? "don't send" : "send");
|
38
|
+
$(el).find(".name").first().toggleClass("strikethrough", !state);
|
39
|
+
});
|
40
|
+
}
|
41
|
+
|
42
|
+
var param = $(this).closest(".param");
|
43
|
+
var send = param.attr("data-send") == "true" ? false : true;
|
44
|
+
setState(param.add(param.find(".param")), send); // enable or disable children
|
45
|
+
if (send)
|
46
|
+
setState(param.parents(".param"), true); // enable parents
|
47
|
+
});
|
48
|
+
|
49
|
+
$("form").submit(function(event) {
|
50
|
+
event.preventDefault();
|
51
|
+
|
52
|
+
var form = $(this);
|
53
|
+
var req = form.serializeObject().request;
|
54
|
+
form.closest(".request").find(".status").html("Requesting...");
|
55
|
+
|
56
|
+
path = req.path;
|
57
|
+
if (req.url_params) {
|
58
|
+
$.each(req.url_params, function(p, v) {
|
59
|
+
path = path.replace(":" + p, v);
|
60
|
+
});
|
61
|
+
}
|
62
|
+
|
63
|
+
$.ajax({
|
64
|
+
url: path,
|
65
|
+
type: req.method,
|
66
|
+
headers: req.headers,
|
67
|
+
data: req.params,
|
68
|
+
dataType: 'json'
|
69
|
+
}).always(function(data, status, error) {
|
70
|
+
if(status == 'success') {
|
71
|
+
code = 200;
|
72
|
+
var key = req.method.toUpperCase() + ":" + req.path.replace(api_explorer_base_url, "");
|
73
|
+
window.responses[key] = data;
|
74
|
+
setValuesFromRequest(key);
|
75
|
+
} else {
|
76
|
+
code = data.status;
|
77
|
+
try {
|
78
|
+
data = $.parseJSON(data.responseText);
|
79
|
+
} catch(SyntaxError) {}
|
80
|
+
}
|
81
|
+
if (code == 0) {
|
82
|
+
form.closest(".request").find(".status").text("Can't reach server");
|
83
|
+
form.closest(".request").find(".response").hide();
|
84
|
+
} else {
|
85
|
+
form.closest(".request").find(".status").text(code);
|
86
|
+
form.closest(".request").find(".response").text(JSON.stringify(data, null, 4)).show();
|
87
|
+
}
|
88
|
+
});
|
89
|
+
});
|
90
|
+
|
91
|
+
|
92
|
+
$.fn.serializeObject = function() {
|
93
|
+
var self = this,
|
94
|
+
json = {},
|
95
|
+
push_counters = {},
|
96
|
+
patterns = {
|
97
|
+
"validate": /^[a-zA-Z][a-zA-Z0-9_\-]*(?:\[(?:\d*|[a-zA-Z0-9_\-]+)\])*$/,
|
98
|
+
"key": /[a-zA-Z0-9_\-]+|(?=\[\])/g,
|
99
|
+
"push": /^$/,
|
100
|
+
"fixed": /^\d+$/,
|
101
|
+
"named": /^[a-zA-Z0-9_\-]+$/
|
102
|
+
};
|
103
|
+
|
104
|
+
this.build = function(base, key, value){
|
105
|
+
base[key] = value;
|
106
|
+
return base;
|
107
|
+
};
|
108
|
+
this.push_counter = function(key){
|
109
|
+
if(push_counters[key] === undefined){
|
110
|
+
push_counters[key] = 0;
|
111
|
+
}
|
112
|
+
return push_counters[key]++;
|
113
|
+
};
|
114
|
+
$.each($(this).serializeArray(), function(){
|
115
|
+
// skip invalid keys
|
116
|
+
if(!patterns.validate.test(this.name)) {
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
var k,
|
120
|
+
keys = this.name.match(patterns.key),
|
121
|
+
merge = this.value,
|
122
|
+
reverse_key = this.name;
|
123
|
+
|
124
|
+
while((k = keys.pop()) !== undefined){
|
125
|
+
|
126
|
+
// adjust reverse_key
|
127
|
+
reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), '');
|
128
|
+
|
129
|
+
// push
|
130
|
+
if(k.match(patterns.push)){
|
131
|
+
merge = self.build([], self.push_counter(reverse_key), merge);
|
132
|
+
}
|
133
|
+
// fixed
|
134
|
+
else if(k.match(patterns.fixed)){
|
135
|
+
merge = self.build([], k, merge);
|
136
|
+
}
|
137
|
+
// named
|
138
|
+
else if(k.match(patterns.named)){
|
139
|
+
merge = self.build({}, k, merge);
|
140
|
+
}
|
141
|
+
}
|
142
|
+
json = $.extend(true, json, merge);
|
143
|
+
});
|
144
|
+
return json;
|
145
|
+
};
|
146
|
+
});
|
@@ -0,0 +1,38 @@
|
|
1
|
+
$(function() {
|
2
|
+
/**
|
3
|
+
* Check a href for an anchor. If exists, and in document, scroll to it.
|
4
|
+
* If href argument ommited, assumes context (this) is HTML Element,
|
5
|
+
* which will be the case when invoked by jQuery after an event
|
6
|
+
*/
|
7
|
+
function scroll_if_anchor(href) {
|
8
|
+
href = typeof(href) == "string" ? href : $(this).attr("href");
|
9
|
+
|
10
|
+
var fromTop = 0;
|
11
|
+
|
12
|
+
if ($(".navbar-fixed-top").length > 0) {
|
13
|
+
fromTop = $(".navbar-fixed-top").height();
|
14
|
+
}
|
15
|
+
|
16
|
+
// If our Href points to a valid, non-empty anchor, and is on the same page (e.g. #foo)
|
17
|
+
// Legacy jQuery and IE7 may have issues: http://stackoverflow.com/q/1593174
|
18
|
+
if(href.indexOf("#") == 0) {
|
19
|
+
var $target = $(href);
|
20
|
+
|
21
|
+
// Older browser without pushState might flicker here, as they momentarily
|
22
|
+
// jump to the wrong position (IE < 10)
|
23
|
+
if($target.length) {
|
24
|
+
$('html, body').animate({ scrollTop: $target.offset().top - fromTop });
|
25
|
+
if(history && "pushState" in history) {
|
26
|
+
history.pushState({}, document.title, window.location.pathname + href);
|
27
|
+
return false;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
// When our page loads, check to see if it contains and anchor
|
34
|
+
scroll_if_anchor(window.location.hash);
|
35
|
+
|
36
|
+
// Intercept all anchor clicks
|
37
|
+
$("body").on("click", "a", scroll_if_anchor);
|
38
|
+
});
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,70 @@
|
|
1
|
+
.hidden {
|
2
|
+
display: none;
|
3
|
+
}
|
4
|
+
|
5
|
+
.params .params {
|
6
|
+
margin-left: 30px;
|
7
|
+
}
|
8
|
+
|
9
|
+
.param-inputs .param-inputs {
|
10
|
+
margin-left: 30px;
|
11
|
+
}
|
12
|
+
|
13
|
+
.param-inputs fieldset {
|
14
|
+
border: none;
|
15
|
+
padding: 0;
|
16
|
+
}
|
17
|
+
.param-inputs input {
|
18
|
+
border: none;
|
19
|
+
}
|
20
|
+
h3 input {
|
21
|
+
border: none;
|
22
|
+
width: 120px;
|
23
|
+
text-align: center;
|
24
|
+
}
|
25
|
+
|
26
|
+
legend {
|
27
|
+
margin-bottom: 5px;
|
28
|
+
}
|
29
|
+
.param-inputs legend {
|
30
|
+
font-size: 100%;
|
31
|
+
border: none;
|
32
|
+
margin-bottom: 0;
|
33
|
+
}
|
34
|
+
label {
|
35
|
+
font-weight: normal;
|
36
|
+
margin-bottom: 0;
|
37
|
+
}
|
38
|
+
|
39
|
+
.response {
|
40
|
+
border: solid 1px black;
|
41
|
+
background-color: #CCC;
|
42
|
+
padding: 20px;
|
43
|
+
max-height: 400px;
|
44
|
+
overflow: scroll;
|
45
|
+
}
|
46
|
+
|
47
|
+
.description {
|
48
|
+
font-size: 80%;
|
49
|
+
}
|
50
|
+
|
51
|
+
fieldset {
|
52
|
+
margin-bottom: 5px;
|
53
|
+
}
|
54
|
+
|
55
|
+
legend, label {
|
56
|
+
position: relative;
|
57
|
+
}
|
58
|
+
|
59
|
+
.send-toggle {
|
60
|
+
font-size: 80%;
|
61
|
+
cursor: pointer;
|
62
|
+
display: none;
|
63
|
+
}
|
64
|
+
:hover > .send-toggle {
|
65
|
+
display: inline;
|
66
|
+
}
|
67
|
+
|
68
|
+
.strikethrough {
|
69
|
+
text-decoration: line-through;
|
70
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<li>
|
2
|
+
<% if node.is_a?(ApiExplorer::Request) %>
|
3
|
+
<a href="#node-<%= id %>"><%= node.title %></a>
|
4
|
+
<% else %>
|
5
|
+
<a href="#node-<%= id %>"><%= node.title %></a>
|
6
|
+
<ul class="children">
|
7
|
+
<% node.children.each_with_index do |ch, i| %>
|
8
|
+
<%= render 'content_node', node: ch, id: "#{id}-#{i}" %>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
<% end %>
|
12
|
+
</li>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<% node.children.each_with_index do |ch, i| %>
|
2
|
+
<% if ch.is_a?(ApiExplorer::Request) %>
|
3
|
+
<%= render 'request', req: ch, id: "#{id}-#{i}", depth: depth + 1 %>
|
4
|
+
<% else %>
|
5
|
+
<div class="group row" id="node<%= id %>-<%= i %>">
|
6
|
+
<div class="col-sm-12">
|
7
|
+
<% header_n = [1 + depth, 2].min %>
|
8
|
+
<h<%= header_n %>><%= ch.title %></h<%= header_n %>>
|
9
|
+
<% if ch.children.any? %>
|
10
|
+
<div class="children">
|
11
|
+
<%= render 'node', id: "#{id}-#{i}", node: ch, depth: depth + 1 %>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
<% end %>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="params">
|
2
|
+
<% params.each do |param| %>
|
3
|
+
<div class="param">
|
4
|
+
<%= param.name %>:
|
5
|
+
<% if param.type == :hash %>
|
6
|
+
{
|
7
|
+
<%= render 'params', params: param.children %>
|
8
|
+
}
|
9
|
+
<% else %>
|
10
|
+
<small><%= param.type %></small>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<div class="row request" id="node<%= id %>">
|
2
|
+
<%= form_for :request, url: request_path, html: { class: "col-sm-6" } do |f| %>
|
3
|
+
<%= f.hidden_field :method, value: req.method %>
|
4
|
+
<%= f.hidden_field :path, value: req.full_path %>
|
5
|
+
|
6
|
+
<%= f.fields_for :url_params do |pf| %>
|
7
|
+
<h3>
|
8
|
+
<%= req.method.to_s.upcase %>
|
9
|
+
<% req.url_segments.each do |type, s| %>
|
10
|
+
<% if type == :param %>
|
11
|
+
<%= pf.text_field s, placeholder: ":#{s}" %>
|
12
|
+
<!-- span contenteditable="true" data-ph="<%= ":#{s}" %>" data-name="<%= s %>"></span -->
|
13
|
+
<% else %>
|
14
|
+
<%= raw s %>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
17
|
+
</h3>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<div>
|
21
|
+
<p><%= simple_format req.description %></p>
|
22
|
+
|
23
|
+
<!-- %= render 'api_explorer/explorer/form/url_params', params: req.url_params, f: f % -->
|
24
|
+
<%= render 'api_explorer/explorer/form/headers', request: req, f: f %>
|
25
|
+
<% if req.params.any? %>
|
26
|
+
<fieldset>
|
27
|
+
<legend>Parameters</legend>
|
28
|
+
<%= render 'api_explorer/explorer/form/params', name: 'params', params: req.params, f: f %>
|
29
|
+
</fieldset>
|
30
|
+
<% end %>
|
31
|
+
<%= f.submit "Send", class: "btn btn-primary" %>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
<div class="col-sm-6 output">
|
36
|
+
<p class="status"></p>
|
37
|
+
<pre class="response hidden"></pre>
|
38
|
+
</div>
|
39
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div class="row">
|
2
|
+
<div class="col-sm-12">
|
3
|
+
<h3>Shared headers</h3>
|
4
|
+
|
5
|
+
<% @description.headers.each do |h| %>
|
6
|
+
<div id="shared-header-<%= h.name %>">
|
7
|
+
<%= label_tag h.name, h.name %>:
|
8
|
+
<%= text_field_tag h.name, "", class: "shared-input header", data: { name: h.name, source_request: h.source[:request], source_accessor: h.source[:accessor] } %>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
11
|
+
</div>
|
12
|
+
</div>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<% if request.excluded_shared_headers.any? %>
|
2
|
+
<p>Note: Does <strong>not</strong> use these shared headers: <%= request.excluded_shared_headers.join(", ") %>.</p>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<%= f.fields_for :headers do |hf| %>
|
6
|
+
<% @description.headers.each do |h| %>
|
7
|
+
<% next if request.excluded_shared_headers.include?(h.name) %>
|
8
|
+
|
9
|
+
<!--%= hf.label h.name, h.name %-->
|
10
|
+
<!--%= raw " (<a class='shared-link' href='#shared-header-#{h.name}'>shared</a>)" %-->
|
11
|
+
<%= hf.hidden_field h.name, class: "shared header", data: { name: h.name } %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<% if request.headers.any? %>
|
16
|
+
<fieldset>
|
17
|
+
<legend>Headers</legend>
|
18
|
+
<%= f.fields_for :headers do |hf| %>
|
19
|
+
<% request.headers.each do |h| %>
|
20
|
+
<%= hf.label h.name, h.name + ":" %>
|
21
|
+
<%= hf.text_field h.name %>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
</fieldset>
|
25
|
+
<% end %>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<div class="param" data-param-name="<%= p.name %>" data-send="true">
|
2
|
+
<% if p.type == :hash %>
|
3
|
+
<fieldset>
|
4
|
+
<legend>
|
5
|
+
<span class="name"><%= p.name %></span>:
|
6
|
+
{
|
7
|
+
<a class="send-toggle">don't send</a>
|
8
|
+
</legend>
|
9
|
+
<div class="description"><%= simple_format p.description %></div>
|
10
|
+
<div class="param-inputs">
|
11
|
+
<%= render 'api_explorer/explorer/form/params', name: p.name, params: p.params, f: f %>
|
12
|
+
</div>
|
13
|
+
}
|
14
|
+
</fieldset>
|
15
|
+
<% else %>
|
16
|
+
<%= f.label p.name do %>
|
17
|
+
<span class="name"><%= p.name %></span>:
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if p.name == 'password' %>
|
21
|
+
<%= f.password_field p.name, placeholder: p.type %>
|
22
|
+
<% else %>
|
23
|
+
<%= f.text_field p.name, placeholder: p.type %>
|
24
|
+
<% end %>
|
25
|
+
<a class="send-toggle">don't send</a>
|
26
|
+
<div class="description"><%= p.description %></div>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% return if params.none? %>
|
2
|
+
|
3
|
+
<fieldset>
|
4
|
+
<legend>URL Parameters</legend>
|
5
|
+
<%= f.fields_for :url_params do |pf| %>
|
6
|
+
<% params.each do |p| %>
|
7
|
+
<p>
|
8
|
+
<%= pf.label p, p + ":" %>
|
9
|
+
<%= pf.text_field p %>
|
10
|
+
</p>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
13
|
+
</fieldset>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
window.api_explorer_base_url = "<%= @description.base_url %>";
|
3
|
+
</script>
|
4
|
+
|
5
|
+
<%= stylesheet_link_tag "api_explorer/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "api_explorer/application" %>
|
7
|
+
|
8
|
+
<div class="row">
|
9
|
+
<div class="col-sm-12">
|
10
|
+
<h1>Index</h1>
|
11
|
+
<ul class="contents">
|
12
|
+
<% @description.children.each_with_index do |ch, i| %>
|
13
|
+
<%= render 'content_node', node: ch, id: i %>
|
14
|
+
<% end %>
|
15
|
+
</ul>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<%= render 'shared' %>
|
20
|
+
<%= render 'node', node: @description, id: "", depth: 0 %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= raw ap @response %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module ApiExplorer
|
2
|
+
class GroupProxy < BaseProxy
|
3
|
+
def path(path = nil)
|
4
|
+
obj.path = path if path
|
5
|
+
obj.path
|
6
|
+
end
|
7
|
+
|
8
|
+
def group(title, &block)
|
9
|
+
group = Group.new(title)
|
10
|
+
proxy = GroupProxy.new(group)
|
11
|
+
proxy.collect(&block) if block_given?
|
12
|
+
obj.add_child group
|
13
|
+
end
|
14
|
+
|
15
|
+
def request(method, path, &block)
|
16
|
+
method = method.to_s.downcase.to_sym
|
17
|
+
req = Request.new(method, path)
|
18
|
+
proxy = RequestProxy.new(req)
|
19
|
+
proxy.collect(&block) if block_given?
|
20
|
+
obj.add_child req
|
21
|
+
end
|
22
|
+
|
23
|
+
def shared(&block)
|
24
|
+
proxy = RequestProxy.new(obj)
|
25
|
+
proxy.collect(&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def get(path, &block)
|
29
|
+
request(:get, path, &block)
|
30
|
+
end
|
31
|
+
def post(path, &block)
|
32
|
+
request(:post, path, &block)
|
33
|
+
end
|
34
|
+
def put(path, &block)
|
35
|
+
request(:put, path, &block)
|
36
|
+
end
|
37
|
+
def patch(path, &block)
|
38
|
+
request(:patch, path, &block)
|
39
|
+
end
|
40
|
+
def delete(path, &block)
|
41
|
+
request(:delete, path, &block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ApiExplorer
|
2
|
+
class RequestProxy < BaseProxy
|
3
|
+
def exclude_shared_header(name)
|
4
|
+
obj.excluded_shared_headers << name
|
5
|
+
end
|
6
|
+
|
7
|
+
def desc(description)
|
8
|
+
obj.description = description
|
9
|
+
end
|
10
|
+
|
11
|
+
def param(name, type = nil, options = {}, &block)
|
12
|
+
desc = options.fetch(:desc, "")
|
13
|
+
if type.nil? && block_given?
|
14
|
+
param = Parameter.new(name, [], desc)
|
15
|
+
param.type = :hash
|
16
|
+
proxy = RequestProxy.new(param)
|
17
|
+
proxy.collect(&block)
|
18
|
+
obj.params << param
|
19
|
+
else
|
20
|
+
obj.params << Parameter.new(name, type, desc)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def struct(name, options = {}, &block)
|
25
|
+
param(name, nil, options, &block)
|
26
|
+
end
|
27
|
+
def string(name, options = {}, &block)
|
28
|
+
param(name, :string, options)
|
29
|
+
end
|
30
|
+
def boolean(name, options = {}, &block)
|
31
|
+
param(name, :boolean, options)
|
32
|
+
end
|
33
|
+
def integer(name, options = {}, &block)
|
34
|
+
param(name, :integer, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def header(name, options = {})
|
38
|
+
obj.headers << Header.new(name, options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ApiExplorer
|
2
|
+
class Group < Node
|
3
|
+
attr_accessor :headers, :params, :title
|
4
|
+
|
5
|
+
def initialize(title, path = "", children = [], shared_headers = [], shared_params = [])
|
6
|
+
super nil, children, path
|
7
|
+
|
8
|
+
self.title = title
|
9
|
+
self.headers = shared_headers
|
10
|
+
self.params = shared_params
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_child(child)
|
14
|
+
children << child
|
15
|
+
child.parent = self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ApiExplorer
|
2
|
+
class Node
|
3
|
+
attr_accessor :parent, :children, :path
|
4
|
+
|
5
|
+
def initialize(parent = nil, children = nil, path = nil)
|
6
|
+
self.parent = parent || NullNode.new
|
7
|
+
self.children = children || []
|
8
|
+
self.path = path.to_s
|
9
|
+
|
10
|
+
self.children.each { |c| c.parent = self }
|
11
|
+
end
|
12
|
+
|
13
|
+
def full_path
|
14
|
+
parent.full_path + path
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
class NullNode
|
21
|
+
def full_path
|
22
|
+
""
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ApiExplorer
|
2
|
+
class Parameter
|
3
|
+
def initialize(name, type_or_children, description)
|
4
|
+
self.name = name.to_s
|
5
|
+
self.description = description
|
6
|
+
if type_or_children.is_a?(Array)
|
7
|
+
self.type = :hash
|
8
|
+
self.params = type_or_children
|
9
|
+
else
|
10
|
+
self.type = type_or_children.to_s.to_sym
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :name, :type, :params, :description
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ApiExplorer
|
2
|
+
class Request < Node
|
3
|
+
attr_accessor :method, :params, :headers, :description, :excluded_shared_headers
|
4
|
+
|
5
|
+
def initialize(method, path, params = [], headers = [], description = "", excluded_shared_headers = [])
|
6
|
+
super nil, nil, path
|
7
|
+
|
8
|
+
self.method = method.to_s
|
9
|
+
self.params = Array(params)
|
10
|
+
self.headers = Array(headers)
|
11
|
+
self.description = description
|
12
|
+
self.excluded_shared_headers = Array(excluded_shared_headers)
|
13
|
+
end
|
14
|
+
|
15
|
+
def url
|
16
|
+
full_path
|
17
|
+
end
|
18
|
+
|
19
|
+
def url_params
|
20
|
+
path.scan(/:[a-zA-Z_\-]+/).map { |s| s[1..s.size] }
|
21
|
+
end
|
22
|
+
|
23
|
+
def url_segments
|
24
|
+
path.scan(/(:[a-zA-Z_\-]+)|([^:]+)/).map do |param, normal|
|
25
|
+
if param
|
26
|
+
[:param, param[1..param.size]]
|
27
|
+
else
|
28
|
+
[:normal, normal]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def title
|
34
|
+
"#{method.upcase} #{path}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/api_explorer.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "api_explorer/engine"
|
2
|
+
require "api_explorer/node"
|
3
|
+
require "api_explorer/group"
|
4
|
+
require "api_explorer/description"
|
5
|
+
require "api_explorer/header"
|
6
|
+
require "api_explorer/parameter"
|
7
|
+
require "api_explorer/request"
|
8
|
+
require "api_explorer/dsl"
|
9
|
+
|
10
|
+
module ApiExplorer
|
11
|
+
mattr_accessor :description, :shared_headers, :shared_params, :auth
|
12
|
+
|
13
|
+
def self.describe(&block)
|
14
|
+
self.description = Description.new("", "", [], [], [])
|
15
|
+
proxy = DescriptionProxy.new(description)
|
16
|
+
proxy.collect(&block)
|
17
|
+
#self.description = Description.new(proxy.path, proxy.children, proxy.shared_headers, proxy.shared_params)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "api_explorer"
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_api_explorer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Max Hollmann
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jquery-rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: ''
|
56
|
+
email:
|
57
|
+
- maxhollmann@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- MIT-LICENSE
|
63
|
+
- Rakefile
|
64
|
+
- app/assets/javascripts/api_explorer/application.js
|
65
|
+
- app/assets/javascripts/api_explorer/explorer.js
|
66
|
+
- app/assets/javascripts/api_explorer/scroll.js
|
67
|
+
- app/assets/stylesheets/api_explorer/application.css
|
68
|
+
- app/assets/stylesheets/api_explorer/explorer.css
|
69
|
+
- app/controllers/api_explorer/application_controller.rb
|
70
|
+
- app/controllers/api_explorer/explorer_controller.rb
|
71
|
+
- app/helpers/api_explorer/application_helper.rb
|
72
|
+
- app/helpers/api_explorer/explorer_helper.rb
|
73
|
+
- app/views/api_explorer/explorer/_content_node.html.erb
|
74
|
+
- app/views/api_explorer/explorer/_node.html.erb
|
75
|
+
- app/views/api_explorer/explorer/_params.html.erb
|
76
|
+
- app/views/api_explorer/explorer/_request.html.erb
|
77
|
+
- app/views/api_explorer/explorer/_shared.html.erb
|
78
|
+
- app/views/api_explorer/explorer/form/_headers.html.erb
|
79
|
+
- app/views/api_explorer/explorer/form/_param.html.erb
|
80
|
+
- app/views/api_explorer/explorer/form/_params.html.erb
|
81
|
+
- app/views/api_explorer/explorer/form/_url_params.html.erb
|
82
|
+
- app/views/api_explorer/explorer/index.html.erb
|
83
|
+
- app/views/api_explorer/explorer/perform_request.html.erb
|
84
|
+
- config/routes.rb
|
85
|
+
- lib/api_explorer.rb
|
86
|
+
- lib/api_explorer/description.rb
|
87
|
+
- lib/api_explorer/dsl.rb
|
88
|
+
- lib/api_explorer/dsl/base_proxy.rb
|
89
|
+
- lib/api_explorer/dsl/description_proxy.rb
|
90
|
+
- lib/api_explorer/dsl/group_proxy.rb
|
91
|
+
- lib/api_explorer/dsl/request_proxy.rb
|
92
|
+
- lib/api_explorer/engine.rb
|
93
|
+
- lib/api_explorer/group.rb
|
94
|
+
- lib/api_explorer/header.rb
|
95
|
+
- lib/api_explorer/node.rb
|
96
|
+
- lib/api_explorer/parameter.rb
|
97
|
+
- lib/api_explorer/request.rb
|
98
|
+
- lib/api_explorer/version.rb
|
99
|
+
- lib/rails_api_explorer.rb
|
100
|
+
- lib/tasks/api_explorer_tasks.rake
|
101
|
+
homepage: https://github.com/maxhollmann/rails_api_explorer
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 1.3.1
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.2.2
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: Provides a simple DSL to describe your API, and let's you mount an interactive
|
125
|
+
sandbox to explore and test it.
|
126
|
+
test_files: []
|