skeleton 0.2.0 → 0.3.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -69
  3. data/Rakefile +2 -7
  4. data/lib/skeleton.rb +14 -4
  5. data/lib/skeleton/attributes.rb +19 -0
  6. data/lib/skeleton/config.rb +38 -0
  7. data/lib/skeleton/contact.rb +17 -0
  8. data/lib/skeleton/documentation.rb +17 -0
  9. data/lib/skeleton/example.rb +20 -6
  10. data/lib/skeleton/header.rb +67 -0
  11. data/lib/skeleton/headers.rb +50 -0
  12. data/lib/skeleton/helpers/controller_helpers.rb +25 -0
  13. data/lib/skeleton/info.rb +40 -0
  14. data/lib/skeleton/item.rb +99 -0
  15. data/lib/skeleton/license.rb +16 -0
  16. data/lib/skeleton/model.rb +31 -0
  17. data/lib/skeleton/operation.rb +157 -0
  18. data/lib/skeleton/parameter.rb +100 -20
  19. data/lib/skeleton/path.rb +111 -0
  20. data/lib/skeleton/response.rb +59 -0
  21. data/lib/skeleton/responses.rb +59 -0
  22. data/lib/skeleton/schema.rb +70 -0
  23. data/lib/skeleton/scopes.rb +24 -0
  24. data/lib/skeleton/security_definitions.rb +46 -0
  25. data/lib/skeleton/security_requirement.rb +29 -0
  26. data/lib/skeleton/security_scheme.rb +47 -0
  27. data/lib/skeleton/serializers/swagger.rb +212 -0
  28. data/lib/skeleton/structure.rb +191 -0
  29. data/lib/skeleton/tag.rb +24 -0
  30. data/lib/skeleton/version.rb +1 -1
  31. data/spec/integrations/use_case_spec.rb +130 -0
  32. data/spec/skeleton/operation_spec.rb +113 -0
  33. data/spec/skeleton/serializers/contact_spec.rb +30 -0
  34. data/spec/skeleton/serializers/documentation_spec.rb +23 -0
  35. data/spec/skeleton/serializers/header_spec.rb +57 -0
  36. data/spec/spec_helper.rb +8 -0
  37. metadata +36 -15
  38. data/lib/skeleton/action.rb +0 -46
  39. data/lib/skeleton/builder.rb +0 -47
  40. data/lib/skeleton/link.rb +0 -57
  41. data/spec/skeleton/integrated_spec.rb +0 -38
  42. data/test/skeleton/action_test.rb +0 -78
  43. data/test/skeleton/builder_test.rb +0 -51
  44. data/test/skeleton/link_test.rb +0 -68
  45. data/test/test_helper.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7ccc3daece70c6ebfaeb928ee50c713b1bddea5b
4
- data.tar.gz: 1c4592b173ffc9e0124e1cfdf9e4783fb7444f07
3
+ metadata.gz: 61b5c57ad3f55a99c4f8f7b0c671d7e3bad289e2
4
+ data.tar.gz: 4532bd18c8c705881bce6e06660e402448dc37ad
5
5
  SHA512:
6
- metadata.gz: e73b48301a296dbadfb21f3d4ab2fb7e0a33f8b8e907a0928a0e8a5f14883c9aed5613aa513b6ba72c802e3f07034c7ecb762c23daefb6e2dfa70ea6cf75168d
7
- data.tar.gz: 1e92ae4c2dd11c136e75d3f5aa1d6d21fb60f910b20ab23815c5af29177f2db42e94d985629487c75e57c5674622bf1af8d5e76c99c6ca7b7308111eec1d2651
6
+ metadata.gz: bcfa898ec87b7a53c0c7409629222b8aa200544f72e1f10b78ad01c13e58af7ccf8d265defbecaedb1191c50156168357b34a7fec3284f9cf78e5e73e7e91ba5
7
+ data.tar.gz: 61969916dcf9824aa13baa48a6bcd9931ef35097afb3e40ee3a944182bad472332cf2106eea437bb6a285c9cfe84880fa705ed35d6666163e774e7560a4a0e8b
data/README.md CHANGED
@@ -1,89 +1,112 @@
1
1
  # Skeleton
