apiculture 0.1.6 → 0.2.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.
data/lib/apiculture.rb CHANGED
@@ -9,40 +9,41 @@ module Apiculture
9
9
  require_relative 'apiculture/markdown_segment'
10
10
  require_relative 'apiculture/timestamp_promise'
11
11
  require_relative 'apiculture/app_documentation'
12
-
12
+ require_relative 'apiculture/openapi_documentation'
13
+
13
14
  def self.extended(in_class)
14
15
  in_class.send(:include, SinatraInstanceMethods)
15
16
  super
16
17
  end
17
-
18
+
18
19
  IDENTITY_PROC = ->(arg) { arg }
19
-
20
+
20
21
  AC_APPLY_TYPECAST_PROC = ->(cast_proc_or_method, v) {
21
22
  cast_proc_or_method.is_a?(Symbol) ? v.public_send(cast_proc_or_method) : cast_proc_or_method.call(v)
22
23
  }
23
-
24
+
24
25
  AC_CHECK_PRESENCE_PROC = ->(name_as_string, params) {
25
26
  params.has_key?(name_as_string) or raise MissingParameter.new(name_as_string)
26
27
  }
27
-
28
+
28
29
  AC_CHECK_TYPE_PROC = ->(param, value) {
29
30
  param.matchable === value or raise ParameterTypeMismatch.new(param, value.class)
30
31
  }
31
-
32
+
32
33
  class Parameter < Struct.new(:name, :description, :required, :matchable, :cast_proc_or_method)
33
34
  # Return Strings since Sinatra prefers string keys for params{}
34
35
  def name_as_string; name.to_s; end
35
36
  end
36
-
37
+
37
38
  class RouteParameter < Parameter
38
39
  end
39
-
40
+
40
41
  class PossibleResponse < Struct.new(:http_status_code, :description, :jsonable_object_example)
41
42
  def no_body?
42
43
  jsonable_object_example.nil?
43
44
  end
44
45
  end
45
-
46
+
46
47
  # Indicates where this API will be mounted. This is only used
47
48
  # for the generated documentation. In general, this should match
48
49
  # the SCRIPT_NAME of the Sinatra application when it will be called.
@@ -60,13 +61,13 @@ module Apiculture
60
61
  def mounted_at(path)
61
62
  @apiculture_mounted_at = path.to_s.gsub(/\/$/, '')
62
63
  end
63
-
64
+
64
65
  # Inserts the generation timestamp into the documentation at this point.
65
66
  # The timestamp will be not very precise (to the minute) and in UTC time
66
67
  def documentation_build_time!
67
68
  apiculture_stack << Apiculture::TimestampPromise
68
69
  end
69
-
70
+
70
71
  # Inserts a literal Markdown string into the documentation at this point.
71
72
  # For instance, if used after an API method declaration, it will insert
72
73
  # the header between the API methods in the doc.
@@ -81,7 +82,7 @@ module Apiculture
81
82
  def markdown_string(str)
82
83
  apiculture_stack << MarkdownSegment.new(str)
83
84
  end
84
-
85
+
85
86
  # Inserts the contents of the file at +path+ into the documentation, using +markdown_string+.
86
87
  # For instance, if used after an API method declaration, it will insert
87
88
  # the header between the API methods in the doc.
@@ -94,25 +95,25 @@ module Apiculture
94
95
  md = File.read(path_to_markdown).encode(Encoding::UTF_8)
95
96
  markdown_string(md)
96
97
  end
97
-
98
+
98
99
  # Describe the API method that is going to be defined
99
100
  def desc(action_description)
100
101
  @apiculture_action_definition ||= ActionDefinition.new
101
102
  @apiculture_action_definition.description = action_description.to_s
102
103
  end
103
-
104
+
104
105
  # Add an optional parameter for the API call
105
106
  def param(name, description, matchable, cast: IDENTITY_PROC)
106
107
  @apiculture_action_definition ||= ActionDefinition.new
107
108
  @apiculture_action_definition.parameters << Parameter.new(name, description, required=false, matchable, cast)
108
109
  end
109
-
110
+
110
111
  # Add a requred parameter for the API call
