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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +23 -0
  4. data/app/assets/javascripts/api_explorer/application.js +15 -0
  5. data/app/assets/javascripts/api_explorer/explorer.js +146 -0
  6. data/app/assets/javascripts/api_explorer/scroll.js +38 -0
  7. data/app/assets/stylesheets/api_explorer/application.css +15 -0
  8. data/app/assets/stylesheets/api_explorer/explorer.css +70 -0
  9. data/app/controllers/api_explorer/application_controller.rb +5 -0
  10. data/app/controllers/api_explorer/explorer_controller.rb +9 -0
  11. data/app/helpers/api_explorer/application_helper.rb +4 -0
  12. data/app/helpers/api_explorer/explorer_helper.rb +4 -0
  13. data/app/views/api_explorer/explorer/_content_node.html.erb +12 -0
  14. data/app/views/api_explorer/explorer/_node.html.erb +17 -0
  15. data/app/views/api_explorer/explorer/_params.html.erb +14 -0
  16. data/app/views/api_explorer/explorer/_request.html.erb +39 -0
  17. data/app/views/api_explorer/explorer/_shared.html.erb +12 -0
  18. data/app/views/api_explorer/explorer/form/_headers.html.erb +25 -0
  19. data/app/views/api_explorer/explorer/form/_param.html.erb +28 -0
  20. data/app/views/api_explorer/explorer/form/_params.html.erb +7 -0
  21. data/app/views/api_explorer/explorer/form/_url_params.html.erb +13 -0
  22. data/app/views/api_explorer/explorer/index.html.erb +20 -0
  23. data/app/views/api_explorer/explorer/perform_request.html.erb +1 -0
  24. data/config/routes.rb +4 -0
  25. data/lib/api_explorer/description.rb +7 -0
  26. data/lib/api_explorer/dsl/base_proxy.rb +13 -0
  27. data/lib/api_explorer/dsl/description_proxy.rb +7 -0
  28. data/lib/api_explorer/dsl/group_proxy.rb +44 -0
  29. data/lib/api_explorer/dsl/request_proxy.rb +41 -0
  30. data/lib/api_explorer/dsl.rb +4 -0
  31. data/lib/api_explorer/engine.rb +5 -0
  32. data/lib/api_explorer/group.rb +18 -0
  33. data/lib/api_explorer/header.rb +10 -0
  34. data/lib/api_explorer/node.rb +30 -0
  35. data/lib/api_explorer/parameter.rb +16 -0
  36. data/lib/api_explorer/request.rb +37 -0
  37. data/lib/api_explorer/version.rb +3 -0
  38. data/lib/api_explorer.rb +19 -0
  39. data/lib/rails_api_explorer.rb +1 -0
  40. data/lib/tasks/api_explorer_tasks.rake +4 -0
  41. 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,5 @@
1
+ class ApiExplorer::ApplicationController < ApplicationController
2
+ before_filter do
3
+ instance_eval(&ApiExplorer.auth) if ApiExplorer.auth
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ require_dependency "api_explorer/application_controller"
2
+
3
+ module ApiExplorer
4
+ class ExplorerController < ApplicationController
5
+ def index
6
+ @description = ApiExplorer.description
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module ApiExplorer
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ApiExplorer
2
+ module ExplorerHelper
3
+ end
4
+ end
@@ -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,7 @@
1
+ <%= f.fields_for name do |pf| %>
2
+ <div class="param-inputs">
3
+ <% params.each do |p| %>
4
+ <%= render 'api_explorer/explorer/form/param', f: pf, p: p %>
5
+ <% end %>
6
+ </div>
7
+ <% end %>
@@ -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,4 @@
1
+ ApiExplorer::Engine.routes.draw do
2
+ root to: 'explorer#index'
3
+ post 'request' => 'explorer#perform_request'
4
+ end
@@ -0,0 +1,7 @@
1
+ module ApiExplorer
2
+ class Description < Group
3
+ def base_url
4
+ path
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module ApiExplorer
2
+ class BaseProxy
3
+ attr_accessor :obj
4
+
5
+ def initialize(obj = nil)
6
+ self.obj = obj
7
+ end
8
+
9
+ def collect(&block)
10
+ instance_eval(&block)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module ApiExplorer
2
+ class DescriptionProxy < GroupProxy
3
+ def base_url(url)
4
+ path url
5
+ end
6
+ end
7
+ end
@@ -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,4 @@
1
+ require "api_explorer/dsl/base_proxy"
2
+ require "api_explorer/dsl/group_proxy"
3
+ require "api_explorer/dsl/description_proxy"
4
+ require "api_explorer/dsl/request_proxy"
@@ -0,0 +1,5 @@
1
+ module ApiExplorer
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ApiExplorer
4
+ end
5
+ 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,10 @@
1
+ module ApiExplorer
2
+ class Header
3
+ def initialize(name, options = {})
4
+ self.name = name.to_s
5
+ self.source = options[:source] || {}
6
+ end
7
+
8
+ attr_accessor :name, :source
9
+ end
10
+ 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
@@ -0,0 +1,3 @@
1
+ module ApiExplorer
2
+ VERSION = "0.0.1.pre.1"
3
+ end
@@ -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"
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :api_explorer do
3
+ # # Task goes here
4
+ # end
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: []