2
2
 
3
- Skeleton is a tool to help people construct `OPTIONS` api responses. This
4
- library is simply a data structure with no ties to any single framework.
3
+ Was born from the desire for api documentation that can live along side actively
4
+ running code.
5
5
 
6
6
  ## Example
7
7
 
8
8
  ```ruby
9
- require 'skeleton'
10
-
11
- skeleton = Skeleton.build do |config|
12
- config.define(:get, also: :head) do |action|
13
- action.description = 'Display a list of resources'
14
-
15
- action.param('limit') do |p|
16
- p.type = 'integer'
17
- p.description = 'The number of items desired'
18
- p.required = false
19
- p.restriction('Minimum value is 0')
20
- p.restriction('Maximum value is 9000')
21
- end
9
+ # You would put this somewhere such as config/initializers/skeleton.rb
10
+ Skeleton.configure do |config|
11
+ config.define do |structure|
12
+ structure.host = 'api.example.com'
13
+ structure.base_path = '/foo'
14
+
15
+ structure.schemes = %w(https)
16
+ structure.consumes = %(application/json)
17
+ structure.produces = %(application/json)
18
+ end
22
19
 
23
- action.param('offset') do |p|
24
- p.type = 'integer'
25
- p.description = 'The offset within the collection'
26
- p.required = false
27
- p.restriction('Minimum value is 0')
28
- end
20
+ config.info do |info|
21
+ info.version = '1.0.6'
22
+ info.title = 'An Example API'
23
+ info.description = 'An api to interact with data'
24
+ info.terms_of_service = 'https://api.example.com/terms/'
25
+ end
29
26
 
30
- action.example do |e|
31
- e.param('limit', 10),
32
- e.param('offset', 0)
33
- end
27
+ config.contact do |contact|
28
+ contact.name = 'WarmWaffles'
29
+ contact.email = 'warmwaffles@gmail.com'
30
+ contact.url = 'https://github.com/warmwaffles/skeleton'
31
+ end
34
32
 
35
- action.link(name: 'Self', rel: 'self', href: 'https://api.example.org/resources')
33
+ config.license do |license|
34
+ license.name = 'MIT'
36
35
  end
36
+ end
37
+ ```
38
+
39
+ Inside of your rails controller you would do the following
37
40
 
38
- config.link(name: 'Documentation', rel: 'docs', href: 'https://docs.example.org/resources')
41
+ ```ruby
42
+ class ApplicationController < ActionController::Base
43
+ include Skeleton::Helpers::ControllerHelpers
39
44
  end
45
+ ```
46
+
47
+ Inside a controller that you wish to document do the following
40
48
 
41
- skeleton.to_h
49
+ ```ruby
50
+ class AccountsController < ApplicationController
51
+ define_api_path('/accounts') do |path|
52
+ path.get do |operation|
53
+ operation.tag('account')
54
+ operation.description = 'List all of the accounts available to you'
55
+
56
+ operation.parameter(:query, 'limit') do |p|
57
+ p.description = 'maximum number of results to return'
58
+ p.type = 'integer'
59
+ p.format = 'int32'
60
+ p.required = false
61
+ end
62
+ operation.parameter(:query, 'offset') do |p|
63
+ p.description = 'offset within the results returned'
64
+ p.type = 'integer'
65
+ p.format = 'int32'
66
+ p.required = false
67
+ end
68
+ end
69
+ end
70
+
71
+ define_api_path('/accounts/{account_id}') do |path|
72
+ path.get do |operation|
73
+ operation.tag('account')
74
+ operation.description = 'Get an account'
75
+ operation.parameter(:query, 'account_id') do |p|
76
+ p.description = 'The account id'
77
+ p.type = 'string'
78
+ p.format = 'uuid'
79
+ p.required = true
80
+ end
81
+ end
82
+
83
+ path.options do |operation|
84
+ operation.tag('account')
85
+ operation.description = 'Show available options for the account'
86
+ operation.parameter(:query, 'account_id') do |p|
87
+ p.description = 'The account id'
88
+ p.type = 'string'
89
+ p.format = 'uuid'
90
+ p.required = true
91
+ end
92
+ end
93
+ end
94
+ end
42
95
  ```
