skeleton 0.3.3 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +140 -65
  4. data/Rakefile +10 -3
  5. data/lib/skeleton.rb +6 -10
  6. data/lib/skeleton/contact.rb +1 -13
  7. data/lib/skeleton/error.rb +4 -0
  8. data/lib/skeleton/graph.rb +56 -0
  9. data/lib/skeleton/header.rb +2 -63
  10. data/lib/skeleton/items.rb +34 -0
  11. data/lib/skeleton/license.rb +1 -12
  12. data/lib/skeleton/model.rb +13 -17
  13. data/lib/skeleton/operation.rb +50 -121
  14. data/lib/skeleton/parameter.rb +31 -88
  15. data/lib/skeleton/parameters.rb +40 -0
  16. data/lib/skeleton/path.rb +42 -80
  17. data/lib/skeleton/presenter.rb +19 -0
  18. data/lib/skeleton/property.rb +6 -0
  19. data/lib/skeleton/response.rb +24 -45
  20. data/lib/skeleton/schema.rb +80 -63
  21. data/lib/skeleton/scope.rb +18 -0
  22. data/lib/skeleton/security_scheme.rb +19 -37
  23. data/lib/skeleton/serializers/options.rb +215 -0
  24. data/lib/skeleton/serializers/swagger.rb +197 -0
  25. data/lib/skeleton/structure.rb +92 -138
  26. data/lib/skeleton/swagger.rb +9 -0
  27. data/lib/skeleton/tag.rb +11 -16
  28. data/lib/skeleton/version.rb +1 -1
  29. data/skeleton.gemspec +2 -0
  30. data/test/fixtures/json-schema-draft-04.json +150 -0
  31. data/test/fixtures/schema.json +1482 -0
  32. data/test/integrations/validate_complex_schema_spec.rb +42 -0
  33. data/test/skeleton/graph_test.rb +22 -0
  34. data/test/skeleton/mapper_test.rb +84 -0
  35. data/test/skeleton/operation_test.rb +11 -0
  36. data/test/skeleton/parameter_test.rb +34 -0
  37. data/test/skeleton/parameters_test.rb +9 -0
  38. data/test/skeleton/path_test.rb +46 -0
  39. data/test/skeleton/property_test.rb +8 -0
  40. data/test/skeleton/serializers/options_test.rb +68 -0
  41. data/test/skeleton/serializers/swagger_test.rb +30 -0
  42. data/test/support/factories/structure_factory.rb +86 -0
  43. data/test/support/fixtures.rb +6 -0
  44. data/test/support/kissmetrics/core_api.rb +542 -0
  45. data/{spec/spec_helper.rb → test/test_helper.rb} +7 -1
  46. metadata +73 -25
  47. data/lib/skeleton/config.rb +0 -37
  48. data/lib/skeleton/documentation.rb +0 -17
  49. data/lib/skeleton/example.rb +0 -31
  50. data/lib/skeleton/headers.rb +0 -50
  51. data/lib/skeleton/helpers/controller_helpers.rb +0 -25
  52. data/lib/skeleton/info.rb +0 -40
  53. data/lib/skeleton/item.rb +0 -99
  54. data/lib/skeleton/responses.rb +0 -59
  55. data/lib/skeleton/scopes.rb +0 -24
  56. data/lib/skeleton/security_definitions.rb +0 -46
  57. data/lib/skeleton/security_requirement.rb +0 -29
  58. data/spec/integrations/use_case_spec.rb +0 -131
  59. data/spec/skeleton/operation_spec.rb +0 -113
  60. data/spec/skeleton/serializers/contact_spec.rb +0 -30
  61. data/spec/skeleton/serializers/documentation_spec.rb +0 -23
  62. data/spec/skeleton/serializers/header_spec.rb +0 -57
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc11d07464eef522de2f6495353a454b7ffc5373
4
- data.tar.gz: edbbaf9c7d4af49d479690a8baa8e8fc9bb01ccb
3
+ metadata.gz: 9294bad43773b066e5ce9b168b882617f614d8a2
4
+ data.tar.gz: ff3a33117c6924c76cec5478162571f44f761c07
5
5
  SHA512:
6
- metadata.gz: 3fd5c0bc49baafa724cb849bbd1ae8a0bab61e0fa6aeb715458bf91909c7f77071841a38533c1120e0b5aff4eed7d9d3f4b277993de4bf591de5e00c4d1e6583
7
- data.tar.gz: f90dbc5845e73cc978acd224d0e5db77b7bf3444ddab5dc9bb51af2f3e99eb495989e4f30644b62a2d08c506311a8f613210e869c5b7661559c27a299b450f4b
6
+ metadata.gz: b22eccf0dbd13780cb453fe23a6c24391dd47de28786b113be1944acc7d5c0e79bc2b2889ec3e5c7d0947b1dc50472fbe28d944bdc5c2a8401e64dfe65136ad4
7
+ data.tar.gz: e8de75ff865e644fcd3e6966b8f541a5710fef50e3f6e95ff3f2d1d80590e36973d465717b904e55ed4c44632bda3d0e933e1b07a03cfc7c44ea79dc3be8ca3a
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ .DS_Store
data/README.md CHANGED
@@ -7,88 +7,162 @@ running code.
7
7
 
8
8
  ```ruby
9
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
10
+ structure = Skeleton.build do |s|
11
+ s.title = 'KISSmetrics API'
12
+ s.version = '1.0.0'
13
+ s.description = 'A simply complex skeleton'
19
14
 
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
15
+ s.terms = 'https://www.kissmetrics.com/terms'
26
16
 
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
17
+ s.contact.name = 'KISSmetrics'
18
+ s.contact.email = 'support@kissmetrics.com'
19
+ s.contact.url = 'https://support.kissmetrics.com'
32
20
 
33
- config.license do |license|
34
- license.name = 'MIT'
35
- end
21
+ s.license.name = 'KISSmetrics'
22
+ s.license.url = 'https://www.kissmetrics.com'
23
+
24
+ s.host = 'api.kissmetrics.com'
25
+ s.base_path = '/core'
26
+ s.scheme(:https)
27
+ s.consume('application/json')
28
+ s.produce('application/json')
36
29
  end
37
30
  ```
38
31
 
39
- Inside of your rails controller you would do the following
32
+ ## Defining models
40
33
 
41
34
  ```ruby
42
- class ApplicationController < ActionController::Base
43
- include Skeleton::Helpers::ControllerHelpers
35
+ structure.define_model('Link') do
36
+ describe('The link object used for api discovery')
37
+ required(:href, type: 'string')
38
+ required(:rel, type: 'string')
39
+ optional(:templated, type: 'boolean')
40
+ optional(:name, type: 'string')
41
+ end
42
+
43
+ structure.define_model('Error') do
44
+ describe('Represents an error')
45
+ required(:message, type: 'string')
46
+ optional(:field, type: 'string')
47
+ optional(:errors, type: 'array', items: { ref: 'Error' })
48
+ end
49
+
50
+ structure.define_model('Meta') do
51
+ describe('The meta information regarding the request')
52
+ required(:status, type: 'integer')
53
+ optional(:message, type: 'string')
54
+ optional(:errors, type: 'array', items: { ref: 'Error' })
44
55
  end
45
56
  ```
46
57
 
47
- Inside a controller that you wish to document do the following
58
+ If you wish to reference a definition from within another model, all you need to
59
+ do is to pass a `:ref`. If you want to know more about this structure, please
60
+ see the swagger documentation.
61
+
62
+ ## Defining paths
48
63
 
