rails_api_explorer 0.0.1.pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|