43
96
 
44
- Example `Skeleton::Builder#to_h` call
97
+ If you want to dump the documentation to swagger you can simply do the
98
+ following:
45
99
 
46
100
  ```ruby
47
- {
48
- "links"=>[],
49
- "GET"=>{
50
- "description"=>"Display a list of resources",
51
- "parameters"=>{
52
- "limit"=>{
53
- "type"=>"integer",
54
- "description"=>"The number of items desired",
55
- "required"=>false,
56
- "allowed"=>[],
57
- "restrictions"=>[
58
- "Minimum value is 0",
59
- "Maximum value is 9000"
60
- ]
61
- },
62
- "offset"=>{
63
- "type"=>"integer",
64
- "description"=>"The offset within the collection",
65
- "required"=>false,
66
- "allowed"=>[],
67
- "restrictions"=>[
68
- "Minimum value is 0"
69
- ]
70
- }
71
- },
72
- "links"=>[
73
- {
74
- "name"=>"Self",
75
- "rel"=>"self",
76
- "href"=>"https://api.example.org/resources"
77
- }
78
- ],
79
- "examples"=>[
80
- {
81
- "limit"=>10,
82
- "offset"=>0
83
- }
84
- ]
85
- }
86
- }
101
+ class DocumentationController < ApplicationController
102
+ def swagger
103
+ respond_to do |format|
104
+ format.json do
105
+ render(json: Skeleton.config.to_swagger_json, status: 200)
106
+ end
107
+ end
108
+ end
109
+ end
87
110
  ```
88
111
 
89
112
  ## Testing
data/Rakefile CHANGED
@@ -2,12 +2,7 @@ require 'bundler/gem_tasks'
2
2
 
3
3
  namespace :test do
4
4
  task :env do
5
- $LOAD_PATH.unshift('lib', 'spec', 'test')
6
- end
7
-
8
- desc 'Runs only the units in this project'
9
- task :units => [:env] do
10
- Dir.glob('./test/**/*_test.rb') { |f| require f }
5
+ $LOAD_PATH.unshift('lib', 'spec')
11
6
  end
12
7
 
13
8
  desc 'Runs only the specs in this project'
@@ -16,7 +11,7 @@ namespace :test do
16
11
  end
17
12
 
18
13
  desc 'Runs all of the tests within this project'
19
- task :all => [:units, :specs]
14
+ task :all => [:specs]
20
15
  end
21
16
 
22
17
  desc 'Runs all of the tests within this project'
@@ -1,10 +1,20 @@
1
1
  require 'skeleton/version'
2
- require 'skeleton/builder'
2
+ require 'skeleton/structure'
3
+ require 'skeleton/config'
4
+ require 'skeleton/helpers/controller_helpers'
3
5
 
4
6
  module Skeleton
5
7
  def self.build(&block)
6
- builder = Builder.new
7
- yield(builder) if block
8
- builder
8
+ structure = Skeleton::Structure.new
9
+ yield(structure) if block
10
+ structure
11
+ end
12
+
13
+ def self.config
14
+ @config ||= Skeleton::Config.new
15
+ end
16
+
17
+ def self.configure(&block)
18
+ yield(config) if block
9
19
  end
10
20
  end