49
64
  ```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
65
+ skeleton.define_path('/products') do
66
+ get do
67
+ identify('list-products')
68
+ tag('product')
69
+ summarize('List products')
70
+ describe('List all of the products')
71
+
72
+ parameters(:query) do
73
+ optional(:limit, type: 'integer', format: 'int32', default: 20, maximum: 50, minimum: 0)
74
+ optional(:offset, type: 'integer', format: 'int32', default: 0)
75
+ end
76
+
77
+ response(200, default: true) do
78
+ describe('Product list')
79
+ header('Link', type: 'string', description: 'The list of links for the resource')
80
+ schema(ref: 'Product')
81
+ end
82
+
83
+ response(403) do
84
+ describe('Unauthorized')
85
+ header('Link', type: 'string', description: 'The list of links for the resource')
86
+ schema(ref: 'ErrorResponse')
68
87
  end
69
88
  end
70
89
 
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
90
+ head do
91
+ tag('product')
92
+ summarize('List products headers')
93
+ describe('List the headers for the products action')
94
+
95
+ parameters(:query) do
96
+ optional(:limit, type: 'integer', format: 'int32', default: 20, maximum: 50, minimum: 0)
97
+ optional(:offset, type: 'integer', format: 'int32', default: 0)
81
98
  end
82
99
 
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
100
+ response(200, default: true) do
101
+ describe('Product list')
102
+ header('Link', type: 'string', description: 'The list of links for the resource')
103
+ end
104
+
105
+ response(403) do
106
+ describe('Unauthorized')
107
+ header('Link', type: 'string', description: 'The list of links for the resource')
108
+ end
109
+ end
110
+
111
+ post do
112
+ tag('product')
113
+ summarize('Create product')
114
+ describe('Create a product')
115
+
116
+ parameters(:body) do
117
+ required(:body, schema: { ref: 'NewProduct' })
118
+ end
119
+
120
+ response(201) do
121
+ describe('Product created')
122
+ header('Link', type: 'string', description: 'The list of links for the resource')
123
+ schema(ref: 'Product')
124
+ end
125
+
126
+ response(400) do
127
+ describe('Client error')
128
+ header('Link', type: 'string', description: 'The list of links for the resource')
129
+ schema(ref: 'ErrorResponse')
130
+ end
131
+
132
+ response(403) do
133
+ describe('Unauthorized')
134
+ header('Link', type: 'string', description: 'The list of links for the resource')
135
+ schema(ref: 'ErrorResponse')
136
+ end
137
+ end
138
+
139
+ options do
140
+ tag('product')
141
+ summarize('List actions for a product')
142
+ describe('List actions for a product')
143
+
144
+ parameters(:query) do
145
+ optional(:limit, type: 'integer', format: 'int32', default: 20, maximum: 50, minimum: 0)
146
+ optional(:offset, type: 'integer', format: 'int32', default: 0)
147
+ end
148
+
149
+ response(200, default: true) do
150
+ describe('Product action list')
151
+ header('Allow', type: 'array', items: { type: 'string' }, collection_format: 'csv')
152
+ header('Link', type: 'string', description: 'The list of links for the resource')
153
+ schema(ref: 'OptionsResponse')
154
+ end
155
+
156
+ response(400) do
157
+ describe('Client error')
158
+ header('Link', type: 'string', description: 'The list of links for the resource')
159
+ schema(ref: 'ErrorResponse')
160
+ end
161
+
162
+ response(403) do
163
+ describe('Unauthorized')
164
+ header('Link', type: 'string', description: 'The list of links for the resource')
165
+ schema(ref: 'ErrorResponse')
92
166
  end
93
167
  end
94
168
  end
@@ -102,7 +176,8 @@ class DocumentationController < ApplicationController
102
176
  def swagger
103
177
  respond_to do |format|
104
178
  format.json do
105
- render(json: Skeleton.config.to_swagger_json, status: 200)
179
+ serializer = Skeleton::Serializers::Swagger.new(my_structure)
180
+ render(json: MultiJson.dump(serializer.to_h), status: 200)
106
181
  end
107
182
  end
108
183
  end
data/Rakefile CHANGED
@@ -2,16 +2,23 @@ require 'bundler/gem_tasks'
2
2
 
3
3
  namespace :test do
4
4
  task :env do
5
- $LOAD_PATH.unshift('lib', 'spec')
5
+ $LOAD_PATH.unshift('lib', 'test')
6
6
  end
7
7
 
8
8
  desc 'Runs only the specs in this project'
9
9
  task :specs => [:env] do
10
- Dir.glob('./spec/**/*_spec.rb') { |f| require f }
10
+ Dir.glob('./test/**/*_spec.rb') { |f| require f }
11
+ end
12
+
13
+ desc 'Runs only the test units in this project'
14
+ task :units => [:env] do
15
+ Dir.glob('./test/**/*_test.rb') { |f| require f }
11
16
  end
12
17
 
13
18
  desc 'Runs all of the tests within this project'
14
- task :all => [:specs]
19
+ task :all => [:env] do
20
+ Dir.glob('./test/**/*_{spec,test}.rb') { |f| require f }
21
+ end
15
22
  end
16
23
 
17
24
  desc 'Runs all of the tests within this project'
@@ -1,20 +1,16 @@
1
1
  require 'skeleton/version'
2
2
  require 'skeleton/structure'
3
- require 'skeleton/config'
4
- require 'skeleton/helpers/controller_helpers'
3
+ require 'skeleton/serializers/swagger'
4
+ require 'skeleton/serializers/options'
5
5
 
6
6
  module Skeleton
7
+ # Allows you to build a skeleton structure from within a block
8
+ #
9
+ # @return [Skeleton::Structure]
7
10
  def self.build(&block)
8
11
  structure = Skeleton::Structure.new
9
12
  yield(structure) if block
10
13
  structure
11
14
  end
12
-
13
- def self.config
14
- @config ||= Skeleton::Config.new
15
- end
16
-
17
- def self.configure(&block)
18
- yield(config) if block
19
- end
20
15
  end
16
+
@@ -1,17 +1,5 @@
1
- require 'skeleton/model'
2
-
3
1
  module Skeleton
4
- class Contact < Model
2
+ class Contact
5
3
  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
4
  end
17
5
  end
@@ -0,0 +1,4 @@
1
+ module Skeleton
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,56 @@
1
+ require 'set'
2
+ require 'tsort'
3
+
4
+ module Skeleton
5
+ class Graph
6
+ include Enumerable
7
+ include TSort
8
+
9
+ def initialize
10
+ @dep = Hash.new { |h,k| h[k] = Set.new }
11
+ end
12
+
13
+ # Register a dependency
14
+ #
15
+ # @param base [Object] the base object
16
+ # @param list [Object,Array] list of dependents
17
+ def register(base, *list)
18
+ dependents = Array(list).flatten
19
+ @dep[base] ||= Set.new
20
+ @dep[base].merge(dependents)
21
+ end
22
+
23
+ # Iterate over each of the nodes in this graph with their edge
24
+ #
25
+ # @return [void]
26
+ def each(&block)
27
+ @dep.each do |base, set|
28
+ set.each do |name|
29
+ yield(base, name) if block
30
+ end
31
+ end
32
+ end
33
+
34
+ # Takes all of the nodes in this graph and creates a set with them
35
+ #
36
+ # @return [Set]
37
+ def to_set
38
+ set = Set.new
39
+ @dep.each do |base, deps|
40
+ set.add(base)
41
+ set.merge(deps)
42
+ end
43
+ set
44
+ end
45
+
46
+ def each_dependent_for(base, &block)
47
+ each_strongly_connected_component_from(base) do |dependent, _|
48
+ yield(dependent)
49
+ end
50
+ end
51
+
52
+ def tsort_each_child(node, &block)
53
+ @dep[node].each(&block)
54
+ end
55
+ end
56
+ end
@@ -1,67 +1,6 @@
1
- require 'skeleton/model'
1
+ require 'skeleton/schema'
2
2
 
3
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
4
+ class Header < Schema
66
5
  end
67
6
  end