api_taster 0.1.0

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 (67) hide show
  1. data/.gitignore +11 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +6 -0
  5. data/README.md +68 -0
  6. data/Rakefile +40 -0
  7. data/api_taster.gemspec +27 -0
  8. data/app/assets/images/api_taster/.gitkeep +0 -0
  9. data/app/assets/javascripts/api_taster/01-prettify.js +28 -0
  10. data/app/assets/javascripts/api_taster/02-jquery.form.js +1076 -0
  11. data/app/assets/javascripts/api_taster/app.js +88 -0
  12. data/app/assets/javascripts/api_taster/application.js +16 -0
  13. data/app/assets/stylesheets/api_taster/01-prettify.css +1 -0
  14. data/app/assets/stylesheets/api_taster/02-prettify-sunburst.css +51 -0
  15. data/app/assets/stylesheets/api_taster/application.css +13 -0
  16. data/app/assets/stylesheets/api_taster/layout.css.scss +51 -0
  17. data/app/controllers/api_taster/application_controller.rb +5 -0
  18. data/app/controllers/api_taster/routes_controller.rb +12 -0
  19. data/app/helpers/api_taster/application_helper.rb +4 -0
  20. data/app/views/api_taster/routes/_param_form_element.html.erb +6 -0
  21. data/app/views/api_taster/routes/_param_form_legend.html.erb +1 -0
  22. data/app/views/api_taster/routes/_undefined_route.html.erb +7 -0
  23. data/app/views/api_taster/routes/index.html.erb +24 -0
  24. data/app/views/api_taster/routes/show.html.erb +55 -0
  25. data/app/views/layouts/api_taster/application.html.erb +29 -0
  26. data/config/routes.rb +5 -0
  27. data/lib/api_taster.rb +13 -0
  28. data/lib/api_taster/engine.rb +17 -0
  29. data/lib/api_taster/form_builder.rb +49 -0
  30. data/lib/api_taster/mapper.rb +30 -0
  31. data/lib/api_taster/route.rb +63 -0
  32. data/lib/api_taster/version.rb +3 -0
  33. data/lib/tasks/api_taster_tasks.rake +0 -0
  34. data/script/rails +8 -0
  35. data/spec/controllers/api_taster/routes_controller_spec.rb +21 -0
  36. data/spec/dummy/Rakefile +7 -0
  37. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  38. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  39. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  40. data/spec/dummy/app/controllers/users_controller.rb +21 -0
  41. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  42. data/spec/dummy/app/mailers/.gitkeep +0 -0
  43. data/spec/dummy/app/models/.gitkeep +0 -0
  44. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  45. data/spec/dummy/config.ru +4 -0
  46. data/spec/dummy/config/application.rb +67 -0
  47. data/spec/dummy/config/boot.rb +10 -0
  48. data/spec/dummy/config/environment.rb +5 -0
  49. data/spec/dummy/config/environments/development.rb +37 -0
  50. data/spec/dummy/config/environments/production.rb +67 -0
  51. data/spec/dummy/config/environments/test.rb +37 -0
  52. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/dummy/config/initializers/inflections.rb +15 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  55. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  56. data/spec/dummy/config/initializers/session_store.rb +8 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +5 -0
  59. data/spec/dummy/config/routes.rb +33 -0
  60. data/spec/dummy/lib/assets/.gitkeep +0 -0
  61. data/spec/dummy/log/.gitkeep +0 -0
  62. data/spec/dummy/script/rails +6 -0
  63. data/spec/form_builder_spec.rb +29 -0
  64. data/spec/mapper_spec.rb +41 -0
  65. data/spec/route_spec.rb +71 -0
  66. data/spec/spec_helper.rb +12 -0
  67. metadata +503 -0
@@ -0,0 +1,88 @@
1
+ var ApiTaster = {
2
+ formAction: '',
3
+ disableUrlParams: function() {
4
+ $("#url-params input").prop("disabled", true);
5
+ },
6
+ enableUrlParams: function() {
7
+ $("#url-params input").prop("disabled", false);
8
+ },
9
+ storeFormActionFor: function(form) {
10
+ ApiTaster.formAction = form.attr("action")
11
+ },
12
+ restoreFormActionFor: function(form) {
13
+ $(form).attr("action", ApiTaster.formAction);
14
+ }
15
+ };
16
+
17
+ $.fn.extend({
18
+ replaceUrlParams: function(params) {
19
+ var form = this;
20
+
21
+ $.each(params, function(i, param) {
22
+ var matches = param["name"].match(/\[api_taster_url_params\](.*)/)
23
+ if (matches) {
24
+ var paramKey = matches[1];
25
+ var paramValue = param["value"];
26
+
27
+ ApiTaster.storeFormActionFor(form);
28
+
29
+ var regex = new RegExp(":" + paramKey);
30
+ var replacedAction = ApiTaster.formAction.replace(regex, paramValue);
31
+
32
+ form.attr("action", replacedAction);
33
+ }
34
+ });
35
+ }
36
+ });
37
+
38
+ jQuery(function($) {
39
+ $("a.show-api").click(function(e) {
40
+ e.preventDefault();
41
+
42
+ $("a.show-api").parent().removeClass("active");
43
+ $(this).parent().addClass("active");
44
+
45
+ $("#show-api-div").load(this.href, function() {
46
+ prettyPrint();
47
+ });
48
+ });
49
+
50
+ $("#show-api-div").on("click", "#submit-api", function() {
51
+ $(this).parents("form").submit(function() {
52
+ $(this).unbind("submit").ajaxSubmit({
53
+ beforeSubmit: function(arr, $form, options) {
54
+ $form.replaceUrlParams(arr);
55
+ ApiTaster.disableUrlParams();
56
+ return false;
57
+ }
58
+ });
59
+ });
60
+
61
+ $("form").bind("ajax:complete", function(e, xhr, status) {
62
+ ApiTaster.enableUrlParams();
63
+ ApiTaster.restoreFormActionFor(this);
64
+
65
+ if ($("#show-api-response-div:visible").length == 0) {
66
+ $("#show-api-response-div").slideDown(100);
67
+ }
68
+
69
+ $("#show-api-response-div pre[ref=response-raw]").text(xhr.responseText);
70
+ $("#show-api-response-div pre[ref=response-json]").text(
71
+ JSON.stringify(JSON.parse(xhr.responseText), null, 2)
72
+ );
73
+
74
+ prettyPrint();
75
+ });
76
+
77
+ $("#show-api-response-div ul.nav-tabs a").click(function(e) {
78
+ e.preventDefault();
79
+
80
+ $(this).parent().siblings().removeClass("active");
81
+ $(this).parent().addClass("active");
82
+
83
+ $("pre", "#show-api-response-div").hide();
84
+ $("pre[ref=" + $(this).attr("id") + "]", "#show-api-response-div").show();
85
+ });
86
+ });
87
+ });
88
+
@@ -0,0 +1,16 @@
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
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require bootstrap
16
+ //= require_tree .
@@ -0,0 +1 @@
1
+ .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
@@ -0,0 +1,51 @@
1
+ /* Pretty printing styles. Used with prettify.js. */
2
+ /* Vim sunburst theme by David Leibovic */
3
+
4
+ pre .str, code .str { color: #65B042; } /* string - green */
5
+ pre .kwd, code .kwd { color: #E28964; } /* keyword - dark pink */
6
+ pre .com, code .com { color: #AEAEAE; font-style: italic; } /* comment - gray */
7
+ pre .typ, code .typ { color: #89bdff; } /* type - light blue */
8
+ pre .lit, code .lit { color: #3387CC; } /* literal - blue */
9
+ pre .pun, code .pun { color: #fff; } /* punctuation - white */
10
+ pre .pln, code .pln { color: #fff; } /* plaintext - white */
11
+ pre .tag, code .tag { color: #89bdff; } /* html/xml tag - light blue */
12
+ pre .atn, code .atn { color: #bdb76b; } /* html/xml attribute name - khaki */
13
+ pre .atv, code .atv { color: #65B042; } /* html/xml attribute value - green */
14
+ pre .dec, code .dec { color: #3387CC; } /* decimal - blue */
15
+
16
+ pre.prettyprint, code.prettyprint {
17
+ background-color: #000;
18
+ -moz-border-radius: 8px;
19
+ -webkit-border-radius: 8px;
20
+ -o-border-radius: 8px;
21
+ -ms-border-radius: 8px;
22
+ -khtml-border-radius: 8px;
23
+ border-radius: 8px;
24
+ }
25
+
26
+ pre.prettyprint {
27
+ width: 95%;
28
+ margin: 1em auto;
29
+ padding: 1em;
30
+ white-space: pre-wrap;
31
+ }
32
+
33
+
34
+ /* Specify class=linenums on a pre to get line numbering */
35
+ ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE; } /* IE indents via margin-left */
36
+ li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none }
37
+ /* Alternate shading for lines */
38
+ li.L1,li.L3,li.L5,li.L7,li.L9 { }
39
+
40
+ @media print {
41
+ pre .str, code .str { color: #060; }
42
+ pre .kwd, code .kwd { color: #006; font-weight: bold; }
43
+ pre .com, code .com { color: #600; font-style: italic; }
44
+ pre .typ, code .typ { color: #404; font-weight: bold; }
45
+ pre .lit, code .lit { color: #044; }
46
+ pre .pun, code .pun { color: #440; }
47
+ pre .pln, code .pln { color: #000; }
48
+ pre .tag, code .tag { color: #006; font-weight: bold; }
49
+ pre .atn, code .atn { color: #404; }
50
+ pre .atv, code .atv { color: #060; }
51
+ }
@@ -0,0 +1,13 @@
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 top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,51 @@
1
+ @import "bootstrap";
2
+
3
+ $colour_text: #333;
4
+ $colour_grey: #999;
5
+
6
+ body {
7
+ padding-top: 50px;
8
+ padding-bottom: 50px;
9
+ }
10
+
11
+ .hidden {
12
+ display: none;
13
+ }
14
+
15
+ .form-actions {
16
+ padding-bottom: 0;
17
+ margin-bottom: 0;
18
+ }
19
+
20
+ legend {
21
+ font-size: 14px;
22
+ font-weight: bold;
23
+ color: $colour_grey;
24
+
25
+ &.super-legend {
26
+ color: $colour_text;
27
+ }
28
+ }
29
+
30
+ .label-api {
31
+ display: inline-block;
32
+ padding-right: 5px;
33
+ }
34
+
35
+ .label-api-full {
36
+ width: 50px;
37
+ text-align: right;
38
+ }
39
+
40
+ .breadcrumb-controller {
41
+ font-weight: bold;
42
+ color: $colour_grey;
43
+ }
44
+
45
+ #show-api-response-div {
46
+ pre.nowrap {
47
+ overflow-x: auto;
48
+ white-space: pre;
49
+ word-wrap: normal;
50
+ }
51
+ }
@@ -0,0 +1,5 @@
1
+ module ApiTaster
2
+ class ApplicationController < ActionController::Base
3
+ layout proc { |controller| controller.request.xhr? ? nil : 'api_taster/application' }
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ module ApiTaster
2
+ class RoutesController < ApplicationController
3
+ def index
4
+ @routes = Route.grouped_routes
5
+ end
6
+
7
+ def show
8
+ @route = Route.find(params[:id])
9
+ @inputs = Route.inputs_for(@route)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ module ApiTaster
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ <div class="control-group">
2
+ <label class="control-label" for="<%= label %>"><%= defined?(label_text) ? label_text : label %></label>
3
+ <div class="controls">
4
+ <input type="text" name="<%= label %>" value="<%= value %>"></input>
5
+ </div>
6
+ </div>
@@ -0,0 +1 @@
1
+ <legend><%= label %></legend>
@@ -0,0 +1,7 @@
1
+ <div class="alert alert-error">
2
+ <p><strong><%= "Route '#{route[:verb]} #{route[:path]}' is undefined." %></strong></p>
3
+ <p>Please define this route in <code>routes.rb</code>:</p>
4
+ <pre class="prettyprint lang-rb">ApiTaster.routes do
5
+ <%= route[:verb].downcase %> '<%= route[:path] %>', {}
6
+ end</pre>
7
+ </div>
@@ -0,0 +1,24 @@
1
+ <div class="row">
2
+ <div class="span5">
3
+ <ul class="well nav nav-list">
4
+ <% @routes.each do |controller, routes| %>
5
+ <li class="nav-header"><%= controller %></li>
6
+ <% routes.each do |route| %>
7
+ <li>
8
+ <a class="show-api" href="<%= route_path(route[:id]) %>">
9
+ <div class="label-api label-api-full">
10
+ <span class="label label-important"><%= route[:verb] %></span>
11
+ </div>
12
+ <strong><%= route[:path] %></strong>
13
+ </a>
14
+ </li>
15
+ <% end %>
16
+ <% end %>
17
+ </ul>
18
+ </div>
19
+ <div id="show-api-div" class="span7">
20
+ <div class="alert alert-block alert-info">
21
+ <h3>Select an API endpoint on the left to get started. :)</h3>
22
+ </div>
23
+ </div>
24
+ </div>
@@ -0,0 +1,55 @@
1
+ <ul class="breadcrumb">
2
+ <li class="breadcrumb-controller"><%= @route[:reqs][:controller].humanize %><span class="divider">/</span></li>
3
+ <li>
4
+ <div class="label-api">
5
+ <span class="label label-important"><%= @route[:verb] %></span>
6
+ </div>
7
+ <strong><%= @route[:path] %></strong>
8
+ </li>
9
+ </ul>
10
+
11
+ <% if @inputs.is_a?(Hash) && @inputs.has_key?(:undefined) %>
12
+ <%= render 'undefined_route', :route => @inputs[:undefined] %>
13
+ <% else %>
14
+ <% @inputs.each do |input| %>
15
+ <%= form_tag @route[:path], :method => @route[:verb], :class => 'well form-horizontal', :remote => true do %>
16
+
17
+ <% if input[:url_params].empty? && input[:post_params].empty? %>
18
+ <div class="alert alert-info">
19
+ No params specified.
20
+ </div>
21
+ <% end %>
22
+
23
+ <% if input[:url_params].present? %>
24
+ <fieldset id="url-params">
25
+ <legend class="super-legend">URL Params</legend>
26
+ <% input[:url_params].each do |label, value| %>
27
+ <%= render 'param_form_element', :label => "[api_taster_url_params]#{label}", :value => value, :label_text => label %>
28
+ <% end %>
29
+ </fieldset>
30
+ <% end %>
31
+
32
+ <% if input[:post_params].present? %>
33
+ <fieldset>
34
+ <legend class="super-legend">Post Params</legend>
35
+ <%= ApiTaster::FormBuilder.new(input[:post_params]).html.html_safe %>
36
+ </fieldset>
37
+ <% end %>
38
+
39
+ <div class="form-actions">
40
+ <input id="submit-api" type="submit" class="btn btn-primary pull-right"></input>
41
+ </div>
42
+
43
+ <% end %>
44
+ <% end %>
45
+ <% end %>
46
+
47
+ <div id="show-api-response-div" class="well hidden">
48
+ <legend class="super-legend">Response</legend>
49
+ <ul class="nav nav-tabs">
50
+ <li class="active"><a href="#" id="response-json">JSON</a></li>
51
+ <li><a href="#" id="response-raw">Raw</a></li>
52
+ </ul>
53
+ <pre class="prettyprint nowrap" ref="response-json"></pre>
54
+ <pre class="prettyprint hidden" ref="response-raw"></pre>
55
+ </div>
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>API Taster</title>
5
+ <%= stylesheet_link_tag "api_taster/application", :media => "all" %>
6
+ <%= javascript_include_tag "api_taster/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+ <div class="navbar navbar-fixed-top">
11
+ <div class="navbar-inner">
12
+ <div class="container">
13
+ <h1><a class="brand" href="<%= root_path %>">API Taster</a></h1>
14
+ <div class="nav-collapse collapse">
15
+ <ul class="nav pull-right">
16
+ <li>
17
+ <a href="/" target="_blank"><%= Rails.application.class.name %> <i class="icon-share-alt icon-white"></i></a>
18
+ </li>
19
+ </ul>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ </div>
24
+
25
+ <div class="container">
26
+ <%= yield %>
27
+ </div>
28
+ </body>
29
+ </html>
@@ -0,0 +1,5 @@
1
+ ApiTaster::Engine.routes.draw do
2
+ resources :routes, :only => [:index, :show]
3
+
4
+ root :to => 'routes#index'
5
+ end
@@ -0,0 +1,13 @@
1
+ require 'active_support/dependencies'
2
+ require 'api_taster/engine'
3
+ require 'api_taster/route'
4
+ require 'api_taster/mapper'
5
+ require 'api_taster/form_builder'
6
+
7
+ module ApiTaster
8
+ def self.routes(&block)
9
+ Route.route_set = Rails.application.routes
10
+ Route.inputs = {}
11
+ Mapper.instance_eval(&block)
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ module ApiTaster
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ApiTaster
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec, :view_specs => false
7
+ end
8
+
9
+ silence_warnings do
10
+ begin
11
+ require 'pry'
12
+ IRB = Pry
13
+ rescue LoadError
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ module ApiTaster
2
+ class FormBuilder < AbstractController::Base
3
+ include AbstractController::Rendering
4
+ include ActionView::Context
5
+ include ActionView::Helpers::CaptureHelper
6
+
7
+ self.view_paths = ApiTaster::Engine.root.join('app/views')
8
+
9
+ def initialize(params)
10
+ flush_output_buffer
11
+ @_buffer = ''
12
+ add_to_buffer(params)
13
+ end
14
+
15
+ def html
16
+ @_buffer
17
+ end
18
+
19
+ private
20
+
21
+ def add_to_buffer(params, parent_labels = [])
22
+ params.each do |label, value|
23
+ if value.is_a?(String)
24
+ @_buffer += render(
25
+ :partial => 'api_taster/routes/param_form_element',
26
+ :locals => {
27
+ :label => "#{print_labels(parent_labels)}#{label}",
28
+ :label_text => label,
29
+ :value => value
30
+ }
31
+ )
32
+ else
33
+ parent_labels << label
34
+
35
+ @_buffer += render(
36
+ :partial => 'api_taster/routes/param_form_legend',
37
+ :locals => { :label => print_labels(parent_labels) }
38
+ )
39
+
40
+ add_to_buffer(value, parent_labels)
41
+ end
42
+ end
43
+ end
44
+
45
+ def print_labels(parent_labels)
46
+ "[#{parent_labels * ']['}]"
47
+ end
48
+ end
49
+ end