grape-markdown 0.0.1

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