111
112
  def required_param(name, description, matchable, cast: IDENTITY_PROC)
112
113
  @apiculture_action_definition ||= ActionDefinition.new
113
114
  @apiculture_action_definition.parameters << Parameter.new(name, description, required=true, matchable, cast)
114
115
  end
115
-
116
+
116
117
  # Describe a parameter that has to be included in the URL of the API call.
117
118
  # Route parameters are always required, and all the parameters specified
118
119
  # using +route_param+ should also be included in the path given for the route
@@ -121,28 +122,28 @@ module Apiculture
121
122
  @apiculture_action_definition ||= ActionDefinition.new
122
123
  @apiculture_action_definition.route_parameters << RouteParameter.new(name, description, required=false, matchable, cast)
123
124
  end
124
-
125
+
125
126
  # Add a possible response, specifying the code and the JSON Response by example.
126
127
  # Multiple response packages can be specified.
127
128
  def responds_with(http_status, description, example_jsonable_object = nil)
128
129
  @apiculture_action_definition ||= ActionDefinition.new
129
130
  @apiculture_action_definition.responses << PossibleResponse.new(http_status, description, example_jsonable_object)
130
131
  end
131
-
132
+
132
133
  DefinitionError = Class.new(StandardError)
133
134
  ValidationError = Class.new(StandardError)
134
-
135
+
135
136
  class RouteParameterNotInPath < DefinitionError; end
136
137
  class ReservedParameter < DefinitionError; end
137
138
  class ConflictingParameter < DefinitionError; end
138
-
139
+
139
140
  # Gets raised when a parameter is missing
140
141
  class MissingParameter < ValidationError
141
142
  def initialize(parameter_name)
142
143
  super "Missing parameter :#{parameter_name}"
143
144
  end
144
145
  end
145
-
146
+
146
147
  # Gets raised when a parameter is supplied and has a wrong type
147
148
  class ParameterTypeMismatch < ValidationError
148
149
  def initialize(ac_parameter, received_ruby_type)
@@ -151,7 +152,7 @@ module Apiculture
151
152
  super "Received #{received_type}, expected #{expected_type.inspect} for :#{parameter_name}"
152
153
  end
153
154
  end
154
-
155
+
155
156
  # Returns a Proc that calls the strong parameters to check the presence/types
156
157
  def parametric_validator_proc_from(parametric_validators, implicitly_defined_route_parameter_names)
157
158
  required_params = parametric_validators.select{|e| e.required }
@@ -163,23 +164,23 @@ module Apiculture
163
164
  parametric_validators.each do |param|
164
165
  param_name = param.name_as_string
165
166
  next unless params.has_key?(param_name) # this is checked via required_params
166
-
167
+
167
168
  # Apply the type cast and save it (since using our override we can mutate the params)
168
169
  value_after_type_cast = AC_APPLY_TYPECAST_PROC.call(param.cast_proc_or_method, params[param_name])
169
170
  params[param_name] = value_after_type_cast
170
-
171
+
171
172
  # Ensure the typecast value adheres to the enforced Ruby type
172
173
  AC_CHECK_TYPE_PROC.call(param, params[param_name])
173
174
  end
174
-
175
- # The following only applies if the app does not use strong_parameters -
175
+
176
+ # The following only applies if the app does not use strong_parameters -
176
177
  # this makes use of parameter mutability again to kill the parameters that are not permitted
177
178
  # or mentioned in the API specification. We need to keep the params which are specified in the
178
179
  # route but not documented via Apiculture though
179
180
  unexpected_parameters = Set.new(params.keys.map(&:to_s)) -
180
181
  Set.new(parametric_validators.map(&:name).map(&:to_s)) -
181
182
  Set.new(implicitly_defined_route_parameter_names.map(&:to_s))
182
-
183
+
183
184
  unexpected_parameters.each do | parameter_to_discard |
184
185
  # TODO: raise or record a warning
185
186
  if env['rack.logger'].respond_to?(:warn)
@@ -189,7 +190,7 @@ module Apiculture
189
190
  end
190
191
  }
191
192
  end
192
-
193
+
193
194
  # Serve the documentation for the API at the given URL
