skeleton 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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