@@ -0,0 +1,19 @@
1
+ module Skeleton
2
+ module Attributes
3
+ def attr_presence(*methods)
4
+ Array(methods).each do |method|
5
+ define_method("#{method}?") do
6
+ !!self.public_send(method.to_s)
7
+ end
8
+ end
9
+ end
10
+
11
+ def attr_not_empty(*methods)
12
+ Array(methods).each do |method|
13
+ define_method("#{method}?") do
14
+ !self.public_send(method.to_s).empty?
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ require 'skeleton/structure'
2
+ require 'skeleton/serializers/swagger'
3
+
4
+ module Skeleton
5
+ class Config
6
+ def structure
7
+ @structure ||= Skeleton::Structure.new
8
+ end
9
+
10
+ def info(&block)
11
+ yield(structure.info) if block
12
+ end
13
+
14
+ def define(&block)
15
+ yield(structure) if block
16
+ end
17
+
18
+ def contact(&block)
19
+ yield(structure.info.contact) if block
20
+ end
21
+
22
+ def license(&block)
23
+ yield(structure.info.license) if block
24
+ end
25
+
26
+ def path(resource, &block)
27
+ structure.path(resource, &block)
28
+ end
29
+
30
+ def parameter(location, name, &block)
31
+ structure.parameter(location, name, &block)
32
+ end
33
+
34
+ def to_swagger_json
35
+ structure.to_swagger_json
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ require 'skeleton/model'
2
+
3
+ module Skeleton
4
+ class Contact < Model
5
+ attr_accessor :name, :email, :url
6
+ attr_presence :name, :email, :url
7
+
8
+ def to_h
9
+ hash = {}
10
+ hash[:name] = name if name?
11
+ hash[:email] = email if email?
12
+ hash[:url] = url if url?
13
+ hash
14
+ end
15
+ alias_method :to_swagger_hash, :to_h
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'skeleton/model'
2
+
3
+ module Skeleton
4
+ class Documentation < Model
5
+
6
+ attr_accessor :description, :url
7
+ attr_presence :description, :url
8
+
9
+ def to_h
10
+ hash = {}
11
+ hash[:description] = description if description?
12
+ hash[:url] = url if url?
13
+ hash
14
+ end
15
+ alias_method :to_swagger_hash, :to_h
16
+ end
17
+ end
@@ -1,17 +1,31 @@
1
1
  module Skeleton
2
2
  class Example
3
3
  def initialize
4
- @params = {}
4
+ @mimes = {}
5
5
  end
6
6
 
7
- def param(key, value)
8
- p = key.to_s.downcase
9
- @params[p] = value
7
+ def []=(mime_type, value)
8
+ @mimes[mime_type] = value
9
+ end
10
+
11
+ def [](mime_type)
12
+ @mimes[mime_type]
10
13
  end
11
14
 
12
15
  def to_h
13
- @params
16
+ hash = {}
17
+ @mimes.each do |type, value|
18
+ hash[type] = value.respond_to?(:to_h) ? value.to_h : value
19
+ end
20
+ hash
21
+ end
22
+
23
+ def to_swagger_hash
24
+ hash = {}
25
+ @mimes.each do |type, value|
26
+ hash[type] = value.respond_to?(:to_h) ? value.to_swagger_hash : value
27
+ end
28
+ hash
14
29
  end
15
- alias_method :to_hash, :to_h
16
30
  end
17
31
  end