194
195
  def serve_api_documentation_at(url)
195
196
  get(url) do
@@ -197,7 +198,7 @@ module Apiculture
197
198
  self.class.api_documentation.to_html
198
199
  end
199
200
  end
200
-
201
+
201
202
  # Returns an +AppDocumentation+ object for all actions defined so far.
202
203
  #
203
204
  # MyApi.api_documentation.to_markdown #=> "..."
@@ -205,14 +206,14 @@ module Apiculture
205
206
  def api_documentation
206
207
  AppDocumentation.new(self, @apiculture_mounted_at.to_s, @apiculture_actions_and_docs || [])
207
208
  end
208
-
209
+
209
210
  # Define an API method. Under the hood will call the related methods in Sinatra
210
211
  # to define the route.
211
212
  def api_method(http_verb, path, options={}, &blk)
212
213
  action_def = (@apiculture_action_definition || ActionDefinition.new)
213
214
  action_def.http_verb = http_verb
214
215
  action_def.path = path
215
-
216
+
216
217
  # Ensure no reserved Sinatra parameters are used
217
218
  all_parameter_names = action_def.all_parameter_names_as_strings
218
219
  %w( splat captures ).each do | reserved_param |
@@ -220,34 +221,34 @@ module Apiculture
220
221
  raise ReservedParameter.new(":#{reserved_param} is a reserved magic parameter name in Sinatra")
221
222
  end
222
223
  end
223
-
224
+
224
225
  # Ensure no conflations between route/req params
225
226
  seen_params = {}
226
- all_parameter_names.each do |e|
227
+ all_parameter_names.each do |e|
227
228
  if seen_params[e]
