grape-markdown 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3dc94cf1475a3e3615cc3215cdf9e54eb6a02fa8
4
+ data.tar.gz: 7d5e62aa00c83c69d95bc20c9b0d592bd47ddc87
5
+ SHA512:
6
+ metadata.gz: 855fcaf8917043fe5a6121a1722f6669a73d18bf3cb660ed8ca977f6f3b28b8534b27b1f6b4c600688e52a2c455c8fe34510e80ef54e7de7f4e88c45698bf999
7
+ data.tar.gz: 93694b04db560f1c38c99d1dc247602da9372092aab7501b71403eae875e2c57d80a59b2361a5c73660c867993f456f9edf68689f2ddad9797fd2c0ac6a218e0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=progress
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ AllCops:
2
+ Excludes:
3
+ - Rakefile
4
+ - vendor/**
5
+ - bin/**
6
+
7
+ Documentation:
8
+ # don't require classes to be documented
9
+ Enabled: false
10
+
11
+ Encoding:
12
+ # no need to always specify encoding
13
+ Enabled: false
14
+
15
+ LineLength:
16
+ # just one more character please
17
+ Max: 80
18
+
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.1.0
5
+ - 2.0.0
6
+ - 1.9.3
7
+ - jruby
8
+ addons:
9
+ code_climate:
10
+ repo_token:
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in grape-markdown.gemspec
4
+ gemspec
5
+
6
+ gem 'grape', github: 'intridea/grape'
7
+
8
+ group :development, :test do
9
+ gem 'coveralls', '~> 0.7'
10
+ gem 'rspec', '~> 2.14'
11
+ gem 'bundler', '~> 1.5'
12
+ gem 'rake', '~> 10.0'
13
+ gem 'rubocop', '~> 0.18'
14
+ gem 'pry', '~> 0.9'
15
+ gem 'guard', '~> 2.4'
16
+ gem 'guard-rspec', '~> 4.2'
17
+ gem 'guard-bundler', '~> 2.0'
18
+ end
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ # More info at https://github.com/guard/guard#readme
2
+
3
+ guard 'rspec', :version => 2 do
4
+ watch(%r{^spec/.+_spec\.rb$})
5
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
6
+ watch('spec/spec_helper.rb') { "spec/" }
7
+ end
8
+
9
+ guard 'bundler' do
10
+ watch('Gemfile')
11
+ watch(/^.+\.gemspec/)
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 John Allen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # GrapeMarkdown
2
+
3
+ [![Code Climate](https://codeclimate.com/github/connexio-labs/grape-markdown.png)](https://codeclimate.com/github/connexio-labs/grape-markdown)
4
+ [![Build Status](https://travis-ci.org/connexio-labs/grape-markdown.png?branch=master)](https://travis-ci.org/connexio-labs/grape-markdown)
5
+ [![Coverage Status](https://coveralls.io/repos/connexio-labs/grape-markdown/badge.png)](https://coveralls.io/r/connexio-labs/grape-markdown)
6
+ [![Dependency Status](https://gemnasium.com/connexio-labs/grape-markdown.png)](https://gemnasium.com/connexio-labs/grape-markdown)
7
+ [![Gem Version](https://badge.fury.io/rb/grape-markdown.png)](http://badge.fury.io/rb/grape-markdown)
8
+
9
+ Auto generates Markdown from the docuementation that is created by your [Grape](https://github.com/intridea/grape) API.
10
+
11
+ ### NOTE
12
+
13
+ This is an early implementation that makes some assumptions about your API (follows a standard REST pattern) that works with our implementation of Grape API's. This project will generate a very simplistic Markdown document. It primarily adds some wrappers around Grape's documentation and enables other gems ([grape-apiary](https://github.com/connexio-labs/grape-apiary) and [grape-slate](https://github.com/connexio-labs/grape-slate)) to generate Markdown in specific formats.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'grape-markdown'
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install grape-markdown
28
+
29
+ ## Usage
30
+
31
+ Add some metadata about your API and then execute the `generate` method on the `GrapeMarkdown::Document` class.
32
+
33
+ ### Configuration
34
+
35
+ Configure details about your api in an initializers or similar:
36
+
37
+ ```ruby
38
+ GrapeMarkdown.config do |config|
39
+ # the name of your api
40
+ config.name = 'Awesome API'
41
+ # a description for your api
42
+ config.description = 'The awesome description'
43
+ # the type to use for generated sample id's (`integer` or `uuid`)
44
+ config.example_id_type = :uuid
45
+ # resources you do not want documented
46
+ config.resource_exclusion = [:admin, :swagger_doc]
47
+ # whether or not examples should include a root element (default: false)
48
+ config.include_root = true
49
+ end
50
+
51
+ # request headers you want documented
52
+ GrapeMarkdown.config.request_headers = [
53
+ { 'Accept-Charset' => 'utf-8' },
54
+ { 'Connection' => 'keep-alive' }
55
+ ]
56
+
57
+ # response headers you want documented
58
+ GrapeMarkdown.config.response_headers = [
59
+ { 'Content-Length' => '21685' },
60
+ { 'Connection' => 'keep-alive' }
61
+ ]
62
+ ```
63
+
64
+ ### Generation
65
+
66
+ ```ruby
67
+ # supply the class you'd like to document and generate your blueprint
68
+ GrapeMarkdown::Document.new(AwesomeAPI).generate
69
+ ```
70
+
71
+ ## TODO
72
+
73
+ * Add a rake task to simplify generation
74
+ * ~~Add support for listing all of a resources attributes at the resource level as a markdown table~~
75
+ * Handle ever changing sample id's (don't want git diff's after every generation)
76
+ * Add option to change or remove the sample id field (eg. `_id` vs `id`)
77
+ * What if someone does not use JSON?!?
78
+ * ~~Create sample response for list endpoints (array)~~
79
+ * Add support for writing the markdown to disk
80
+ * ~~Add an option to include root in json~~
81
+
82
+ ## Contributing
83
+
84
+ 1. Fork it ( http://github.com/connexio-labs/grape-markdown/fork )
85
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
86
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
87
+ 4. Push to the branch (`git push origin my-new-feature`)
88
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup :default, :test, :development
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.pattern = 'spec/**/*_spec.rb'
10
+ end
11
+
12
+ require 'rainbow/ext/string' unless String.respond_to?(:color)
13
+ require 'rubocop/rake_task'
14
+ Rubocop::RakeTask.new(:rubocop)
15
+
16
+ task default: [:rubocop, :spec]
17
+
18
+ task :console do
19
+ require 'pry'
20
+ require 'grape-markdown'
21
+ ARGV.clear
22
+ Pry.start
23
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grape-markdown/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'grape-markdown'
8
+ spec.version = GrapeMarkdown::VERSION
9
+ spec.authors = ['John Allen']
10
+ spec.email = ['john@threedogconsulting.com']
11
+ spec.summary = %q{Allows for generating a Markdown document from you Grape API}
12
+ spec.description = %q{Auto generates Markdown from the docuementation that is created by your Grape API}
13
+ spec.homepage = 'https://github.com/connexio-labs/grape-markdown'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'grape', '~> 0.6'
22
+
23
+ spec.add_development_dependency 'coveralls', '~> 0.7'
24
+ spec.add_development_dependency 'rspec', '~> 2.14'
25
+ spec.add_development_dependency 'bundler', '~> 1.5'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rubocop', '~> 0.18'
28
+ spec.add_development_dependency 'pry', '~> 0.9'
29
+ spec.add_development_dependency 'guard', '~> 2.4'
30
+ spec.add_development_dependency 'guard-rspec', '~> 4.2'
31
+ spec.add_development_dependency 'guard-bundler', '~> 2.0'
32
+ end
@@ -0,0 +1 @@
1
+ require 'grape-markdown'
@@ -0,0 +1,62 @@
1
+ module GrapeMarkdown
2
+ class Config
3
+ SETTINGS = [
4
+ :name,
5
+ :description,
6
+ :request_headers,
7
+ :response_headers,
8
+ :example_id_type,
9
+ :resource_exclusion,
10
+ :include_root
11
+ ]
12
+
13
+ class << self
14
+ attr_accessor(*SETTINGS)
15
+
16
+ def request_headers
17
+ @request_headers ||= []
18
+ end
19
+
20
+ def response_headers
21
+ @response_headers ||= []
22
+ end
23
+
24
+ def resource_exclusion
25
+ @resource_exclusion ||= []
26
+ end
27
+
28
+ def include_root
29
+ @include_root ||= false
30
+ end
31
+
32
+ def supported_id_types
33
+ [:integer, :uuid, :bson]
34
+ end
35
+
36
+ def example_id_type=(value)
37
+ fail UnsupportedIDType unless supported_id_types.include?(value)
38
+
39
+ if value.to_sym == :bson && !Object.const_defined?('BSON')
40
+ fail BSONNotDefinied
41
+ end
42
+
43
+ @example_id_type = value
44
+ end
45
+
46
+ def example_id_type
47
+ @example_id_type ||= :integer
48
+ end
49
+
50
+ def generate_id
51
+ case example_id_type
52
+ when :integer
53
+ SecureRandom.random_number(1000)
54
+ when :uuid
55
+ SecureRandom.uuid
56
+ when :bson
57
+ BSON::ObjectId.new.to_s
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,76 @@
1
+ module GrapeMarkdown
2
+ class Document
3
+ attr_reader :api_class, :document_template, :properties_template
4
+
5
+ delegate(*GrapeMarkdown::Config::SETTINGS, to: 'GrapeMarkdown::Config')
6
+
7
+ def initialize(api_class)
8
+ @api_class = api_class
9
+ @document_template = template_for(:document)
10
+ @properties_template = template_for(:properties)
11
+ end
12
+
13
+ def generate
14
+ ERB.new(document_template, nil, '-').result(binding)
15
+ end
16
+
17
+ def write
18
+ fail 'Not yet supported'
19
+ end
20
+
21
+ def routes
22
+ @routes ||= api_class.routes.map do |route|
23
+ GrapeMarkdown::Route.new(route)
24
+ end
25
+ end
26
+
27
+ def resources
28
+ @resources ||= begin
29
+ grouped_routes = routes.group_by(&:route_name).reject do |name, routes|
30
+ resource_exclusion.include?(name.to_sym)
31
+ end
32
+
33
+ grouped_routes.map { |name, routes| Resource.new(name, routes) }
34
+ end
35
+ end
36
+
37
+ def properties_table(resource)
38
+ ERB.new(properties_template, nil, '-').result(resource.resource_binding)
39
+ end
40
+
41
+ def formatted_request_headers
42
+ formatted_headers(GrapeMarkdown::Config.request_headers)
43
+ end
44
+
45
+ def formatted_response_headers
46
+ formatted_headers(GrapeMarkdown::Config.response_headers)
47
+ end
48
+
49
+ def show_request_sample?(route)
50
+ %w(PUT POST).include?(route.route_method)
51
+ end
52
+
53
+ private
54
+
55
+ def template_for(name)
56
+ directory = File.dirname(File.expand_path(__FILE__))
57
+ path = File.join(directory, "./templates/#{name}.md.erb")
58
+
59
+ File.read(path)
60
+ end
61
+
62
+ def formatted_headers(headers)
63
+ return '' unless headers.present?
64
+
65
+ spacer = "\n" + (' ' * 12)
66
+
67
+ strings = headers.map do |header|
68
+ key, value = *header.first
69
+
70
+ "#{key}: #{value}"
71
+ end
72
+
73
+ " + Headers\n" + spacer + strings.join(spacer)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,45 @@
1
+ module GrapeMarkdown
2
+ class Parameter
3
+ attr_reader :route, :full_name, :name, :settings
4
+
5
+ delegate :route_model, :route_namespace, to: :route
6
+ delegate :requirement, :type, :documentation, :desc, to: :settings
7
+ delegate :example, to: :documentation, allow_nil: true
8
+
9
+ def initialize(route, name, options)
10
+ @full_name = name
11
+ @name = name
12
+ @name = name.scan(/\[(.*)\]/).flatten.first if name.include?('[')
13
+ @route = route
14
+ @settings = parse_options(options)
15
+ end
16
+
17
+ def description
18
+ "#{name} (#{requirement}, #{type}, `#{example}`) ... #{desc}"
19
+ end
20
+
21
+ private
22
+
23
+ def parse_options(options)
24
+ options = default_options(options) if options.blank?
25
+
26
+ options[:requirement] = options[:required] ? 'required' : 'optional'
27
+
28
+ Hashie::Mash.new(options)
29
+ end
30
+
31
+ def default_options(options)
32
+ model = name.include?('_id') ? name.gsub('_id', '') : route.route_model
33
+
34
+ {
35
+ required: true,
36
+ requirement: 'required',
37
+ type: 'uuid',
38
+ desc: "the `id` of the `#{model}`",
39
+ documentation: {
40
+ example: GrapeMarkdown::Config.generate_id
41
+ }
42
+ }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,63 @@
1
+ module GrapeMarkdown
2
+ class Resource
3
+ attr_reader :key, :name, :routes, :sample_generator
4
+
5
+ def initialize(key, routes)
6
+ @key = key
7
+ @name = key.humanize
8
+ @routes = routes
9
+ @sample_generator = SampleGenerator.new(self)
10
+ end
11
+
12
+ def title
13
+ @title ||= name.titleize
14
+ end
15
+
16
+ def namespaced
17
+ @namespaced ||= routes.group_by(&:route_namespace).map do |_, routes|
18
+ Resource.new(name, routes)
19
+ end
20
+ end
21
+
22
+ def paths
23
+ @paths ||= routes.group_by(&:route_path_without_format).map do |n, routes|
24
+ Resource.new(name, routes)
25
+ end
26
+ end
27
+
28
+ def header
29
+ # TODO: ???
30
+ route = routes.first
31
+
32
+ "#{title} #{route.route_type} [#{route.route_path_without_format}]"
33
+ end
34
+
35
+ def sample_request
36
+ sample_generator.request
37
+ end
38
+
39
+ def sample_response(route)
40
+ sample_generator.response(route.list?)
41
+ end
42
+
43
+ def unique_params
44
+ # TODO: this is a hack, assuming that the resource has a POST or PUT
45
+ # route that defines all of the parameters that would define the resource
46
+ methods = %w(POST PUT)
47
+
48
+ potential = routes.select do |route|
49
+ methods.include?(route.route_method) && route.route_params.present?
50
+ end
51
+
52
+ if potential.present?
53
+ potential.first.route_params
54
+ else
55
+ []
56
+ end
57
+ end
58
+
59
+ def resource_binding
60
+ binding
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,54 @@
1
+ module GrapeMarkdown
2
+ class Route < SimpleDelegator
3
+ # would like to rely on SimpleDelegator but Grape::Route uses
4
+ # method_missing for these methods :'(
5
+ delegate :route_namespace, :route_path, :route_method, to: '__getobj__'
6
+
7
+ def route_params
8
+ @route_params ||= __getobj__.route_params.sort.map do |param|
9
+ Parameter.new(self, *param)
10
+ end
11
+ end
12
+
13
+ def route_name
14
+ route_namespace.split('/').last ||
15
+ route_path.match('\/(\w*?)[\.\/\(]').captures.first
16
+ end
17
+
18
+ def route_description
19
+ "#{__getobj__.route_description} [#{route_method.upcase}]"
20
+ end
21
+
22
+ def route_path_without_format
23
+ route_path.gsub(/\((.*?)\)/, '')
24
+ end
25
+
26
+ def route_model
27
+ route_namespace.split('/').last.singularize
28
+ end
29
+
30
+ def route_type
31
+ list? ? 'collection' : 'single'
32
+ end
33
+
34
+ def request_description
35
+ "+ Request #{'(application/json)' if request_body?}"
36
+ end
37
+
38
+ def response_description
39
+ code = route_method == 'POST' ? 201 : 200
40
+
41
+ "+ Response #{code} (application/json)"
42
+ end
43
+
44
+ def list?
45
+ %w(GET POST).include?(route_method) && !route_path.include?(':id')
46
+ end
47
+
48
+ private
49
+
50
+ def request_body?
51
+ !%w(GET DELETE).include?(route_method)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,55 @@
1
+ module GrapeMarkdown
2
+ class SampleGenerator
3
+ attr_reader :resource, :root
4
+
5
+ delegate :unique_params, to: :resource
6
+
7
+ def initialize(resource)
8
+ @resource = resource
9
+ @root = resource.key.singularize
10
+ end
11
+
12
+ def sample(id = false)
13
+ array = resource.unique_params.map do |param|
14
+ next if param.name == root
15
+
16
+ [param.name, param.example]
17
+ end
18
+
19
+ hash = Hash[array.compact]
20
+
21
+ hash = hash.reverse_merge(id: Config.generate_id) if id
22
+ hash = { root => hash } if Config.include_root
23
+
24
+ hash
25
+ end
26
+
27
+ def request
28
+ hash = sample
29
+
30
+ return unless hash.present?
31
+
32
+ # format json spaces for blueprint markdown
33
+ JSON.pretty_generate(hash)
34
+ .gsub('{', (' ' * 14) + '{')
35
+ .gsub('}', (' ' * 14) + '}')
36
+ .gsub(/\ {2}\"/, (' ' * 16) + '"')
37
+ end
38
+
39
+ def response(list = false)
40
+ hash = sample(true)
41
+
42
+ return unless hash.present?
43
+
44
+ hash = [hash] if list
45
+
46
+ # format json spaces for blueprint markdown
47
+ JSON.pretty_generate(hash)
48
+ .gsub('[', (' ' * 12) + '[')
49
+ .gsub(']', (' ' * 12) + ']')
50
+ .gsub('{', (' ' * 14) + '{')
51
+ .gsub('}', (' ' * 14) + '}')
52
+ .gsub(/\ {2}\"/, (' ' * 16) + '"')
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,15 @@
1
+ # <%= name %>
2
+
3
+ <%= description %>
4
+
5
+ <% resources.each do |resource| %>
6
+ # <%= resource.title %>
7
+ <%= properties_table(resource) %>
8
+ <% resource.namespaced.each do |grouped_resource| %>
9
+ <% grouped_resource.paths.each do |resource_by_path| %>
10
+ <% resource_by_path.routes.each do |route| %>
11
+ ## <%= route.route_description %>
12
+ <% end %>
13
+ <% end %>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,7 @@
1
+ Properties
2
+
3
+ | Name | Type | Description |
4
+ |:-----|:-----|:------------|
5
+ <% unique_params.each do |param| -%>
6
+ | <%= param.full_name %> | <%= param.type %> | <%= param.desc %> |
7
+ <% end -%>
@@ -0,0 +1,3 @@
1
+ module GrapeMarkdown
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,27 @@
1
+ require 'grape'
2
+
3
+ module GrapeMarkdown
4
+ autoload :Version, 'grape-markdown/version'
5
+ autoload :Config, 'grape-markdown/config'
6
+ autoload :Parameter, 'grape-markdown/parameter'
7
+ autoload :SampleGenerator, 'grape-markdown/sample_generator'
8
+ autoload :Route, 'grape-markdown/route'
9
+ autoload :Resource, 'grape-markdown/resource'
10
+ autoload :Document, 'grape-markdown/document'
11
+
12
+ def self.config
13
+ block_given? ? yield(Config) : Config
14
+ end
15
+ end
16
+
17
+ class UnsupportedIDType < StandardError
18
+ def message
19
+ 'Unsupported id type, supported types are [integer, uuid, bson]'
20
+ end
21
+ end
22
+
23
+ class BSONNotDefinied < StandardError
24
+ def message
25
+ 'BSON type id requested but bson library is not present'
26
+ end
27
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeMarkdown::Config do
4
+ include_context 'configuration'
5
+
6
+ subject { GrapeMarkdown::Config }
7
+
8
+ it 'allows for name to be set' do
9
+ subject.name = name
10
+
11
+ expect(subject.name).to eq(name)
12
+ end
13
+
14
+ it 'allows for description to be set' do
15
+ subject.description = description
16
+
17
+ expect(subject.description).to eq(description)
18
+ end
19
+
20
+ context '.include_root' do
21
+ it 'defaults to false' do
22
+ expect(subject.include_root).to be(false)
23
+ end
24
+
25
+ it 'allows for inclusion of the root to be set' do
26
+ subject.include_root = true
27
+
28
+ expect(subject.include_root).to eq(true)
29
+ end
30
+ end
31
+
32
+ context 'headers' do
33
+ [:request_headers, :response_headers].each do |type|
34
+ context type do
35
+ it 'is an array' do
36
+ expect(subject.send(type)).to be_a(Array)
37
+ end
38
+
39
+ it 'allows for request headers to be set in bulk' do
40
+ headers = send(type)
41
+
42
+ subject.send("#{type}=", headers)
43
+
44
+ expect(subject.send(type)).to eq(headers)
45
+ end
46
+
47
+ it 'allows for request headers to be set individually' do
48
+ header = { Host: 'api.connexiolabs-qa.com' }
49
+
50
+ expect do
51
+ subject.send(type) << header
52
+ end.to change { subject.send(type).length }.by(1)
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ context 'sample id generation' do
59
+ it 'allows for setting the type for id generation' do
60
+ subject.example_id_type = :uuid
61
+
62
+ expect(subject.example_id_type).to eq(:uuid)
63
+ end
64
+
65
+ it 'guards against unsupported types' do
66
+ expect do
67
+ subject.example_id_type = :foo
68
+ end.to raise_error(UnsupportedIDType)
69
+ end
70
+
71
+ it 'checks for the bson library if requested' do
72
+ expect { subject.example_id_type = :bson }.to raise_error(BSONNotDefinied)
73
+ end
74
+
75
+ it 'generates a valid uuid' do
76
+ test = /[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/i
77
+
78
+ subject.example_id_type = :uuid
79
+
80
+ expect(subject.generate_id).to match(test)
81
+ end
82
+
83
+ it 'generates a valid integer' do
84
+ test = /^[0-9]{1,10}$/
85
+
86
+ subject.example_id_type = :integer
87
+
88
+ expect(subject.generate_id.to_s).to match(test)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeMarkdown::Document do
4
+ include_context 'configuration'
5
+
6
+ before do
7
+ GrapeMarkdown.config do |config|
8
+ config.name = name
9
+ config.description = description
10
+ config.resource_exclusion = [:admin]
11
+ end
12
+
13
+ GrapeMarkdown.config.request_headers = [
14
+ { 'Accept-Charset' => 'utf-8' },
15
+ { 'Connection' => 'keep-alive' }
16
+ ]
17
+
18
+ GrapeMarkdown.config.response_headers = [
19
+ { 'Content-Length' => '21685' },
20
+ { 'Connection' => 'keep-alive' }
21
+ ]
22
+ end
23
+
24
+ subject { GrapeMarkdown::Document.new(SampleApi) }
25
+
26
+ context '#generate' do
27
+ let(:klass) { SampleApi }
28
+
29
+ subject { GrapeMarkdown::Document.new(klass).generate }
30
+
31
+ it 'creates a header from configuration' do
32
+ expect(subject).to include("# #{name}")
33
+ end
34
+
35
+ it 'adds the description' do
36
+ expect(subject).to include(description)
37
+ end
38
+
39
+ it 'includes properties for the resources' do
40
+ expect(subject).to include('Properties')
41
+ `clear`
42
+ puts subject
43
+ end
44
+ end
45
+
46
+ it 'exposes configuration settings' do
47
+ GrapeMarkdown::Config::SETTINGS.each do |setting|
48
+ expect(subject.send(setting)).to eq(GrapeMarkdown.config.send(setting))
49
+ end
50
+ end
51
+
52
+ it 'exposes the raw routes of the given api' do
53
+ expect(subject.routes).to eq(SampleApi.routes)
54
+ end
55
+
56
+ context '#resources' do
57
+ let(:unique_routes) { subject.routes.map(&:route_name).uniq }
58
+
59
+ let(:included_routes) do
60
+ unique_routes.reject do |name|
61
+ GrapeMarkdown.config.resource_exclusion.include?(name.to_sym)
62
+ end
63
+ end
64
+
65
+ it 'aggregates routes into resources' do
66
+ expect(subject.resources.first).to be_a(GrapeMarkdown::Resource)
67
+ end
68
+
69
+ it 'excluded resources based on configuration' do
70
+ expect(subject.resources.map(&:key)).to eq(included_routes)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeMarkdown::Resource do
4
+ include_context 'configuration'
5
+
6
+ subject { GrapeMarkdown::Resource.new('foo', []) }
7
+
8
+ context 'sample' do
9
+ it 'request generation is delegated to a generator' do
10
+ expect(subject.sample_generator).to receive(:request)
11
+
12
+ subject.sample_request
13
+ end
14
+
15
+ it 'response generation is delegated to a generator' do
16
+ expect(subject.sample_generator).to receive(:response)
17
+
18
+ subject.sample_response(GrapeMarkdown::Route.new(Grape::Route.new))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeMarkdown::Route do
4
+ include_context 'configuration'
5
+
6
+ let(:routes) { GrapeMarkdown::Document.new(SampleApi).routes }
7
+
8
+ subject { routes.first }
9
+
10
+ it 'adds a name helper to routes' do
11
+ expect(subject.route_name).to eq('widgets')
12
+ end
13
+
14
+ it 'adds a path helper without format' do
15
+ expect(subject.route_path_without_format).to eq('/widgets')
16
+ end
17
+
18
+ it 'adds a type helper' do
19
+ expect(subject.route_type).to eq('collection')
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeMarkdown::SampleGenerator do
4
+ include_context 'configuration'
5
+
6
+ before do
7
+ GrapeMarkdown.config do |config|
8
+ config.name = name
9
+ config.description = description
10
+ config.include_root = false
11
+ end
12
+ end
13
+
14
+ let(:blueprint) { GrapeMarkdown::Document.new(SampleApi) }
15
+ let(:resource) { blueprint.resources.first }
16
+
17
+ subject { GrapeMarkdown::SampleGenerator.new(resource) }
18
+
19
+ it 'creates a sample hash from a resource' do
20
+ expect(subject.sample).to be_a(Hash)
21
+ end
22
+
23
+ context '#request' do
24
+ it 'creates a sample request in JSON form' do
25
+ expect { JSON.parse(subject.request) }.to_not raise_error
26
+ end
27
+ end
28
+
29
+ context '#response' do
30
+ it 'creates a sample response in JSON form' do
31
+ expect { JSON.parse(subject.response) }.to_not raise_error
32
+ end
33
+
34
+ it 'includes a sample id' do
35
+ expect(JSON.parse(subject.response)['id']).to_not be(nil)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support'))
4
+
5
+ require 'grape/markdown'
6
+
7
+ require 'rubygems'
8
+ require 'bundler'
9
+ Bundler.setup :default, :test
10
+
11
+ require 'coveralls'
12
+ Coveralls.wear!
13
+
14
+ require 'rspec'
15
+ require 'pry'
16
+
17
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
@@ -0,0 +1,13 @@
1
+ shared_context 'configuration' do
2
+ let(:name) { 'some api v1' }
3
+ let(:description) { 'some blueprint description' }
4
+ let(:resource_exclusion) { [:admin, :swagger_docs] }
5
+
6
+ let(:request_headers) do
7
+ [{ 'Accept-Charset' => 'utf-8' }]
8
+ end
9
+
10
+ let(:response_headers) do
11
+ [{ 'Connection' => 'keep-alive' }]
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ class SampleApi < Grape::API
2
+ resource 'widgets' do
3
+ desc 'widgets list'
4
+ get '/' do
5
+ end
6
+
7
+ desc 'individual widget'
8
+ get ':id' do
9
+ end
10
+
11
+ desc 'create a widget'
12
+ params do
13
+ requires :name,
14
+ type: 'string',
15
+ desc: 'the widgets name',
16
+ documentation: { example: 'super widget' }
17
+ optional :description,
18
+ type: 'string',
19
+ desc: 'the widgets name',
20
+ documentation: { example: 'the best widget ever made' }
21
+ end
22
+ post '/' do
23
+ end
24
+
25
+ desc 'update a widget'
26
+ params do
27
+ optional :name, type: 'string', desc: 'the widgets name'
28
+ optional :description, type: 'string', desc: 'the widgets name'
29
+ end
30
+ put ':id' do
31
+ end
32
+ end
33
+
34
+ resource 'admin' do
35
+ get '/' do
36
+ end
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grape-markdown
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Allen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: grape
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: coveralls
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.18'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.18'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.4'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.4'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '4.2'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '4.2'
139
+ - !ruby/object:Gem::Dependency
140
+ name: guard-bundler
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '2.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2.0'
153
+ description: Auto generates Markdown from the docuementation that is created by your
154
+ Grape API
155
+ email:
156
+ - john@threedogconsulting.com
157
+ executables: []
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - ".gitignore"
162
+ - ".rspec"
163
+ - ".rubocop.yml"
164
+ - ".travis.yml"
165
+ - Gemfile
166
+ - Guardfile
167
+ - LICENSE.txt
168
+ - README.md
169
+ - Rakefile
170
+ - grape-markdown.gemspec
171
+ - lib/grape-markdown.rb
172
+ - lib/grape-markdown/config.rb
173
+ - lib/grape-markdown/document.rb
174
+ - lib/grape-markdown/parameter.rb
175
+ - lib/grape-markdown/resource.rb
176
+ - lib/grape-markdown/route.rb
177
+ - lib/grape-markdown/sample_generator.rb
178
+ - lib/grape-markdown/templates/document.md.erb
179
+ - lib/grape-markdown/templates/properties.md.erb
180
+ - lib/grape-markdown/version.rb
181
+ - lib/grape/markdown.rb
182
+ - spec/grape-markdown/config_spec.rb
183
+ - spec/grape-markdown/document_spec.rb
184
+ - spec/grape-markdown/resource_spec.rb
185
+ - spec/grape-markdown/route_spec.rb
186
+ - spec/grape-markdown/sample_generator_spec.rb
187
+ - spec/spec_helper.rb
188
+ - spec/support/config_context.rb
189
+ - spec/support/sample_api.rb
190
+ homepage: https://github.com/connexio-labs/grape-markdown
191
+ licenses:
192
+ - MIT
193
+ metadata: {}
194
+ post_install_message:
195
+ rdoc_options: []
196
+ require_paths:
197
+ - lib
198
+ required_ruby_version: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ required_rubygems_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ requirements: []
209
+ rubyforge_project:
210
+ rubygems_version: 2.2.2
211
+ signing_key:
212
+ specification_version: 4
213
+ summary: Allows for generating a Markdown document from you Grape API
214
+ test_files:
215
+ - spec/grape-markdown/config_spec.rb
216
+ - spec/grape-markdown/document_spec.rb
217
+ - spec/grape-markdown/resource_spec.rb
218
+ - spec/grape-markdown/route_spec.rb
219
+ - spec/grape-markdown/sample_generator_spec.rb
220
+ - spec/spec_helper.rb
221
+ - spec/support/config_context.rb
222
+ - spec/support/sample_api.rb
223
+ has_rdoc: