avocado-docs 2.0.5

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.
@@ -0,0 +1,22 @@
1
+ module Avocado
2
+ class AvocadoController < ActionController::Base
3
+
4
+ def create
5
+ File.open(yaml, 'w+') { |f| f.write params[:file].read }
6
+ head :ok
7
+ end
8
+
9
+ def index
10
+ hash = YAML.load File.read(yaml)
11
+ @data = Avocado::Parser.new(hash)
12
+ render '/template'
13
+ end
14
+
15
+ private
16
+
17
+ def yaml
18
+ Avocado::Config.yaml_path.join('avocado.yml')
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ module Avocado
2
+ class SpecsController < AvocadoController
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Avocado
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,13 @@
1
+ module Avocado
2
+ class Endpoint
3
+ include ActiveModel::Model
4
+
5
+ attr_accessor :signature, :requests
6
+
7
+ def initialize(*)
8
+ @requests = SortedSet.new
9
+ super
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,56 @@
1
+ module Avocado
2
+ class Parser
3
+
4
+ attr_reader :resources, :endpoints, :requests
5
+
6
+ def initialize(json)
7
+ @resources = SortedSet.new
8
+ @endpoints = Set.new
9
+ @requests = Set.new
10
+ parse(json)
11
+ end
12
+
13
+ def resource(name)
14
+ @resources.find { |r| r.name == name }
15
+ end
16
+
17
+ private
18
+
19
+ def parse(json)
20
+ json.each do |req|
21
+ resource = find_or_create_resource_by_name req[:resource][:name]
22
+ endpoint = find_or_create_endpoint_by_signature resource, req[:request]
23
+ request = instantiate_request_object_from(req)
24
+ request.endpoint = endpoint
25
+
26
+ if request.unique?(@requests)
27
+ endpoint.requests << request
28
+ resource.endpoints << endpoint
29
+ @resources << resource
30
+ @endpoints << endpoint
31
+ @requests << request
32
+ end
33
+ end
34
+ end
35
+
36
+ def instantiate_request_object_from(params)
37
+ Avocado::Request.new \
38
+ path: params[:request][:path],
39
+ params: params[:request][:params],
40
+ headers: params[:request][:headers],
41
+ status: params[:response][:status],
42
+ body: params[:response][:body],
43
+ description: params[:description]
44
+ end
45
+
46
+ def find_or_create_resource_by_name(name)
47
+ resource(name) || Avocado::Resource.new(name: name)
48
+ end
49
+
50
+ def find_or_create_endpoint_by_signature(resource, request)
51
+ signature = "#{request[:method]} #{request[:path]}"
52
+ resource.endpoints.find { |e| e.signature == signature } || Avocado::Endpoint.new(signature: signature)
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,37 @@
1
+ module Avocado
2
+ class Request
3
+ include ActiveModel::Model
4
+
5
+ attr_accessor :uid, :url, :path, :params, :status, :body, :headers
6
+ attr_accessor :endpoint, :description
7
+
8
+ def initialize(*)
9
+ super
10
+ self.uid = SecureRandom.hex
11
+ end
12
+
13
+ def ==(other)
14
+ other.path == path &&
15
+ other.params == params &&
16
+ other.status == status &&
17
+ other.body == body
18
+ end
19
+
20
+ def <=>(other)
21
+ status <=> other.status
22
+ end
23
+
24
+ def unique?(requests)
25
+ requests.none? { |req| self == req }
26
+ end
27
+
28
+ def params
29
+ @params || {}
30
+ end
31
+
32
+ def body
33
+ @body || {}
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ module Avocado
2
+ class Resource
3
+ include ActiveModel::Model
4
+
5
+ attr_accessor :name, :endpoints
6
+
7
+ def initialize(*)
8
+ @endpoints = Set.new
9
+ super
10
+ end
11
+
12
+ def <=>(other)
13
+ name <=> other.name
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>API Docs</title>
5
+ <%= stylesheet_link_tag "avocado/application", media: "all" %>
6
+ <%= javascript_include_tag "avocado/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,54 @@
1
+ <div id="avocado">
2
+
3
+ <ul id="sidebar">
4
+ <% @data.resources.each do |resource| %>
5
+ <li><a href="#<%= resource.name %>"><%= resource.name %></a></li>
6
+ <ul class="dropdown">
7
+ <% @data.resource(resource.name).endpoints.map(&:signature).uniq.each do |signature| %>
8
+ <li><a href="#<%= resource.name %>"><%= signature %></a></li>
9
+ <% end %>
10
+ </ul>
11
+ <% end %>
12
+ </ul>
13
+
14
+ <div id="requests">
15
+ <% @data.resources.each do |resource| %>
16
+ <a name="<%= resource.name %>"></a>
17
+ <h4><%= resource.name %></h4>
18
+ <ul class="request-endpoints">
19
+ <% resource.endpoints.each do |endpoint| %>
20
+ <% endpoint.requests.each do |request| %>
21
+ <li>
22
+ <a href="#" data-request-uid="<%= request.uid %>">
23
+ <span class="status"><%= request.status %></span>
24
+ <span class="url"><%= endpoint.signature %></span>
25
+ <span class="description"><%= request.description %></span>
26
+ </a>
27
+ </li>
28
+ <% end %>
29
+ <% end %>
30
+ </ul>
31
+ <% end %>
32
+ </div>
33
+
34
+ <div id="docs">
35
+ <% @data.requests.each do |req| %>
36
+ <div class="request" data-uid="<%= req.uid %>">
37
+ <h4><%= req.endpoint.signature %></h4>
38
+ <code class="request-parameters">
39
+ <%= JSON.pretty_generate req.params.to_h %>
40
+ </code>
41
+ <code class="example-headers">
42
+ <% req.headers.each do |name, value| %>
43
+ <%= name %>: <%= value %>
44
+ <% end %>
45
+ </code>
46
+ <code class="response-status"><%= req.status %></code>
47
+ <code class="response-body">
48
+ <%= JSON.pretty_generate JSON[req.body] rescue '' %>
49
+ <div class="clearfix"></div>
50
+ </code>
51
+ </div>
52
+ <% end %>
53
+
54
+ </div>
@@ -0,0 +1,4 @@
1
+ Avocado::Engine.routes.draw do
2
+ root to: 'specs#index'
3
+ resources :specs, only: [:create]
4
+ end
@@ -0,0 +1,31 @@
1
+ require 'avocado/engine'
2
+ require 'avocado/config'
3
+ require 'avocado/uploader'
4
+ require 'avocado/controller'
5
+ require 'avocado/cache'
6
+ require 'avocado/middleware'
7
+ require 'avocado/middleware/defaults'
8
+
9
+ require 'yaml'
10
+ require 'net/http/post/multipart'
11
+
12
+ module Avocado
13
+ @payload = []
14
+
15
+ class << self
16
+ attr_accessor :payload
17
+ end
18
+
19
+ def self.upload!
20
+ return if @payload.empty?
21
+ File.open('avocado.yml', 'w+') { |file| file.write payload.to_yaml }
22
+ Avocado::Uploader.new.upload('avocado.yml') if Avocado::Config.url
23
+ end
24
+
25
+ def self.reset!
26
+ self.payload = []
27
+ Avocado::Config.reset!
28
+ Avocado::Cache.clean
29
+ end
30
+
31
+ end
@@ -0,0 +1,20 @@
1
+ # Temporarily store a JSON request/response. Ultimately RSpec will determine
2
+ # if this request/response gets documented or not in an after(:each) block
3
+ module Avocado
4
+ class Cache
5
+ class << self
6
+
7
+ attr_accessor :request, :response, :json
8
+
9
+ def store(request, response)
10
+ @json = {}
11
+ @request, @response = request, response
12
+ end
13
+
14
+ def clean
15
+ @json = @request = @response = nil
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ module Avocado
2
+ class Config
3
+ class << self
4
+
5
+ attr_accessor :url, :headers, :document_if, :yaml_path, :ignored_params
6
+
7
+ def configure(&block)
8
+ yield self
9
+ end
10
+
11
+ def headers
12
+ Array(@headers) || []
13
+ end
14
+
15
+ def document_if
16
+ @document_if || -> { true }
17
+ end
18
+
19
+ def yaml_path
20
+ @yaml_path || Rails.application.root
21
+ end
22
+
23
+ def ignored_params
24
+ @ignored_params ||= ['controller', 'action']
25
+ end
26
+
27
+ def reset!
28
+ @headers = @document_if = @yaml_path = @ignored_params = nil
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ # This concern gets patched into ActionController::Base during testing
2
+ # The after_action will ensure every request gets documented regardless of the
3
+ # type of test (controller, integration, etc)
4
+ module Avocado
5
+ module Controller
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ after_action -> do
10
+ Avocado::Cache.clean
11
+ Avocado::Cache.store(request, response) if documentable?
12
+ end
13
+ end
14
+
15
+ def documentable?
16
+ response.body.blank? || !!JSON.parse(response.body)
17
+ rescue
18
+ false
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module Avocado
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Avocado
4
+ end
5
+ end
@@ -0,0 +1,37 @@
1
+ module Avocado
2
+ class Middleware
3
+
4
+ attr_accessor :entries
5
+
6
+ def initialize
7
+ @entries = []
8
+ end
9
+
10
+ def <<(klass)
11
+ @entries << klass
12
+ end
13
+
14
+ def invoke(*args, &finally)
15
+ chain = entries.map(&:new).dup
16
+ traversal = -> (continue = true) do
17
+ return if !continue
18
+ if chain.empty?
19
+ finally.call
20
+ else
21
+ chain.shift.call(*args, &traversal)
22
+ end
23
+ end
24
+ traversal.call
25
+ end
26
+
27
+ def self.configure(&block)
28
+ @chain ||= new
29
+ yield @chain
30
+ end
31
+
32
+ def self.invoke(*args, &block)
33
+ @chain.invoke(*args, &block)
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ require 'avocado/middleware/document_metadata'
2
+ require 'avocado/middleware/document_if_configuration'
3
+ require 'avocado/middleware/request_serialization'
4
+ require 'avocado/middleware/response_serialization'
5
+ require 'avocado/middleware/resource_serialization'
6
+ require 'avocado/middleware/example_serialization'
7
+
8
+ Avocado::Middleware.configure do |chain|
9
+ chain << Avocado::Middleware::DocumentMetadata
10
+ chain << Avocado::Middleware::DocumentIfConfiguration
11
+ chain << Avocado::Middleware::RequestSerialization
12
+ chain << Avocado::Middleware::ResponseSerialization
13
+ chain << Avocado::Middleware::ResourceSerialization
14
+ chain << Avocado::Middleware::ExampleSerialization
15
+ end
@@ -0,0 +1,12 @@
1
+ module Avocado
2
+ class Middleware::DocumentIfConfiguration
3
+
4
+ # return false if document_if returns false, so it will
5
+ # stop the middleware chain and not upload anything
6
+ def call(*)
7
+ continue = Avocado::Config.document_if.call
8
+ yield continue
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Avocado
2
+ class Middleware::DocumentMetadata
3
+
4
+ # return false if the :document metadata is given and is explicitly false
5
+ def call(example, *)
6
+ if example.metadata[:document] == false
7
+ yield false
8
+ else
9
+ yield
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Avocado
2
+ class Middleware::ExampleSerialization
3
+
4
+ def call(example, *)
5
+ Avocado::Cache.json[:description] = example.description
6
+ yield
7
+ end
8
+
9
+ end
10
+ end