apidoco_dsl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b2ac5c3c67fb00766a4f2b0e950d5a78fcb3856ca7aa326bda1190178ed4782b
4
+ data.tar.gz: c31cbdb3a36888731cfc8de10ff1126e745ba2fec09184f021655a81cd0dd7c3
5
+ SHA512:
6
+ metadata.gz: 63e799515b3cf792abed9424abee6afca94165a84851290ef7dafa7feb9dadb8915e765bb06e143453e26965a3aa87f89c80335f91be8f949bad94ff97193493
7
+ data.tar.gz: c668eb909680132161998c93c0d47e4e1bf0c98a402690af54f3a109ed88cbf7b16f11cd77209be3c3a6201c023abce1e45ba0409b58d68ba2eff0575b946e65
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.1
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in apidoco_dsl.gemspec
4
+ gemspec
@@ -0,0 +1,166 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ apidoco_dsl (0.1.0)
5
+ apidoco (~> 1.5)
6
+ kramdown
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (6.0.3.1)
12
+ actionpack (= 6.0.3.1)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailbox (6.0.3.1)
16
+ actionpack (= 6.0.3.1)
17
+ activejob (= 6.0.3.1)
18
+ activerecord (= 6.0.3.1)
19
+ activestorage (= 6.0.3.1)
20
+ activesupport (= 6.0.3.1)
21
+ mail (>= 2.7.1)
22
+ actionmailer (6.0.3.1)
23
+ actionpack (= 6.0.3.1)
24
+ actionview (= 6.0.3.1)
25
+ activejob (= 6.0.3.1)
26
+ mail (~> 2.5, >= 2.5.4)
27
+ rails-dom-testing (~> 2.0)
28
+ actionpack (6.0.3.1)
29
+ actionview (= 6.0.3.1)
30
+ activesupport (= 6.0.3.1)
31
+ rack (~> 2.0, >= 2.0.8)
32
+ rack-test (>= 0.6.3)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
35
+ actiontext (6.0.3.1)
36
+ actionpack (= 6.0.3.1)
37
+ activerecord (= 6.0.3.1)
38
+ activestorage (= 6.0.3.1)
39
+ activesupport (= 6.0.3.1)
40
+ nokogiri (>= 1.8.5)
41
+ actionview (6.0.3.1)
42
+ activesupport (= 6.0.3.1)
43
+ builder (~> 3.1)
44
+ erubi (~> 1.4)
45
+ rails-dom-testing (~> 2.0)
46
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
47
+ activejob (6.0.3.1)
48
+ activesupport (= 6.0.3.1)
49
+ globalid (>= 0.3.6)
50
+ activemodel (6.0.3.1)
51
+ activesupport (= 6.0.3.1)
52
+ activerecord (6.0.3.1)
53
+ activemodel (= 6.0.3.1)
54
+ activesupport (= 6.0.3.1)
55
+ activestorage (6.0.3.1)
56
+ actionpack (= 6.0.3.1)
57
+ activejob (= 6.0.3.1)
58
+ activerecord (= 6.0.3.1)
59
+ marcel (~> 0.3.1)
60
+ activesupport (6.0.3.1)
61
+ concurrent-ruby (~> 1.0, >= 1.0.2)
62
+ i18n (>= 0.7, < 2)
63
+ minitest (~> 5.1)
64
+ tzinfo (~> 1.1)
65
+ zeitwerk (~> 2.2, >= 2.2.2)
66
+ angularjs-rails (1.6.8)
67
+ apidoco (1.5.4)
68
+ angularjs-rails
69
+ rails (>= 4.0.0)
70
+ builder (3.2.4)
71
+ concurrent-ruby (1.1.6)
72
+ crass (1.0.6)
73
+ diff-lcs (1.3)
74
+ erubi (1.9.0)
75
+ globalid (0.4.2)
76
+ activesupport (>= 4.2.0)
77
+ i18n (1.8.2)
78
+ concurrent-ruby (~> 1.0)
79
+ kramdown (2.2.1)
80
+ rexml
81
+ loofah (2.5.0)
82
+ crass (~> 1.0.2)
83
+ nokogiri (>= 1.5.9)
84
+ mail (2.7.1)
85
+ mini_mime (>= 0.1.1)
86
+ marcel (0.3.3)
87
+ mimemagic (~> 0.3.2)
88
+ method_source (1.0.0)
89
+ mimemagic (0.3.5)
90
+ mini_mime (1.0.2)
91
+ mini_portile2 (2.4.0)
92
+ minitest (5.14.1)
93
+ nio4r (2.5.2)
94
+ nokogiri (1.10.9)
95
+ mini_portile2 (~> 2.4.0)
96
+ rack (2.2.2)
97
+ rack-test (1.1.0)
98
+ rack (>= 1.0, < 3)
99
+ rails (6.0.3.1)
100
+ actioncable (= 6.0.3.1)
101
+ actionmailbox (= 6.0.3.1)
102
+ actionmailer (= 6.0.3.1)
103
+ actionpack (= 6.0.3.1)
104
+ actiontext (= 6.0.3.1)
105
+ actionview (= 6.0.3.1)
106
+ activejob (= 6.0.3.1)
107
+ activemodel (= 6.0.3.1)
108
+ activerecord (= 6.0.3.1)
109
+ activestorage (= 6.0.3.1)
110
+ activesupport (= 6.0.3.1)
111
+ bundler (>= 1.3.0)
112
+ railties (= 6.0.3.1)
113
+ sprockets-rails (>= 2.0.0)
114
+ rails-dom-testing (2.0.3)
115
+ activesupport (>= 4.2.0)
116
+ nokogiri (>= 1.6)
117
+ rails-html-sanitizer (1.3.0)
118
+ loofah (~> 2.3)
119
+ railties (6.0.3.1)
120
+ actionpack (= 6.0.3.1)
121
+ activesupport (= 6.0.3.1)
122
+ method_source
123
+ rake (>= 0.8.7)
124
+ thor (>= 0.20.3, < 2.0)
125
+ rake (10.5.0)
126
+ rexml (3.2.4)
127
+ rspec (3.9.0)
128
+ rspec-core (~> 3.9.0)
129
+ rspec-expectations (~> 3.9.0)
130
+ rspec-mocks (~> 3.9.0)
131
+ rspec-core (3.9.2)
132
+ rspec-support (~> 3.9.3)
133
+ rspec-expectations (3.9.2)
134
+ diff-lcs (>= 1.2.0, < 2.0)
135
+ rspec-support (~> 3.9.0)
136
+ rspec-mocks (3.9.1)
137
+ diff-lcs (>= 1.2.0, < 2.0)
138
+ rspec-support (~> 3.9.0)
139
+ rspec-support (3.9.3)
140
+ sprockets (4.0.0)
141
+ concurrent-ruby (~> 1.0)
142
+ rack (> 1, < 3)
143
+ sprockets-rails (3.2.1)
144
+ actionpack (>= 4.0)
145
+ activesupport (>= 4.0)
146
+ sprockets (>= 3.0.0)
147
+ thor (1.0.1)
148
+ thread_safe (0.3.6)
149
+ tzinfo (1.2.7)
150
+ thread_safe (~> 0.1)
151
+ websocket-driver (0.7.2)
152
+ websocket-extensions (>= 0.1.0)
153
+ websocket-extensions (0.1.5)
154
+ zeitwerk (2.3.0)
155
+
156
+ PLATFORMS
157
+ ruby
158
+
159
+ DEPENDENCIES
160
+ apidoco_dsl!
161
+ bundler (~> 2.0)
162
+ rake (~> 10.0)
163
+ rspec (~> 3.0)
164
+
165
+ BUNDLED WITH
166
+ 2.0.2
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Steve Lewis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,268 @@
1
+ # ApidocoDsl
2
+
3
+ This gem provides a DSL wrapper around the excellent [Apidoco Gem](https://github.com/72pulses/apidoco) for Ruby on Rails projects. The DSL is heavily inspired by the one provided by [Apipie](https://github.com/Apipie/apipie-rails), but it was developed from scratch. In our opinion, the Apidoco system is more flexible, and it's output is prettier than Apipie. The one thing it was lacking was a good DSL to produce the JSON files that make up its documentation output. That's where this gem comes in. Instead of writing JSON files by hand, using this gem you can write your documentation directly in to your code _as code_, which is later parsed and converted into the JSON files that feed the Apidoco engine.
4
+
5
+ ## Compatibility with Apidoco
6
+
7
+ This DSL gem was written against the 1.5.4 version of the Apidoco gem. Later versions of Apidoco may introduce breaking changes with this gem, but we'll do our best to keep it up to date. One important point with regards to compatibility is that this gem renders Apidoco's resource generator, (`rails g apidoco resource`), unnecessary, unless there really is certain documentation you'd rather write the raw JSON file for. Using this gem, documentation that you write into your code will automatically be placed in the right locations for the Apidoco engine.
8
+
9
+ ## Generating documentation
10
+
11
+ Once you've written your documentation into your code, it has to be parsed and written out to the appropriate Apidoco directory in JSON format. This gem provides a generator for this purpose that is invoked this way:
12
+
13
+ ```bash
14
+ rails g apidoco_dsl:documentation
15
+ ```
16
+
17
+ ## Writing Documentation
18
+
19
+ ### Where to write it
20
+
21
+ As mentioned before, this gem allows you to write documentation for your code using code. There are a couple ways you can do this, depending upon the needs of your project and how much you want to intermingle your functional code with your documentation.
22
+
23
+ #### Inline with the methods they document
24
+
25
+ By including the ApidocoDsl module in to the classes you want to document, you can write your documentation in the same file with the code it documents:
26
+
27
+ ```ruby
28
+ class MyApiController < ApplicationController
29
+ require 'apidoco_dsl'
30
+ extend ApidocoDsl
31
+
32
+ namespace "V1"
33
+ resource :widgets
34
+
35
+ api_doc do
36
+ ...
37
+ end
38
+ def show
39
+ ...
40
+ end
41
+ end
42
+ ```
43
+
44
+ #### In a separate class
45
+
46
+ Unlike systems like Apipie, with Apidoco and this gem, there is no inherent programmatic link between documentation and the classes/methods being documented. Because of this, it is possible, (and perhaps even preferable), to define all of your documentation in locations and classes that are entirely separate from the code they document. You are free to organize your documentation files however it makes sense to you.
47
+
48
+ ```ruby
49
+ module ApiDocs
50
+ class MyResource
51
+ extend ApidocoDsl
52
+ extend ActiveSupport::Concern
53
+
54
+ api_doc do
55
+ ...
56
+ end
57
+ end
58
+ end
59
+ ```
60
+
61
+ ### The DSL
62
+
63
+ At the most basic level, ApidocoDsl is used for writing documentation about the individual endpoints of your API. Doing so starts with defining an `api_doc` block. Below is a "full" doc block:
64
+
65
+ ```ruby
66
+ api_doc do
67
+ published true
68
+ name "Do Something"
69
+ description "Does Something"
70
+ endpoint "/v1/do_something/"
71
+ http_method 'POST'
72
+ header({ 'Content-Type' => 'application/json', 'Authentication' => "Bearer <token>" })
73
+
74
+ param :thing1, type: 'String', desc: 'The first thing', notes: "Introduced by the Cat In The Hat", required: true
75
+ param :thing2, type: 'Integer', desc: 'The second thing', notes: "Comes with Thing 1"
76
+
77
+ example_request do
78
+ {
79
+ thing1: "This is the first thing",
80
+ thing2: 42
81
+ }
82
+ end
83
+
84
+ example_response path: example_root + '/create_response.json'
85
+
86
+ returns code: :created do
87
+ property :thing_id, type: 'Integer', desc: "The thing's ID", notes: "Will be a number"
88
+ param_group :carrier_connection_response
89
+ end
90
+ end
91
+ ```
92
+
93
+ Note in the example above that `example_root` is a variable defined above this definition, and not actually part of the ApidocoDsl.
94
+
95
+ Everything inside the block is considered documentation for the endpoint. It may be helpful to read the [Apidoco documentation](https://github.com/72pulses/apidoco), for context on some of these, but each method is described below.
96
+
97
+ #### published
98
+
99
+ Whether or not this documentation should be 'published', that is, visible on the documentation site. By default, documentation is _not_ published. Calling this method with an argument of `true`, will make it visible.
100
+
101
+ #### name
102
+
103
+ This is the name that will be displayed on the documentation page to identify this endpoint, both as a header and in the navigation. It can be anything you want, but obviously should actually identify the endpoint being documented. It takes a string.
104
+
105
+ #### description
106
+
107
+ A longer-form description of the endpoint. This method takes either a string _or_ a path argument. Given a string, it will simply display that string. Given a path, it will parse the provided file, (as Markdown), and use the result as the description for the endpoint.
108
+
109
+ #### endpoint
110
+
111
+ The URL for the endpoint being documented. It will be copied as is into the final documentation for the endpoint. By convention, any parameters provided in the endpoint url are preceded by colons, like this:
112
+
113
+ `endpoint: "v1/customers/:id"`
114
+
115
+ #### http_method
116
+
117
+ Which HTTP method(s) to use when calling this endpoint. This is a string that will be passed, as-is to the final documentation. For endpoints that accept more than one method, (`PUT` or `PATCH`, for instance), separate them with pipes: `PUT|PATCH`.
118
+
119
+ #### header
120
+
121
+ What, if any sort of headers to include in the request to this endpoint. Common choices might be a `Content-Type` header or an `Authentication` header.
122
+
123
+ #### param
124
+
125
+ This method defines a param for this endpoint. It is discussed in more detail below.
126
+
127
+ #### property
128
+
129
+ Like a `param`, but generally used to define _responses_ rather than requests. Discussed in more detail below.
130
+
131
+ #### param_group
132
+
133
+ A param_group is used to, literally, group a set of params or properties together. This may be done for simple organizatonal reasons, but it is most useful for creating sets of parameters that can be reused in multiple endpoint definitions. Param groups are discussed in more detail below.
134
+
135
+ #### example_request / example_response
136
+
137
+ There are two ways to provide an example request or an example response for an endpoint. The first is to provide a block to the method itself, adding your example inside:
138
+
139
+ ```ruby
140
+ example_request do
141
+ { "param1": "foo", "param2": "bar" }
142
+ end
143
+ ```
144
+
145
+ The hash will be automatically converted to JSON for presentation purposes.
146
+
147
+
148
+ Alternatively, you can provide a `path` argument that points to a `json` file:
149
+
150
+ ```ruby
151
+ example_request path: "/examples/endpoint_request.json"
152
+ ```
153
+
154
+ #### returns
155
+
156
+ If your API endpoint returns any properties, the `returns` method allows you to specify what they are by identifying them as properties:
157
+
158
+ ```ruby
159
+ returns do
160
+ property :response_1, type: "String", desc: "The first part of the response"
161
+ property :response_2, type: "Integer", desc:
162
+ end
163
+ ```
164
+
165
+ You can also specify a response `code`, whether or not the endpoint returns any other properties. If not specified, the `code` is 200.
166
+
167
+ ```ruby
168
+ returns code: 203 do
169
+ ...
170
+ end
171
+ ```
172
+
173
+ ### Params and Properties
174
+
175
+ Describing which parameters your endpoints take and what properties they return is handled by the `param` and `property` methods respectively. These methods work the same way and are technically interchangeable. The only difference is that any defined properties
176
+ are considered `required` by default.
177
+
178
+ Here's an example param:
179
+
180
+ ```ruby
181
+ param :age, type: 'Integer', desc: "How old this person is.", notes: "Used for setting various interface options", validations: "Must be a positive integer.", required: true
182
+ ```
183
+
184
+ A param/property takes the following attributes:
185
+
186
+ #### name
187
+
188
+ The name of the param/property is the first argument to the method and is always required. Note that it is a positional argument, not a kwarg.
189
+
190
+ #### type
191
+
192
+ A kwarg that describes the "type" of the parameter. If you provide a string, this value can be anything you want. It's also possible to pass any valid Ruby type, (String, Array, Hash, etc.), without enclosing it in a string. The `type` argument is required.
193
+
194
+ #### desc
195
+
196
+ An optional kwarg that takes a string describing the parameter. For the sake of formatting, it's best if the description is kept relatively short.
197
+
198
+ #### notes
199
+
200
+ An optional kwarg for providing additional context or information about a parameter. For the sake of formatting, it's best if the description is kept relatively short. Notes are an optional means of highlighting specific information about a parameter, (such as acceptable values), etc.
201
+
202
+ #### validations
203
+
204
+ An optional kwarg for adding a note about any validations present on the given parameter. For the sake of formatting, it's best if the validations argument is kept relatively short.
205
+
206
+ #### required
207
+
208
+ Whether or not this parameter is required. Take a boolean and defaults to `false` for a param and `true` for a property.
209
+
210
+ ### Param Groups
211
+
212
+ Param groups allow you to define a set of parameters that can be shared between individual documentation blocks, reducing duplication. You define them by calling `def_param_group`:
213
+
214
+ ```ruby
215
+ def_param_group :my_group do
216
+ param :param1, type: "Integer", desc: "The first param"
217
+ param :param2, type: "Integer", desc: "The second param"
218
+ end
219
+ ```
220
+ You can define params or properties inside of a param group just like you would inside of an `api_doc` block. To add a param group to a doc block, use the `param_group` method:
221
+
222
+ ```ruby
223
+ api_doc do
224
+ ...
225
+
226
+ param_group :my_group
227
+
228
+ ...
229
+
230
+ returns do
231
+ param_group :my_response_group
232
+ end
233
+ end
234
+ ```
235
+
236
+ The `param_group` method takes a single argument, the name of the param group you want to invoke.
237
+
238
+ Once a param group is defined, it can be used in _any_ documentation block, even those that are in a different source file. For that reason, it may be convenient to define your param groups in files separate from your api documentation itself.
239
+
240
+ ## Installation
241
+
242
+ Add this line to your application's Gemfile:
243
+
244
+ ```ruby
245
+ gem 'apidoco_dsl'
246
+ ```
247
+
248
+ And then execute:
249
+
250
+ $ bundle
251
+
252
+ Or install it yourself as:
253
+
254
+ $ gem install apidoco_dsl
255
+
256
+ ## Development
257
+
258
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
259
+
260
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
261
+
262
+ ## Contributing
263
+
264
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/apidoco_dsl.
265
+
266
+ ## License
267
+
268
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "apidoco_dsl/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "apidoco_dsl"
7
+ spec.version = ApidocoDsl::VERSION
8
+ spec.authors = ["Steve Lewis"]
9
+ spec.email = ["steve.l@lojistic.com"]
10
+
11
+ spec.summary = %q{ Provides a block-based DSL for apidoco similar to ApiPie }
12
+ spec.homepage = "https://github.com/lojistic/apidoco_dsl"
13
+ spec.license = "MIT"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = spec.homepage
17
+ spec.metadata["changelog_uri"] = spec.homepage
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.add_dependency "apidoco", "~> 1.5"
26
+ spec.add_dependency "kramdown"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 2.0"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "apidoco_dsl"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,42 @@
1
+ require "apidoco_dsl/version"
2
+ require "apidoco_dsl/documentable"
3
+ require 'apidoco_dsl/param'
4
+ require "apidoco_dsl/api"
5
+ require 'apidoco_dsl/api_doc'
6
+ require 'apidoco_dsl/param_group'
7
+ require 'apidoco_dsl/railtie'
8
+
9
+ module ApidocoDsl
10
+ class Error < StandardError; end
11
+
12
+ @@api = Api.new
13
+
14
+ def self.fetch_docs
15
+ return @@api.api_docs
16
+ end
17
+
18
+ def namespace(namespace)
19
+ @@api.namespace = namespace
20
+
21
+ end
22
+
23
+ def resource(resource)
24
+ @@api.resource = resource
25
+ end
26
+
27
+ def api_doc(&block)
28
+ api_doc = ApiDoc.new(@@api)
29
+ api_doc.instance_exec(&block)
30
+
31
+ @@api.api_docs << api_doc
32
+ end
33
+
34
+ def def_param_group(group_name, &block)
35
+ param_group = ParamGroup.new(group_name, @@api)
36
+ param_group.instance_exec(&block)
37
+
38
+ @@api.param_groups[group_name] = param_group
39
+ end
40
+
41
+
42
+ end
@@ -0,0 +1,10 @@
1
+ module ApidocoDsl
2
+ class Api
3
+ attr_accessor :api_docs, :param_groups, :namespace, :resource
4
+
5
+ def initialize
6
+ @api_docs = []
7
+ @param_groups = {}
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,180 @@
1
+ require 'kramdown'
2
+
3
+ module ApidocoDsl
4
+ class ApiDoc
5
+ include Documentable
6
+ attr_accessor :doc_request_params, :doc_response_params, :doc_published, :doc_name,
7
+ :doc_endpoint, :doc_http_method, :doc_header, :doc_examples,
8
+ :doc_sort_order, :doc_description, :doc_request_example, :doc_response_example,
9
+ :doc_return_code, :doc_namespace, :doc_resource, :doc_markdown
10
+
11
+ #attr_reader :api
12
+
13
+ SETTABLE = ['published', 'name', 'endpoint',
14
+ 'http_method', 'header', 'markdown', 'sort_order']
15
+
16
+ def initialize(api)
17
+ @api = api
18
+ @doc_request_params = []
19
+ @doc_response_params = []
20
+ @doc_examples = []
21
+ @doc_namespace = @api.namespace
22
+ @doc_resource = @api.resource
23
+ @param_destination = 'request'
24
+ end
25
+
26
+ def param_key
27
+ "Document"
28
+ end
29
+
30
+ def returns(code: 200, &block)
31
+ @doc_return_code = translate_return_code(code)
32
+ @param_destination = 'response'
33
+ self.instance_exec(&block) if block_given?
34
+ @param_destination = 'request'
35
+ end
36
+
37
+ def method_missing(name, *args)
38
+ return set_attribute(name, *args) if SETTABLE.include?(name.to_s)
39
+ super
40
+ end
41
+
42
+ def set_attribute(name, value)
43
+ self.send(:"doc_#{name}=", value)
44
+ end
45
+
46
+ def example_request(path: nil)
47
+ ex = {}
48
+ ex['request'] = yield if block_given?
49
+ ex['request'] = JSON.parse(File.read(path)) if path
50
+ @doc_examples << ex
51
+ end
52
+
53
+ def example_response(path: nil)
54
+ ex = {}
55
+ ex['response'] = yield if block_given?
56
+ ex['response'] = JSON.parse(File.read(path)) if path
57
+ @doc_examples << ex
58
+ end
59
+
60
+ def description(txt = nil, path: nil)
61
+ if path
62
+ txt = File.read(path)
63
+ erb = ERB.new(txt).result
64
+ html = Kramdown::Document.new(erb).to_html
65
+ @doc_description = ERB.new(html).result
66
+ else
67
+ @doc_description = txt
68
+ end
69
+ end
70
+
71
+ def doc_folder
72
+ doc_namespace.split('::').map(&:underscore).map(&:downcase).join('/') + "/#{doc_resource.to_s.underscore}/"
73
+ end
74
+
75
+ def doc_file
76
+ doc_name.gsub(/\s/, '').underscore
77
+ end
78
+
79
+ def to_json
80
+ doc = {}
81
+ doc['published'] = doc_published unless doc_published.nil?
82
+ doc['name'] = doc_name unless doc_name.nil?
83
+ doc['end_point'] = doc_endpoint unless doc_endpoint.nil?
84
+ doc['http_method'] = doc_http_method unless doc_http_method.nil?
85
+ doc['params'] = unroll_parameters(doc_request_params, []) unless doc_request_params.empty?
86
+ doc['response_params'] = unroll_parameters(doc_response_params, []) unless doc_response_params.empty?
87
+ doc['header'] = doc_header unless doc_header.nil?
88
+ doc['description'] = doc_description unless doc_description.nil?
89
+ doc['examples'] = doc_examples unless doc_examples.empty?
90
+ doc['sort_order'] = doc_sort_order unless doc_sort_order.nil?
91
+ doc['return_code'] = doc_return_code
92
+ doc['markdown'] = doc_markdown
93
+
94
+ return JSON.pretty_generate(doc)
95
+ end
96
+
97
+ private
98
+
99
+ def translate_return_code(code)
100
+ trans = {
101
+ 100 => :continue,
102
+ 101 => :switching_protocols,
103
+ 102 => :processing,
104
+ 200 => :ok,
105
+ 201 => :created,
106
+ 202 => :accepted,
107
+ 203 => :non_authoritative_information,
108
+ 204 => :no_content,
109
+ 205 => :reset_content,
110
+ 206 => :partial_content,
111
+ 207 => :multi_status,
112
+ 226 => :im_used,
113
+ 300 => :multiple_choices,
114
+ 301 => :moved_permanently,
115
+ 302 => :found,
116
+ 303 => :see_other,
117
+ 304 => :not_modified,
118
+ 305 => :use_proxy,
119
+ 307 => :temporary_redirect,
120
+ 400 => :bad_request,
121
+ 401 => :unauthorized,
122
+ 402 => :payment_required,
123
+ 403 => :forbidden,
124
+ 404 => :not_found,
125
+ 405 => :method_not_allowed,
126
+ 406 => :not_acceptable,
127
+ 407 => :proxy_authentication_required,
128
+ 408 => :request_timeout,
129
+ 409 => :conflict,
130
+ 410 => :gone,
131
+ 411 => :length_required,
132
+ 412 => :precondition_failed,
133
+ 413 => :request_entity_too_large,
134
+ 414 => :request_uri_too_long,
135
+ 415 => :unsupported_media_type,
136
+ 416 => :requested_range_not_satisfiable,
137
+ 417 => :expectation_failed,
138
+ 422 => :unprocessable_entity,
139
+ 423 => :locked,
140
+ 424 => :failed_dependency,
141
+ 426 => :upgrade_required,
142
+ 500 => :internal_server_error,
143
+ 501 => :not_implemented,
144
+ 502 => :bad_gateway,
145
+ 503 => :service_unavailable,
146
+ 504 => :gateway_timeout,
147
+ 505 => :http_version_not_supported,
148
+ 507 => :insufficient_storage,
149
+ 510 => :not_extended
150
+ }.invert
151
+
152
+ trans[code] || code
153
+ end
154
+
155
+ def unroll_parameters(params, unrolled = [])
156
+ # Unrolling parameters is a matter of recursively seeking down through each base set of params for
157
+ # each of _their_ params, and assigning/setting keys appropriately.
158
+
159
+ params.each do |pr|
160
+ if pr.params.any?
161
+
162
+ unrolled << pr.to_h
163
+ duped = pr.params.dup
164
+ duped.each{|d| d.parent = pr; }
165
+ unroll_parameters(duped, unrolled)
166
+ else
167
+ unrolled << pr.to_h
168
+ end
169
+
170
+ end
171
+
172
+ return unrolled
173
+ end
174
+
175
+ def push_to
176
+ self.send(:"doc_#{@param_destination}_params")
177
+ end
178
+
179
+ end
180
+ end
@@ -0,0 +1,65 @@
1
+ module ApidocoDsl
2
+ module Documentable
3
+ attr_accessor :api
4
+
5
+ def param(key, type:, desc: nil, notes: nil, validations: nil, required: false, &block)
6
+ this_param = Param.new(
7
+ param_key: key,
8
+ param_type: type,
9
+ param_description: desc,
10
+ param_notes: notes,
11
+ param_required: required,
12
+ param_validations: validations,
13
+ parent: self
14
+ )
15
+
16
+ if block_given?
17
+ this_param.instance_exec(this_param, &block)
18
+ end
19
+
20
+ push_to << this_param
21
+ end
22
+
23
+ # There may be a clever way to do this, preserving key ancestry without duplication...I haven't found it.
24
+ def property(key, type:, desc: nil, notes: nil, validations: nil, required: true, &block)
25
+ this_param = Param.new(
26
+ param_key: key,
27
+ param_type: type,
28
+ param_description: desc,
29
+ param_notes: notes,
30
+ param_required: required,
31
+ param_validations: validations,
32
+ parent: self
33
+ )
34
+
35
+ if block_given?
36
+ this_param.instance_exec(this_param, &block)
37
+ end
38
+
39
+ push_to << this_param
40
+ end
41
+
42
+ def param_group(group_name)
43
+ # Param groups seem to have trouble inheriting parentage correctly.
44
+ # This appears to be because the params are "parented" when they are
45
+ # initally defined and when grabbing them here, we need to "re-parent"
46
+ # them...or more accurately, a copy of them.
47
+ #
48
+ # Again, problem, because a single param may itself have multiple layers
49
+ # of child params and groups...
50
+ #
51
+ # An interesting note here is that defining a nested grouping _manually_
52
+ # appears to have the desired effect.
53
+
54
+ pg = @api.param_groups[group_name].deep_dup
55
+ #pg.reparent(self) # Make the calling object the top-level parent for every param in this group
56
+
57
+ pg.params.each do |param|
58
+ push_to << param
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
65
+
@@ -0,0 +1,85 @@
1
+ module ApidocoDsl
2
+ class Param
3
+ include Documentable
4
+
5
+ attr_accessor :param_key, :param_type, :param_description,
6
+ :param_notes, :param_validations, :param_required, :params, :parent
7
+
8
+ SETTABLE = ['key', 'type', 'description', 'notes', 'validations', 'required']
9
+
10
+ def initialize(param_key:,
11
+ param_type:,
12
+ param_description: nil,
13
+ param_notes: nil,
14
+ param_validations: nil,
15
+ param_required: false,
16
+ parent: nil)
17
+
18
+ @param_key = param_key
19
+ @param_type = param_type
20
+ @param_description = param_description
21
+ @param_notes = param_notes
22
+ @param_validations = param_validations
23
+ @param_required = param_required
24
+ @parent = parent
25
+ @params = []
26
+ @api = parent.api
27
+ end
28
+
29
+ def method_missing(name, *args)
30
+ return set_attribute(name, *args) if SETTABLE.include?(name.to_s)
31
+ super
32
+ end
33
+
34
+ def set_attribute(name, value)
35
+ self.send(:"param_#{name}=", value)
36
+ end
37
+
38
+ def doc_request_params
39
+ @params
40
+ end
41
+ alias doc_response_params doc_request_params
42
+
43
+ def to_h
44
+ param = {}
45
+ param['key'] = compound_key
46
+ param['type'] = param_type unless param_type.nil?
47
+ param['description'] = param_description unless param_description.nil?
48
+ param['notes'] = param_notes unless param_notes.nil?
49
+ param['required'] = param_required
50
+ param['validations'] = param_validations unless param_validations.nil?
51
+
52
+ return param
53
+ end
54
+
55
+ def display_key
56
+ return param_key.to_s unless @parent && @parent.is_a?(Param)
57
+ return "[#{param_key.to_s}]"
58
+ end
59
+
60
+ private
61
+
62
+ def compound_key
63
+ return param_key.to_s unless @parent && @parent.is_a?(Param)
64
+
65
+ ancestors = []
66
+ this_parent = @parent
67
+ ancestor = this_parent
68
+
69
+ while ancestor
70
+ ancestors << ancestor
71
+ ancestor = ancestor.try(:parent)
72
+ end
73
+
74
+ keys = ancestors.map{|a| a.try(:display_key) }.reverse
75
+
76
+ compounded = keys.map(&:to_s).join('')
77
+ return compounded + display_key
78
+ end
79
+
80
+ def push_to
81
+ @params
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,53 @@
1
+ module ApidocoDsl
2
+ class ParamGroup
3
+ include Documentable
4
+
5
+ attr_accessor :name, :params
6
+
7
+ def initialize(group_name, api)
8
+ @name = group_name
9
+ @api = api
10
+ @params = []
11
+ @doc_request_params = @params
12
+ end
13
+
14
+ def doc_request_params
15
+ @params
16
+ end
17
+ alias doc_response_params doc_request_params
18
+
19
+
20
+ def reparent(new_parent)
21
+ original_params = @params.deep_dup
22
+ @params = []
23
+
24
+ p '&&&&&&&&&&'
25
+ p '&&&&&&&&&&'
26
+ p '&&&&&&&&&&'
27
+ #original_params.each do |param|
28
+ #p param.parent.class
29
+ #p param.display_key #unless param.parent
30
+ #end
31
+
32
+ #original_params.each do |param|
33
+ #param = param.dup
34
+ #param.parent = new_parent
35
+
36
+ #@params << param
37
+ #end
38
+
39
+ @params.each do |param|
40
+ p param.parent.parent.class
41
+ p param.parent.display_key
42
+ p param.display_key #unless param.parent
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def push_to
49
+ @params
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ require 'apidoco_dsl'
2
+ require 'rails'
3
+
4
+ module ApidocoDsl
5
+ class Railtie < Rails::Railtie
6
+ railtie_name :apidoco_dsl
7
+
8
+ rake_tasks do
9
+ path = File.expand_path(__dir__ + '/../')
10
+ Dir.glob("#{path}/tasks/**/*.rake").each{|t| load t }
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,3 @@
1
+ module ApidocoDsl
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,18 @@
1
+ module ApidocoDsl
2
+ module Generators
3
+ class DocumentationGenerator < Rails::Generators::Base
4
+ desc "Generate static JSON documentation for your API endpoints"
5
+
6
+ def generate_docs
7
+ docs = ApidocoDsl.fetch_docs
8
+ base_path = Apidoco.base_path
9
+
10
+ docs.each do |doc|
11
+ doc_path = "#{base_path}/#{doc.doc_folder}/#{doc.doc_file}.json"
12
+ create_file(doc_path, doc.to_json, force: true)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apidoco_dsl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Steve Lewis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-06-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: apidoco
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kramdown
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description:
84
+ email:
85
+ - steve.l@lojistic.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - apidoco_dsl.gemspec
99
+ - bin/console
100
+ - bin/setup
101
+ - lib/apidoco_dsl.rb
102
+ - lib/apidoco_dsl/api.rb
103
+ - lib/apidoco_dsl/api_doc.rb
104
+ - lib/apidoco_dsl/documentable.rb
105
+ - lib/apidoco_dsl/param.rb
106
+ - lib/apidoco_dsl/param_group.rb
107
+ - lib/apidoco_dsl/railtie.rb
108
+ - lib/apidoco_dsl/version.rb
109
+ - lib/generators/apidoco_dsl/documentation_generator.rb
110
+ homepage: https://github.com/lojistic/apidoco_dsl
111
+ licenses:
112
+ - MIT
113
+ metadata:
114
+ homepage_uri: https://github.com/lojistic/apidoco_dsl
115
+ source_code_uri: https://github.com/lojistic/apidoco_dsl
116
+ changelog_uri: https://github.com/lojistic/apidoco_dsl
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.7.6
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Provides a block-based DSL for apidoco similar to ApiPie
137
+ test_files: []