medschool 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: 53704fee4693a236c333219f661fa9454f34c1a8
4
+ data.tar.gz: 98067f01b54b65da5ae474683c0795bbfcf627a7
5
+ SHA512:
6
+ metadata.gz: 05f192ff18bf6a245ef9aa71d398c82db1a2ce7f2efc92870019562c8c061e10b57dd4411e96279ee39d7ae65409bf7e786299f2957bc47b71bdf4cf8f3fba42
7
+ data.tar.gz: 818bfed1da98d9b4943e2eb35e298b739c7d4859c70ba8e2d8f8ff289007fddc471d5813cff267c4eb5681cdab91a97c0a6ce32c33ca11bc5e5834638344a865
data/.gitignore ADDED
@@ -0,0 +1,21 @@
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
18
+ tmp/
19
+ .ds_store
20
+ .ruby-version
21
+ .ruby-gemset
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in medschool.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Emily Dobervich; TeamSnap, Inc.
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,81 @@
1
+ # Medschool
2
+
3
+ Make your docs smarter! Medschool is a tool for documenting APIs that helps you keep your documentation in-sync with reality.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'medschool'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install medschool
18
+
19
+ ## Usage
20
+
21
+ Write markdown narrative in a `*.md.msd` file:
22
+
23
+ ```
24
+ ## Creating a new user
25
+
26
+ This is how you create a new user!
27
+ ```
28
+
29
+ Embed requests and responses:
30
+
31
+ ```
32
+ ## Creating a new user
33
+
34
+ This is how you create a new user!
35
+
36
+ @@@request: creating_a_user
37
+ type: json
38
+ method: post
39
+ route: '/sports'
40
+ data:
41
+ user:
42
+ properties:
43
+ username:
44
+ value: "Bobby"
45
+ type: string
46
+ password:
47
+ value: "password"
48
+ type: string
49
+ @@@
50
+
51
+ ### Response
52
+
53
+ @@@response: creating_a_user
54
+ type: json
55
+ status: 200
56
+ data:
57
+ user:
58
+ username: "Bobby"
59
+ @@@
60
+ ```
61
+
62
+ ### Using Medschool in tests
63
+
64
+ ```ruby
65
+ msd = Medschool::Example.new(file, request_or_response_name)
66
+ msd.request_hash # Request hash
67
+ msd.validate_response(response) # Validates a response object
68
+ ```
69
+
70
+ ### Generating HTML Docs
71
+
72
+ `rake docs:generate`
73
+
74
+ ## Contributing
75
+
76
+ 1. Fork it
77
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
78
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
79
+ 4. Push to the branch (`git push origin my-new-feature`)
80
+ 5. Create new Pull Request
81
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/medschool ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env ruby
@@ -0,0 +1,42 @@
1
+ module Medschool
2
+ class Example
3
+ def initialize(path, example_name)
4
+ self.path = File.join(Medschool.root, "#{path}.md.med")
5
+ self.example_name = example_name
6
+ load_examples
7
+ end
8
+
9
+ def request_hash
10
+ Medschool::HashGenerator.new(raw_request[:data]).process
11
+ end
12
+
13
+ def validate_response(response)
14
+ Medschool::Validator.new(raw_response, response).validate!
15
+ end
16
+
17
+ private
18
+
19
+ attr_accessor :path, :example_name, :raw_request, :raw_response
20
+
21
+ def load_examples
22
+ contents = File.read(path)
23
+ regex = /^@@@(?<type>request|response): #{example_name}\n(?<yaml>.*?)@@@$/m
24
+ matches = contents.scan(regex)
25
+
26
+ matches.each do |match|
27
+ load_example_from_match(match)
28
+ end
29
+ end
30
+
31
+ def load_example_from_match(match)
32
+ example = YAML.load(match[1]).with_indifferent_access
33
+
34
+ case match[0]
35
+ when "request"
36
+ self.raw_request = example
37
+ when "response"
38
+ self.raw_response = example
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ module Medschool
2
+ class HashGenerator < SchemaProcessor
3
+ def initialize(data)
4
+ self.data = data
5
+ end
6
+
7
+ def process
8
+ process_properties(data)
9
+ end
10
+
11
+ def check_item(item)
12
+ item['value']
13
+ end
14
+
15
+ private
16
+
17
+ attr_accessor :data
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ module Medschool
2
+ module Rails
3
+ end
4
+ end
5
+
6
+ Medschool.root = Rails.root.join("features/markdown")
7
+
8
+ RSpec.configure do |config|
9
+ config.include RSpec::Rails::RequestExampleGroup, :example_group => {
10
+ :file_path => config.escaped_path(%w[features])
11
+ }
12
+ end
@@ -0,0 +1,5 @@
1
+ class Medschool::Railtie < Rails::Railtie
2
+ rake_tasks do
3
+ load "medschool/tasks/generate.rake"
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ module Medschool
2
+ class SchemaProcessor
3
+ def process_item(item, *args)
4
+ if Array === item
5
+ process_items(item, *args)
6
+ elsif item['properties']
7
+ process_properties(item['properties'], *args)
8
+ elsif item['type']
9
+ check_item(item, *args)
10
+ end
11
+ end
12
+
13
+ def process_properties(properties, *args)
14
+ h = HashWithIndifferentAccess.new
15
+ properties ||= {}
16
+ properties.each do |key, value|
17
+ h[key] = properties_process_item(value, key, *args)
18
+ end
19
+ h
20
+ end
21
+
22
+ def properties_process_item(value, key, *args)
23
+ process_item(value, *args)
24
+ end
25
+
26
+ def process_items(items, *args)
27
+ items.collect do |item|
28
+ items_process_item(item, *args)
29
+ end
30
+ end
31
+
32
+ def items_process_item(item, *args)
33
+ process_item(item, *args)
34
+ end
35
+
36
+ def check_item(item, *args)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,55 @@
1
+ require 'redcarpet'
2
+
3
+ class CodeRayRenderer < Redcarpet::Render::HTML
4
+ def block_code(code, language)
5
+ CodeRay.scan(code, language).div
6
+ end
7
+ end
8
+
9
+ namespace :docs do
10
+ task :generate do
11
+ markdown_renderer = Redcarpet::Markdown.new(CodeRayRenderer.new, {:fenced_code_blocks => true, :disable_indented_code_blocks => true })
12
+
13
+ `rm -rf #{Rails.root.join('docs')}`
14
+ `mkdir -p #{Rails.root.join('docs')}`
15
+
16
+ Medschool.root = Rails.root.join('features/markdown')
17
+
18
+ Dir[Medschool.root.join("**/*.md.med")].each do |markdown|
19
+ preprocessed = nil
20
+
21
+ File.open(markdown) do |f|
22
+ preprocessed = preprocess_markdown(f.read)
23
+ end
24
+
25
+ File.open(Rails.root.join('docs', File.basename(markdown, ".md.med") + '.html'), "w") do |f|
26
+ f.write markdown_renderer.render(preprocessed)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def preprocess_markdown(markdown)
33
+ example_regex = /^@@@(?<type>request|response): (?<name>[\w\-]*?)\n(?<yaml>.*?)@@@$/m
34
+ results = markdown.gsub(example_regex) do |match|
35
+ hash = YAML.load($3).with_indifferent_access
36
+ data = hash[:data]
37
+
38
+ h = Medschool::HashGenerator.new(data).process
39
+
40
+ curl = $1 == "request" ? curl_from_params(hash, h) : ''
41
+
42
+ curl + (h.size > 0 ? "```json\n#{MultiJson.dump(h, :pretty => true)}\n```" : "")
43
+ end
44
+ end
45
+
46
+ def curl_from_params(hash, params)
47
+ method = hash[:method]
48
+ route = hash[:route]
49
+
50
+ if params.size > 0
51
+ "`$ curl -X #{method.upcase} -d \"#{MultiJson.dump(params).gsub("\"", "\\\"").gsub(",", ", ")}\" http://localhost:3000#{route}`\n\n"
52
+ else
53
+ "`$ curl -X #{method.upcase} http://localhost:3000#{route}`\n\n"
54
+ end
55
+ end
@@ -0,0 +1,105 @@
1
+ module Medschool
2
+ class Validator < SchemaProcessor
3
+ def initialize(example, response)
4
+ self.example = example
5
+ self.response = response
6
+ end
7
+
8
+ def validate!
9
+ process_status(example[:status], response.status)
10
+ process_headers(example[:headers], response)
11
+ if response.body.empty?
12
+ {}
13
+ else
14
+ process_properties(example[:data], JSON.parse(response.body))
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def process_status(example_status, status)
21
+ unless example_status == status
22
+ raise "Expected status code of #{example_status}. Got: #{status}."
23
+ end
24
+ end
25
+
26
+ def process_headers(headers, response)
27
+ if headers.respond_to?(:each)
28
+ headers.each do |header, value|
29
+ unless response.headers[header] == value
30
+ raise "Expected #{header}: '#{response.headers[header]}' to be '#{value}'"
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def properties_process_item(value, key, hash)
37
+ process_item(value, hash[key])
38
+ end
39
+
40
+ def items_process_item(item, array)
41
+ validated = false
42
+
43
+ array.each do |array_item|
44
+ begin
45
+ process_item(item, array_item)
46
+ validated = true
47
+ rescue
48
+ end
49
+ end
50
+
51
+ pretty_item = HashGenerator.new(:item => item).process
52
+
53
+ raise "Array item:\n#{MultiJson.dump(pretty_item[:item], :pretty => true)}\nNot found in:\n#{MultiJson.dump(array, :pretty => true)}" unless validated
54
+ end
55
+
56
+ def check_item(item_schema, item)
57
+ case
58
+ when item_schema['pattern']
59
+ check_pattern(item_schema['pattern'], item)
60
+ when item_schema['exact'] != false
61
+ check_value(item_schema['value'], item)
62
+ when item_schema['type']
63
+ check_type(item_schema['type'], item)
64
+ end
65
+ end
66
+
67
+ def check_value(schema_value, value)
68
+ unless schema_value == value
69
+ raise "Expected '#{value}' to equal '#{schema_value}'"
70
+ end
71
+ end
72
+
73
+ def check_pattern(schema_pattern, value)
74
+ regexp = Regexp.new(schema_pattern)
75
+ value = value.to_s
76
+
77
+ unless value =~ regexp
78
+ raise "Expected '#{value}' to match /#{schema_pattern}/"
79
+ end
80
+ end
81
+
82
+ def check_type(type, value)
83
+ case type
84
+ when 'integer'
85
+ check_integer(value)
86
+ when 'timestamp'
87
+ check_timestamp(value)
88
+ end
89
+ end
90
+
91
+ def check_integer(value)
92
+ if !(Integer === value)
93
+ raise "Expected '#{value}' to be an integer"
94
+ end
95
+ end
96
+
97
+ def check_timestamp(value)
98
+ Time.parse(value)
99
+ rescue
100
+ raise "Expected '#{value}' to be a timestamp"
101
+ end
102
+
103
+ attr_accessor :example, :response
104
+ end
105
+ end
@@ -0,0 +1,3 @@
1
+ module Medschool
2
+ VERSION = "0.1"
3
+ end
data/lib/medschool.rb ADDED
@@ -0,0 +1,13 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ module Medschool
4
+ class << self
5
+ attr_accessor :root
6
+ end
7
+ end
8
+
9
+ require "medschool/example"
10
+ require "medschool/schema_processor"
11
+ require "medschool/hash_generator"
12
+ require "medschool/validator"
13
+ require "medschool/railtie" if defined?(Rails)
data/medschool.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'medschool/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "medschool"
8
+ gem.version = Medschool::VERSION
9
+ gem.authors = ["Emily Dobervich"]
10
+ gem.email = ["emily@narwhunderful.com"]
11
+ gem.description = %q{Medschool makes your API Docs smarter}
12
+ gem.summary = %q{Document your API}
13
+ gem.homepage = "http://github.com/teamsnap/medschool"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency("redcarpet")
21
+ gem.add_dependency("coderay")
22
+ gem.add_dependency("json-schema")
23
+ gem.add_dependency("multi_json")
24
+
25
+ gem.add_development_dependency("pry")
26
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: medschool
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Emily Dobervich
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redcarpet
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: coderay
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: json-schema
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: multi_json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Medschool makes your API Docs smarter
84
+ email:
85
+ - emily@narwhunderful.com
86
+ executables:
87
+ - medschool
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/medschool
97
+ - lib/medschool.rb
98
+ - lib/medschool/example.rb
99
+ - lib/medschool/hash_generator.rb
100
+ - lib/medschool/rails.rb
101
+ - lib/medschool/railtie.rb
102
+ - lib/medschool/schema_processor.rb
103
+ - lib/medschool/tasks/generate.rake
104
+ - lib/medschool/validator.rb
105
+ - lib/medschool/version.rb
106
+ - medschool.gemspec
107
+ homepage: http://github.com/teamsnap/medschool
108
+ licenses: []
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.0.14
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Document your API
130
+ test_files: []
131
+ has_rdoc: