apic 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f34a4f4aab7e80104a44cadc30f087c953b02f26
4
- data.tar.gz: f3f170ca3ddf9ce1d90bdc222a9f31786dbf91e6
3
+ metadata.gz: 62e91d7da069396fc04033442798fcfa93717c2e
4
+ data.tar.gz: 09884be511addc4b3a701d8a16ce9e1373a08358
5
5
  SHA512:
6
- metadata.gz: 43bf31780842c732681233c1e55a9b2abbcb20a25d6182b80b5c6fa30f082d0143df9a3442f2dced1eb01596b2495f3bde042662cf5dfab4e2a088571e939891
7
- data.tar.gz: 0779a4e019559f14b99e10a0bfc7d463ff4ae2e42380438c6aa3dcdf5e4f6e51fd33163a1defa4ab8b589b738d120cd7897d91e103098d446f10cdcfa6b4928c
6
+ metadata.gz: 34c1269243b0ffe1e7908d577a8776703445a62dfd1cd4aea88a88f2d166e964580c7d4420d2a83296324709b3f8721364fcbb79b2e04f7f62bf97ad538c0a09
7
+ data.tar.gz: 4353172bb5077836055d5778326664e3b05a84a6347b4026d3c6f9ed4a65a0fff394e614d997d71bd35cd2ea08b733c8766b332bd72978f49fb09307787a162b
data/README.md CHANGED
@@ -1,7 +1,12 @@
1
- = Apic
1
+ = APIc
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/hsbt/whispered.png)](https://travis-ci.org/hsbt/whispered) [![Coverage Status](https://coveralls.io/repos/randym/apic/badge.png?branch=master)](https://coveralls.io/r/randym/apic) [![Dependency Status](https://gemnasium.com/randym/apic.png)](https://gemnasium.com/randym/apic) [![Code Climate](https://codeclimate.com/github/randym/apic.png)](https://codeclimate.com/github/randym/apic)
3
+ ## What is it?
4
4
 
5
+ APIc is a bolt on API console for Rails 3+ applications.
6
+ It rounds up your endpoints and makes it dead easy to configure, send, review and replay any request.
7
+
8
+
9
+ ![Screen 1](https://github.com/randym/apic/raw/master/sample.png)
5
10
 
6
11
  What you need to do?
7
12
 
@@ -11,35 +16,73 @@ add the gem to your Gemfile
11
16
  gem 'apic'
12
17
  ```
13
18
 
14
- require the gem in your application.rb
19
+ Run the generator!
15
20
 
16
21
  ```
17
- require 'apic'
22
+ rails generate apic:install
18
23
  ```
19
24
 
20
- mount the gem in your routes.rb
25
+ If you are using bundler:
21
26
 
22
27
  ```
23
- mount Apic::Engine => "/apic"
28
+ bundle exec rails generate apic:install
29
+
24
30
  ```
25
31
 
26
- ## and some groovy stuff too
32
+ Spin up your Rails application and navigate to
33
+
34
+ ```
35
+ localhost:3000/apic
36
+ ```
27
37
 
28
- Filter routes on a regex match
29
38
 
30
39
 
40
+ ## Configuration
41
+
42
+ ### Controller Parameters
43
+
44
+ APIc needs to know about the parameters your PUT, PATCH and UPDATE requests require.
45
+ To make this as simple and painless as possible, APIc exposes a DSL to your controllers so you can specify what you need.
46
+ APIc knows what type of routes are available and will automatically add in the _method parameter and value for PATCH and DELETE requests.
47
+
31
48
  ```
32
- Apic.routes_matcher = /\/api\//
49
+ class TestController << ActionController.base
50
+
51
+ apic_action_params create: [:name, :acceptance]
52
+
53
+ def create
54
+ # all your cool stuff that creates a new object
55
+ end
56
+
57
+ end
58
+ ```
59
+
60
+ ### Initializer
61
+
62
+ The apic:install generator will add in a default intializer providing examples of the route matching and authentication filtering.
63
+
33
64
  ```
65
+ config/initializers/apic.rb
66
+ ```
67
+
68
+ #### Route Matching
69
+
70
+ APIc will load all routes in your rails app by default under the assumption that you are building your api as a dedicated service.
71
+ If this is not the case, you can specify a matching regular expression that APIc will use to find your API routes.
34
72
 
35
- Tell us what your authorization filter is!
73
+ For example, if you have namespecs all of your api endpoints to /api/v1/ simply uncomment out the Apic.routes_matcher line in config/initializers/apic.rb
36
74
 
37
75
  ```
38
- Apic.authorization_filter = :authenticate
76
+ Apic.route_matcher = /\/api\/v1\//
39
77
  ```
40
78
 
79
+ #### Authentication filters
41
80
 
42
- git remote add origin https://github.com/randym/apic.git
81
+ When testing your API it is often convenient to know which routes require authentication as you will need to add those headers before sending your request.
82
+ If you specify in your configuration the before_filter you are using for authentication APIc will mark those routes as restricted.
43
83
 
84
+ ```
85
+ Apic.authentication_filter = :authenticate
86
+ ```
44
87
 
45
88
  This project rocks and uses MIT-LICENSE.
@@ -2,4 +2,5 @@ $(document).ready ->
2
2
  $('.http-headers').httpHeaders()
3
3
  $('.endpoints-component').endpoints()
4
4
  $('.console').apic_console(host: $('.console').data('host'))
5
+ $('.xhr-history').xhr_history()
5
6
  true
@@ -9,6 +9,7 @@ $.fn.extend
9
9
  params: '.parameter',
10
10
  console: '.console',
11
11
  console_log: '.console-log',
12
+ history: '.xhr-history',
12
13
  host: 'localhost:3000',
13
14
  request_timeout: 10000,
14
15
  timeout_callback: null,
@@ -16,6 +17,8 @@ $.fn.extend
16
17
 
17
18
  self = this
18
19
 
20
+ history = []
21
+
19
22
  settings = $.extend settings, options
20
23
 
21
24
  log_timeline = (method, uri) ->
@@ -25,13 +28,14 @@ $.fn.extend
25
28
 
26
29
  log_request = (xhr) ->
27
30
  self._start = new Date().getTime()
28
- console.log xhr
29
- log_message "Request URL", uri()
30
- log_message "Request Method", endpoint().verb
31
- log_message "Response Code", [xhr.status, xhr.statusText].join(' ')
32
- log_message "Request Headers", xhr.getAllResponseHeaders()
33
31
 
34
- log_message = (title, message) ->
32
+ $(settings.console_log).empty()
33
+ log_item "Request URL", uri()
34
+ log_item "Request Method", endpoint().verb
35
+ log_item "Response Code", [xhr.status, xhr.statusText].join(' ')
36
+ log_item "Request Headers", xhr.getAllResponseHeaders()
37
+
38
+ log_item = (title, message) ->
35
39
  $(settings.console_log).append('<div><span class="log-header">' + title + '</span>:<span class="log-data"> ' + message + '</span></div>')
36
40
 
37
41
  duration = ->
@@ -57,7 +61,6 @@ $.fn.extend
57
61
  path += query_string()
58
62
  path
59
63
 
60
- $('#do-me').on('click', -> send.apply self)
61
64
 
62
65
  value_for = (name) ->
63
66
  $(settings.params + ' [name="' + name + '"]').val()
@@ -89,14 +92,19 @@ $.fn.extend
89
92
  $(settings.console).text(xhr.responseText)
90
93
 
91
94
  log_request xhr
95
+ record(xhr)
92
96
 
93
97
  onerror = (response) ->
94
98
  console.log response
95
99
 
96
- request = () ->
100
+ request = (endpoint, headers, uri) ->
97
101
  xhr = new XMLHttpRequest
98
- xhr.open endpoint().verb, uri()
99
- set_headers xhr
102
+ xhr.open endpoint.verb, uri, true
103
+ $.each headers, (key, value) ->
104
+ if key is 'Authorization'
105
+ xhr.withCredentials = true
106
+ xhr.setRequestHeader(key, value)
107
+
100
108
  xhr.onerror = (xhr) ->
101
109
  onerror.apply self, [xhr]
102
110
  xhr.onload = (response) ->
@@ -105,6 +113,8 @@ $.fn.extend
105
113
 
106
114
  set_headers = (xhr) ->
107
115
  $.each headers(), (key, value) ->
116
+ if key is 'Authorization'
117
+ xhr.withCredentials = true
108
118
  xhr.setRequestHeader(key, value)
109
119
 
110
120
  validate_params = () ->
@@ -112,14 +122,34 @@ $.fn.extend
112
122
  for part in endpoint().parts
113
123
  if value_for(part) is ""
114
124
  missing.push(part)
115
- if missing.length > 0
116
- alert('url parts reqired: ' + missing)
125
+ if missing.length > 0
126
+ alert('url parts reqired: ' + missing)
117
127
  missing.length is 0
118
128
 
119
- send = () ->
129
+ record = (xhr) ->
130
+ history_item =
131
+ endpoint: endpoint()
132
+ headers: headers()
133
+ body: body()
134
+ uri: uri()
135
+ xhr: xhr
136
+
137
+ $(settings.history).trigger('add', [history_item])
138
+
139
+ send = (_point, _head, _bod, _u) ->
140
+ _point ||= endpoint()
141
+ _head ||= headers()
142
+ _bod ||= body()
143
+ _u ||= uri()
144
+
120
145
  if validate_params()
121
- xhr = request()
122
- if endpoint().verb is 'GET'
146
+ xhr = request(_point, _head, _u)
147
+ if _point.verb is 'GET'
123
148
  xhr.send()
124
149
  else
125
- xhr.send JSON.stringify(body)
150
+ data = JSON.stringify(_bod)
151
+ xhr.send(data)
152
+
153
+ $('#do-me').on('click', -> send.apply self, [null])
154
+ $(self).on('send', (e, point, head, bod, u) -> send.apply self, [point, head, bod, u])
155
+ true
@@ -13,7 +13,6 @@ $.fn.extend
13
13
 
14
14
  $.each endpoints, (key, value) ->
15
15
  option = $('<option>'+ key + '</option>')
16
- console.log value
17
16
  option.text('[restricted] ' + option.text()) if value.authorization_required
18
17
  $(settings.select).append(option)
19
18
 
@@ -32,7 +31,7 @@ $.fn.extend
32
31
  el.find('#input-_method').val(endpoint.verb.toLowerCase())
33
32
 
34
33
  selected = ->
35
- endpoints[$(settings.select).val()]
34
+ endpoints[$(settings.select).val().replace('[restricted] ', '')]
36
35
 
37
36
  field_set_for = (name, options={}) ->
38
37
  clone = $(self).find(settings.template).clone()
@@ -1,7 +1,7 @@
1
1
  $.fn.extend
2
2
  httpHeaders: (options) ->
3
3
  settings =
4
- presets: {}
4
+ presets: {'Content-Type': 'application/json'}
5
5
 
6
6
  $.each $(this).data(), (key, value) ->
7
7
  settings[key] = value
@@ -0,0 +1,48 @@
1
+
2
+ $.fn.extend
3
+
4
+ xhr_history: (options) ->
5
+ defaults = {}
6
+ self = this
7
+ $(self).data('items', [])
8
+
9
+ refresh = ->
10
+ $(self).empty()
11
+ $.each items(), (index, item) ->
12
+ el = list_item(item)
13
+ el.data('request', item)
14
+
15
+ $(self).append el
16
+
17
+ list_item = (item) ->
18
+ $('<li class="xhr-history-item ' + item.xhr.statusText.toLowerCase() + '"><a href="#"><i class="icon-refresh icon-white ' + item.xhr.statusText.toLowerCase() + '"/><i>' + item.xhr.status + ' '+ item.endpoint.verb + ' ' + item.uri + '</span></a></li>')
19
+
20
+ items = ->
21
+ $(self).data('items')
22
+
23
+ add = (item)->
24
+ i = items()
25
+ i.push item
26
+ $(self).data('items',i)
27
+ refresh()
28
+
29
+ replay = (el) ->
30
+ request = $(el).data('request')
31
+ $('.console').trigger('send', [request.endpoint, request.headers, request.body, request.uri])
32
+ console.log($(el).data('request'))
33
+
34
+ show = ->
35
+ el = $('.history')
36
+ el.toggle()
37
+
38
+ li = $(event.target).closest('li')
39
+ if $(el).is(':visible')
40
+ li.addClass('active')
41
+ else
42
+ li.removeClass('active')
43
+
44
+ #$(self).on('refresh', -> refresh.apply self)
45
+ $(self).on('add', (e, item) -> add.apply self, [item])
46
+ $('.xhr-history').on('dblclick', 'li', (e) -> replay this)
47
+ $('.toggle-history').on('click', -> show.apply self)
48
+ this
@@ -10,9 +10,6 @@
10
10
  margin-left: 10px;
11
11
  min-height: 130px;
12
12
  overflow-y: scroll;
13
- h4 {
14
- text-align: center;
15
- }
16
13
  .http-headers {
17
14
  ul {
18
15
  border: 1px solid #eee;
@@ -0,0 +1,16 @@
1
+ .history {
2
+ display: none;
3
+ border-top: 1px solid #DDD;
4
+ padding: 5px;
5
+ }
6
+ .xhr-history {
7
+ list-style-type: none;
8
+ i.forbidden {
9
+ background-color: red;
10
+ margin-right: 5px;
11
+ }
12
+ i.ok {
13
+ background-color: green;
14
+ margin-right: 5px;
15
+ }
16
+ }
@@ -1,4 +1,12 @@
1
1
  module Apic
2
2
  class ApplicationController < ActionController::Base
3
+ def index
4
+ Rails.logger.debug endpoints
5
+ endpoints
6
+ end
7
+
8
+ def endpoints
9
+ @endpoints ||= Apic.endpoints
10
+ end
3
11
  end
4
12
  end
@@ -7,3 +7,5 @@ div.navbar.navbar-inverse.navbar-fixed-top
7
7
  a(href="#headers") Headers
8
8
  li.edit-endpoints.active
9
9
  a(href="#endpoints") Endpoints
10
+ li.toggle-history
11
+ a(href='#history') History
@@ -5,7 +5,7 @@ div.endpoints-component( data-endpoints="#{@endpoints.to_json}")
5
5
  div.control-group
6
6
  label.control-label(for="selectEndpoint") Api Endpoint
7
7
  div.controls
8
- select(id='selectEndpoint')
8
+ select.input-xxlarge(id='selectEndpoint')
9
9
 
10
10
  div.control-group.template
11
11
  label.control-label(for="inputParam") Param Name
@@ -0,0 +1,2 @@
1
+ h4 History
2
+ ul.xhr-history
@@ -8,3 +8,5 @@ div.container
8
8
  button#do-me.btn.btn-primary(href='#') Send
9
9
  div.row
10
10
  == render :partial => "apic/application/components/console"
11
+ div.row.history
12
+ == render :partial => "apic/application/components/xhr_history"
data/config/routes.rb CHANGED
@@ -1,3 +1,12 @@
1
- Apic::Engine.routes.draw do
2
- root "#index"
1
+ if Rails.version < '4.0.0'
2
+
3
+ Apic::Engine.routes.draw do
4
+ match "/" => "application#index"
5
+ end
6
+ else
7
+
8
+
9
+ Apic::Engine.routes.draw do
10
+ get "/" => "application#index"
11
+ end
3
12
  end
data/lib/apic/engine.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  module Apic
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Apic
4
+ require 'slim'
4
5
  require 'jquery-rails'
5
6
  require 'bootstrap-sass'
6
7
  ActiveSupport.on_load(:action_controller) do
@@ -0,0 +1,22 @@
1
+ require 'rails/generators'
2
+ module Apic
3
+ module Generators
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../../generators/templates", __FILE__)
6
+
7
+ desc "creates initializer and add engine mount point to your routes"
8
+
9
+ def copy_initializer
10
+ template 'apic.rb', 'config/initializers/apic.rb'
11
+ end
12
+
13
+ def mount_engine
14
+ if Rails.version < '4.0.0'
15
+ route 'mount Apic::Engine, :at => "/apic"'
16
+ else
17
+ route 'mount Apic::Engine => "/apic"'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ # Specify a route matcher to restrict which application routes are available
2
+ # in the console. By default all routes will be loaded
3
+ #
4
+ # Apic.route_matcher = /\/api\/v1\//
5
+ #
6
+ #
7
+ # Specify your authentication filter. Requests that use this filter will be marked
8
+ # as restricted in the Api endpoints list.
9
+ #
10
+ # Apic.authentication_filter = :authenticate
data/lib/apic/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Apic
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/apic.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  require 'apic/params_cache'
2
2
  require "apic/extension"
3
3
  require "apic/engine"
4
-
4
+ require "apic/generators/install_generator"
5
5
  module Apic
6
6
 
7
- mattr_accessor :authorization_filter
8
- @@authorization_filter = nil
7
+ mattr_accessor :authentication_filter
8
+ @@authentication_filter = nil
9
9
 
10
10
  mattr_accessor :route_matcher
11
11
  @@route_matcher = /\/api\//
@@ -35,12 +35,11 @@ module Apic
35
35
  end
36
36
 
37
37
  def self.requires_authorization(controller, action_name)
38
- return false unless @@authorization_filter
38
+ return false unless @@authentication_filter
39
39
  controller = (controller + '_controller').camelize.constantize
40
40
  controller._process_action_callbacks.any? do |callback|
41
- p @@authorization_filter
42
41
  eval <<-RUBY_EVAL
43
- #{callback.filter == @@authorization_filter} && #{callback.instance_values['compiled_options']}
42
+ #{callback.filter == @@authentication_filter} && #{callback.instance_values['compiled_options']}
44
43
  RUBY_EVAL
45
44
  end
46
45
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Randy Morgan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-01 00:00:00.000000000 Z
11
+ date: 2013-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
- version: 4.0.0
19
+ version: 3.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
- version: 4.0.0
26
+ version: 3.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: slim-rails
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -104,25 +104,29 @@ extra_rdoc_files: []
104
104
  files:
105
105
  - app/assets/javascripts/apic/apic.js.coffee
106
106
  - app/assets/javascripts/apic/application.js
107
- - app/assets/javascripts/apic/components/console.js.coffee
107
+ - app/assets/javascripts/apic/components/apic_console.js.coffee
108
108
  - app/assets/javascripts/apic/components/endpoints.js.coffee
109
109
  - app/assets/javascripts/apic/components/http-headers.js.coffee
110
+ - app/assets/javascripts/apic/components/xhr_history.js.coffee
110
111
  - app/assets/stylesheets/apic/application.css.scss
111
112
  - app/assets/stylesheets/apic/components/console.css.scss
112
113
  - app/assets/stylesheets/apic/components/endpoints.css.scss
113
114
  - app/assets/stylesheets/apic/components/http-headers.css.scss
115
+ - app/assets/stylesheets/apic/components/xhr-history.css.scss
114
116
  - app/controllers/apic/application_controller.rb
115
- - app/controllers/apic/controller.rb
116
117
  - app/helpers/apic/application_helper.rb
117
118
  - app/views/apic/application/_navigation.html.slim
118
119
  - app/views/apic/application/components/_console.html.slim
119
120
  - app/views/apic/application/components/_endpoints.html.slim
120
121
  - app/views/apic/application/components/_http_headers.html.slim
122
+ - app/views/apic/application/components/_xhr_history.html.slim
121
123
  - app/views/apic/application/index.html.slim
122
124
  - app/views/layouts/apic/application.html.erb
123
125
  - config/routes.rb
124
126
  - lib/apic/engine.rb
125
127
  - lib/apic/extension.rb
128
+ - lib/apic/generators/install_generator.rb
129
+ - lib/apic/generators/templates/apic.rb
126
130
  - lib/apic/params_cache.rb
127
131
  - lib/apic/version.rb
128
132
  - lib/apic.rb
@@ -1,13 +0,0 @@
1
- module Apic
2
- class Controller < ApplicationController
3
-
4
-
5
- def index
6
- endpoints
7
- end
8
-
9
- def endpoints
10
- @endpoints ||= Apic.endpoints
11
- end
12
- end
13
- end