@@ -0,0 +1,67 @@
1
+ require 'skeleton/model'
2
+
3
+ module Skeleton
4
+ class Header < Model
5
+ attr_accessor :type, :format, :title, :description, :default, :multiple_of,
6
+ :maximum, :exclusive_maximum, :minimum, :exclusive_minimum,
7
+ :max_length, :min_length, :pattern, :max_items, :min_items,
8
+ :unique_items, :max_properties, :min_properties
9
+
10
+ attr_writer :enum
11
+ attr_presence :exclusive_maximum, :exclusive_minimum, :unique_items
12
+
13
+ def enum
14
+ @enum ||= []
15
+ end
16
+
17
+ def enum?
18
+ !enum.empty?
19
+ end
20
+
21
+ def to_h
22
+ hash = {}
23
+ hash[:description] = header.description if header.description?
24
+ hash[:type] = header.type if header.type?
25
+ hash[:format] = header.format if header.format?
26
+ hash[:items] = header.items if header.items?
27
+ hash[:collection_format] = header.collection_format if header.collection_format?
28
+ hash[:default] = header.default if header.default?
29
+ hash[:maximum] = header.maximum if header.maximum?
30
+ hash[:exclusive_maximum] = header.exclusive_maximum if header.exclusive_maximum?
31
+ hash[:minimum] = header.minimum if header.minimum?
32
+ hash[:exclusive_minimum] = header.exclusive_minimum if header.exclusive_minimum?
33
+ hash[:max_length] = header.max_length if header.max_length?
34
+ hash[:min_length] = header.min_length if header.min_length?
35
+ hash[:pattern] = header.pattern if header.pattern?
36
+ hash[:max_items] = header.max_items if header.max_items?
37
+ hash[:min_items] = header.min_items if header.min_items?
38
+ hash[:unique_items] = header.unique_items if header.unique_items?
39
+ hash[:enum] = header.enum if header.enum?
40
+ hash[:multiple_of] = header.multiple_of if header.multiple_of?
41
+ hash
42
+ end
43
+
44
+ def to_swagger_hash
45
+ hash = {}
46
+ hash[:description] = header.description if header.description?
47
+ hash[:type] = header.type if header.type?
48
+ hash[:format] = header.format if header.format?
49
+ hash[:items] = header.items if header.items?
50
+ hash[:collectionFormat] = header.collection_format if header.collection_format?
51
+ hash[:default] = header.default if header.default?
52
+ hash[:maximum] = header.maximum if header.maximum?
53
+ hash[:exclusiveMaximum] = header.exclusive_maximum if header.exclusive_maximum?
54
+ hash[:minimum] = header.minimum if header.minimum?
55
+ hash[:exclusiveMinimum] = header.exclusive_minimum if header.exclusive_minimum?
56
+ hash[:maxLength] = header.max_length if header.max_length?
57
+ hash[:minLength] = header.min_length if header.min_length?
58
+ hash[:pattern] = header.pattern if header.pattern?
59
+ hash[:maxItems] = header.max_items if header.max_items?
60
+ hash[:minItems] = header.min_items if header.min_items?
61
+ hash[:uniqueItems] = header.unique_items if header.unique_items?
62
+ hash[:enum] = header.enum if header.enum?
63
+ hash[:multipleOf] = header.multiple_of if header.multiple_of?
64
+ hash
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,50 @@
1
+ require 'skeleton/model'
2
+ require 'skeleton/header'
3
+
4
+ module Skeleton
5
+ class Headers < Model
6
+ def initialize(args={})
7
+ @fields = {}
8
+ args.each { |k, v| set(k, v)}
9
+ end
10
+
11
+ def get(name)
12
+ @fields[name]
13
+ end
14
+ alias_method :[], :get
15
+
16
+ def set(name, value)
17
+ case value
18
+ when Hash
19
+ @fields[name] = Skeleton::Header.new(value)
20
+ else
21
+ @fields[name] = value
22
+ end
23
+ end
24
+ alias_method :[]=, :set
25
+
26
+ def to_h
27
+ hash ={}
28
+ @fields.each do |name, value|
29
+ if value.respond_to?(:to_h)
30
+ hash[name] = value.to_h
31
+ else
32
+ hash[name] = value
33
+ end
34
+ end
35
+ hash
36
+ end
37
+
38
+ def to_swagger_hash
39
+ hash ={}
40
+ @fields.each do |name, value|
41
+ if value.respond_to?(:to_swagger_hash)
42
+ hash[name] = value.to_swagger_hash
43
+ else
44
+ hash[name] = value
45
+ end
46
+ end
47
+ hash
48
+ end
49
+ end
50
+ end