228
- raise ConflictingParameter.new(":#{e} mentioned twice as a possible parameter. Note that URL" +
229
+ raise ConflictingParameter.new(":#{e} mentioned twice as a possible parameter. Note that URL" +
229
230
  " parameters and request parameters share a namespace.")
230
231
  else
231
232
  seen_params[e] = true
232
233
  end
233
234
  end
234
-
235
+
235
236
  # Ensure the path has the route parameters that were predeclared
236
237
  action_def.route_parameters.map(&:name).each do | route_parameter_key |
237
238
  unless path.include?(':%s' % route_parameter_key)
238
239
  raise RouteParameterNotInPath.new("Parameter :#{route_parameter_key} not present in path #{path.inspect}")
239
240
  end
240
241
  end
241
-
242
+
242
243
  # TODO: ensure all route parameters are documented
243
-
244
+
244
245
  # Pick out all the defined parameters and set up a block that can validate them
245
246
  # when the action is called. With that, set up the actual Sinatra method that will
246
247
  # respond to the request. We take care to preserve all the params that have NOT been documented
247
248
  # using Apiculture but _were_ in fact specified in the actual path.
248
249
  route_parameter_names = path.scan(/:([^:\/]+)/).flatten.map(&:to_sym)
249
250
  parametric_checker_proc = parametric_validator_proc_from(action_def.parameters + action_def.route_parameters, route_parameter_names)
250
- public_send(http_verb, path, options) do |*matched_sinatra_route_params|
251
+ public_send(http_verb, path, **options) do |*matched_sinatra_route_params|
251
252
  # Extract all the parameter names from the route path as given to the method
252
253
  route_parameters = Hash[route_parameter_names.zip(matched_sinatra_route_params)]
253
254
 
@@ -268,13 +269,13 @@ module Apiculture
268
269
  # Execute the original action via instance_exec, passing along the route args
269
270
  instance_exec(*route_parameters.values, &blk)
270
271
  end
271
-
272
+
272
273
  # Reset for the subsequent action definition
273
274
  @apiculture_action_definition = ActionDefinition.new
274
275
  # and store the just defined action for future use
275
276
  apiculture_stack << action_def
276
277
  end
277
-
278
+
278
279
  def apiculture_stack
279
280
  @apiculture_actions_and_docs ||= []
280
281
  @apiculture_actions_and_docs
metadata CHANGED
@@ -1,32 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apiculture
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
8
  - WeTransfer
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-01-27 00:00:00.000000000 Z
12
+ date: 2023-02-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: mustermann
15
+ name: builder
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: '1'
20
+ version: '3'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: '1'
27
+ version: '3'
28
28
  - !ruby/object:Gem::Dependency
29
- name: builder
29
+ name: github-markup
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - "~>"
@@ -40,21 +40,21 @@ dependencies:
40
40
  - !ruby/object:Gem::Version
41
41
  version: '3'
42
42
  - !ruby/object:Gem::Dependency
43
- name: rdiscount
43
+ name: mustache
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '2'
48
+ version: '1'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: '2'
55
+ version: '1'
56
56
  - !ruby/object:Gem::Dependency
57
- name: github-markup
57
+ name: mustermann
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - "~>"
@@ -68,61 +68,61 @@ dependencies:
68
68
  - !ruby/object:Gem::Version
69
69
  version: '3'
70
70
  - !ruby/object:Gem::Dependency
71
- name: mustache
71
+ name: rdiscount
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: '1'
76
+ version: '2'
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: '1'
83
+ version: '2'
84
84
  - !ruby/object:Gem::Dependency
85
- name: rack-test
85
+ name: bundler
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - ">="
88
+ - - "~>"
89
89
  - !ruby/object:Gem::Version
90
- version: '0'
90
+ version: '2.0'
91
91
  type: :development
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - ">="
95
+ - - "~>"
96
96
  - !ruby/object:Gem::Version
97
- version: '0'
97
+ version: '2.0'
98
98
  - !ruby/object:Gem::Dependency
99
- name: rspec
99
+ name: cgi
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - "~>"
102
+ - - ">="
103
103
  - !ruby/object:Gem::Version
104
- version: '3'
104
+ version: '0'
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - "~>"
109
+ - - ">="
110
110
  - !ruby/object:Gem::Version
111
- version: '3'
111
+ version: '0'
112
112
  - !ruby/object:Gem::Dependency
113
- name: rdoc
113
+ name: rack-test
114
114
  requirement: !ruby/object:Gem::Requirement
115
115
  requirements:
116
- - - "~>"
116
+ - - ">="
117
117
  - !ruby/object:Gem::Version
118
- version: '6.0'
118
+ version: '0'
119
119
  type: :development
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
- - - "~>"
123
+ - - ">="
124
124
  - !ruby/object:Gem::Version
125
- version: '6.0'
125
+ version: '0'
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: rake
128
128
  requirement: !ruby/object:Gem::Requirement
@@ -138,33 +138,33 @@ dependencies:
138
138
  - !ruby/object:Gem::Version
139
139
  version: '0'
140
140
  - !ruby/object:Gem::Dependency
141
- name: bundler
141
+ name: rdoc
142
142
  requirement: !ruby/object:Gem::Requirement
143
143
  requirements:
144
144
  - - "~>"
145
145
  - !ruby/object:Gem::Version
146
- version: '1.0'
146
+ version: '6.0'
147
147
  type: :development
148
148
  prerelease: false
149
149
  version_requirements: !ruby/object:Gem::Requirement
150
150
  requirements:
151
151
  - - "~>"
152
152
  - !ruby/object:Gem::Version
153
- version: '1.0'
153
+ version: '6.0'
154
154
  - !ruby/object:Gem::Dependency
155
- name: simplecov
155
+ name: rspec
156
156
  requirement: !ruby/object:Gem::Requirement
157
157
  requirements:
158
- - - ">="
158
+ - - "~>"
159
159
  - !ruby/object:Gem::Version
160
- version: '0'
160
+ version: '3'
161
161
  type: :development
162
162
  prerelease: false
163
163
  version_requirements: !ruby/object:Gem::Requirement
164
164
  requirements:
165
- - - ">="
165
+ - - "~>"
166
166
  - !ruby/object:Gem::Version
167
- version: '0'
167
+ version: '3'
168
168
  description: A toolkit for building REST APIs on top of Rack
169
169
  email: me@julik.nl
170
170
  executables: []
@@ -175,6 +175,7 @@ extra_rdoc_files:
175
175
  files:
176
176
  - ".gitignore"
177
177
  - ".travis.yml"
178
+ - CHANGELOG.md
178
179
  - Gemfile
179
180
  - LICENSE.txt
180
181
  - README.md
@@ -191,20 +192,16 @@ files:
191
192
  - lib/apiculture/indifferent_hash.rb
192
193
  - lib/apiculture/markdown_segment.rb
193
194
  - lib/apiculture/method_documentation.rb
195
+ - lib/apiculture/openapi_documentation.rb
194
196
  - lib/apiculture/sinatra_instance_methods.rb
195
197
  - lib/apiculture/timestamp_promise.rb
196
198
  - lib/apiculture/version.rb
197
- - spec/apiculture/action_spec.rb
198
- - spec/apiculture/app_documentation_spec.rb
199
- - spec/apiculture/method_documentation_spec.rb
200
- - spec/apiculture_spec.rb
201
- - spec/spec_helper.rb
202
199
  homepage: https://github.com/WeTransfer/apiculture
203
200
  licenses:
204
201
  - MIT
205
202
  metadata:
206
203
  allowed_push_host: https://rubygems.org
207
- post_install_message:
204
+ post_install_message:
208
205
  rdoc_options: []
209
206
  require_paths:
210
207
  - lib
@@ -219,8 +216,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
216
  - !ruby/object:Gem::Version
220
217
  version: '0'
221
218
  requirements: []
222
- rubygems_version: 3.0.3
223
- signing_key:
219
+ rubygems_version: 3.2.33
220
+ signing_key:
224
221
  specification_version: 4
225
222
  summary: Sweet API sauce on top of Rack
226
223
  test_files: []
@@ -1,45 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Apiculture::Action do
4
- context '.new' do
5
- it 'exposes the methods of the object given as a first argument to initialize' do
6
- action_class = Class.new(described_class)
7
- fake_app = double('Apiculture::App', something: 'value')
8
- action = action_class.new(fake_app)
9
- expect(action).to respond_to(:something)
10
- expect(action.something).to eq('value')
11
- end
12
-
13
- it 'converts keyword arguments to instance variables' do
14
- action_class = Class.new(described_class)
15
- action = action_class.new(nil, foo: 'a string')
16
- expect(action.instance_variable_get('@foo')).to eq('a string')
17
- end
18
- end
19
-
20
- it 'responds to perform()' do
21
- expect(described_class.new(nil)).to respond_to(:perform)
22
- end
23
-
24
- it 'can use bail() to throw a Sinatra halt' do
25
- fake_app = double('Apiculture::App')
26
- expect(fake_app).to receive(:json_halt).with('Failure', status: 400)
27
- action_class = Class.new(described_class)
28
- action_class.new(fake_app).bail "Failure"
29
- end
30
-
31
- it 'can use bail() to throw a Sinatra halt with a custom status' do
32
- fake_app = double('Apiculture::App')
33
- expect(fake_app).to receive(:json_halt).with("Failure", status: 417)
34
-
35
- action_class = Class.new(described_class)
36
- action_class.new(fake_app).bail "Failure", status: 417
37
- end
38
-
39
- it 'can use bail() to throw a Sinatra halt with extra JSON attributes' do
40
- fake_app = double('Apiculture::App')
41
- expect(fake_app).to receive(:json_halt).with("Failure", status: 417, message: "Totale")
42
- action_class = Class.new(described_class)
43
- action_class.new(fake_app).bail "Failure", status: 417, message: 'Totale'
44
- end
45
- end
@@ -1,126 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe "Apiculture.api_documentation" do
4
- let(:app) do
5
- Class.new(Apiculture::App) do
6
- extend Apiculture
7
-
8
- markdown_string 'This API is very important. Because it has to do with pancakes.'
9
-
10
- documentation_build_time!
11
-
12
- desc 'Order a pancake'
13
- required_param :diameter, "Diameter of the pancake. The pancake will be **bold**", Integer
14
- param :topping, 'Type of topping', String
15
- pancake_response_info = <<~EOS
16
- When the pancake has been baked successfully
17
- The pancake will have the following properties:
18
-
19
- * It is going to be round
20
- * It is going to be delicious
21
- EOS
22
- responds_with 200, pancake_response_info, { id: 'abdef..c21' }
23
- api_method :post, '/pancakes' do
24
- end
25
-
26
- desc 'Check the pancake status'
27
- route_param :id, 'Pancake ID to check status on'
28
- responds_with 200, 'When the pancake is found', { status: 'Baking' }
29
- responds_with 404, 'When no such pancake exists', { status: 'No such pancake' }
30
- api_method :get, '/pancake/:id' do
31
- end
32
-
33
- desc 'Throw away the pancake'
34
- route_param :id, 'Pancake ID to delete'
35
- api_method :delete, '/pancake/:id' do
36
- end
37
-
38
- desc 'Pancake ingredients are in the URL'
39
- route_param :topping_id, 'Pancake topping ID', Integer, cast: :to_i
40
- api_method :get, '/pancake/with/:topping_id' do |topping_id|
41
- end
42
- end
43
- end
44
-
45
- it 'generates app documentation as HTML without the body element' do
46
- docco = app.api_documentation
47
- generated_html = docco.to_html_fragment
48
-
49
- expect(generated_html).not_to include('<body')
50
- expect(generated_html).to include('Pancake ID to check status on')
51
- expect(generated_html).to include('Pancake ID to delete')
52
- end
53
-
54
- it 'generates app documentation in HTML' do
55
- docco = app.api_documentation
56
- generated_html = docco.to_html
57
-
58
- if ENV['SHOW_TEST_DOC']
59
- File.open('t.html', 'w') do |f|
60
- f.write(generated_html)
61
- f.flush
62
- `open #{f.path}`
63
- end
64
- end
65
-
66
- expect(generated_html).to include('<body')
67
- expect(generated_html).to include('Pancake ID to check status on')
68
- expect(generated_html).to include('When the pancake has been baked successfully')
69
- expect(generated_html).to include('"id": "abdef..c21"')
70
- expect(generated_html).to end_with("\n")
71
- end
72
-
73
- it 'generates app documentation in Markdown' do
74
- docco = app.api_documentation
75
- generated_markdown = docco.to_markdown
76
-
77
- expect(generated_markdown).not_to include('<body')
78
- expect(generated_markdown).to include('## POST /pancakes')
79
- end
80
-
81
- it 'generates app documentation honoring the mount point' do
82
- overridden = Class.new(Apiculture::App) do
83
- extend Apiculture
84
- mounted_at '/api/v2/'
85
- api_method :get, '/pancakes' do
86
- end
87
- end
88
-
89
- generated_markdown = overridden.api_documentation.to_markdown
90
- expect(generated_markdown).to include('## GET /api/v2/pancakes')
91
- end
92
-
93
- it 'generates app documentation injecting the inline Markdown strings' do
94
- app_class = Class.new(Apiculture::App) do
95
- extend Apiculture
96
- markdown_string '# This describes important stuff'
97
- api_method :get, '/pancakes' do
98
- end
99
- markdown_string '# This describes even more important stuff'
100
- markdown_string 'This is a paragraph'
101
- end
102
-
103
- generated_html = app_class.api_documentation.to_html
104
- expect(generated_html).to include('<h2>GET /pancakes</h2>')
105
- expect(generated_html).to include('<h1>This describes even more important stuff')
106
- expect(generated_html).to include('<h1>This describes important stuff')
107
- expect(generated_html).to include('<p>This is a paragraph')
108
- end
109
-
110
- context 'with a file containing Markdown that has to be spliced into the docs' do
111
- before(:each) { File.open('./TEST.md', 'w') { |f| f << "# This is an important header" } }
112
- after(:each) { File.unlink('./TEST.md') }
113
- it 'splices the contents of the file using markdown_file' do
114
- app_class = Class.new(Apiculture::App) do
115
- extend Apiculture
116
- markdown_file './TEST.md'
117
- api_method :get, '/pancakes' do
118
- end
119
- end
120
-
121
- generated_html = app_class.api_documentation.to_html
122
- expect(generated_html).to include('<h2>GET /pancakes</h2>')
123
- expect(generated_html).to include('<h1>This is an important header')
124
- end
125
- end
126
- end