td_rspec-api-blueprint-formatter 0.2.0

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: 59a8e91cd531a25b3b19452d5dce0beef7f3f22d
4
+ data.tar.gz: 49503ca3349755f31f1caf88a65c0c1c175db506
5
+ SHA512:
6
+ metadata.gz: 2933fc8ed8472b06ffd1874537fd4771a2e1f8b0225469cf51d0657772479197db49a6770dda24ebe52ee53e40af5443c7be5f08c9353455e045a50ce4e7cb66
7
+ data.tar.gz: 2715f7b0116bad33ef7d8bdc189d812917d60e77cddb683ffc7237d375d36fb4ca8ee56759b081bc9d7007d8cc3538fa8b617e1bb1d769c1579fdc9b3e8e4817
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'api_blueprint_formatter'
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
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ require_relative 'api_blueprint/rspec_formatter'
2
+
3
+ # keeping reference for backwards compatibility
4
+ ApiBlueprint = APIBlueprint::RspecFormatter
@@ -0,0 +1,21 @@
1
+ module APIBlueprint
2
+ module Configurable
3
+ def configure
4
+ yield(configuration)
5
+ end
6
+
7
+ def configuration
8
+ @configuration ||= default_configuration
9
+ end
10
+
11
+ def reset_configuration!
12
+ @configuration = default_configuration
13
+ end
14
+
15
+ def default_configuration
16
+ configuration = Configuration.new
17
+ configuration.output_source = true
18
+ configuration
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ module APIBlueprint
2
+ class Configuration
3
+ attr_accessor :output_source
4
+ end
5
+ end
@@ -0,0 +1,84 @@
1
+ module APIBlueprint
2
+ # Collects example for API documentation
3
+ class OutputCollector
4
+ attr_accessor :resources, :resource_parameters, :configuration
5
+
6
+ def initialize(configuration)
7
+ @configuration = configuration
8
+ @resources = {}
9
+ @resource_parameters = {}
10
+ end
11
+
12
+ def add_example(metadata, example_block, request, response)
13
+ @resources.deep_merge!(
14
+ metadata[:resource_group] => build_resource(example_block, metadata,
15
+ request, response)
16
+ )
17
+ end
18
+
19
+ private
20
+
21
+ def build_resource(example_block, metadata, request, response)
22
+ @resource_parameters[metadata[:resource]] = metadata[:resource_parameters]
23
+
24
+ {
25
+ metadata[:resource] => {
26
+ metadata[:action] => build_action(example_block, metadata, request,
27
+ response)
28
+ }
29
+ }
30
+ end
31
+
32
+ def build_action(example_block, metadata, request, response)
33
+ example_description = metadata[:example_description] ||
34
+ metadata[:description].tr('()', '/')
35
+
36
+ {
37
+ description: metadata[:action_description],
38
+ examples: {
39
+ example_description => build_example(example_block, metadata,
40
+ request, response)
41
+ }
42
+ }
43
+ end
44
+
45
+ def build_example(example_block, metadata, request, response)
46
+ {
47
+ request: build_request(request),
48
+ source: example_block.source,
49
+ location: metadata[:location],
50
+ response: build_response(response)
51
+ }
52
+ end
53
+
54
+ def build_request(request)
55
+ {
56
+ parameters: request_parameters(request),
57
+ format: request.format
58
+ }
59
+ end
60
+
61
+ def request_parameters(request)
62
+ path_params = request.path_parameters.keys.map(&:to_s)
63
+ request.parameters.except(*path_params).to_json
64
+ rescue Encoding::UndefinedConversionError
65
+ 'binary'
66
+ end
67
+
68
+ def build_response(response)
69
+ {
70
+ status: response.status,
71
+ content_type: response.content_type.to_s,
72
+ body: response_body(response)
73
+ }
74
+ end
75
+
76
+ def response_body(response)
77
+ if response['Content-Transfer-Encoding'] == 'binary'
78
+ 'binary'
79
+ else
80
+ response.body
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,118 @@
1
+ module APIBlueprint
2
+ # Prints API blueprint output
3
+ class OutputPrinter
4
+ attr_accessor :configuration, :examples, :output
5
+
6
+ def initialize(configuration, output_collector, output)
7
+ @configuration = configuration
8
+ @output_collector = output_collector
9
+ @output = output
10
+ end
11
+
12
+ def print
13
+ sorted_examples.each do |resource_group_name, resource_group_resources|
14
+ print_resource_group(resource_group_name, resource_group_resources)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def print_resource_group(resource_group_name, resource_group_resources)
21
+ output.puts "# Group #{resource_group_name}"
22
+ resource_group_resources.each(&method(:print_resource))
23
+ end
24
+
25
+ def sorted_examples
26
+ @output_collector.resources.sort_by { |k, _v| k }
27
+ end
28
+
29
+ def print_resource(resource_name, actions)
30
+ validate_resource_name(resource_name)
31
+
32
+ output.puts "# #{resource_name}"
33
+
34
+ print_resource_parameters(resource_name)
35
+
36
+ validate_http_verbs(actions, resource_name)
37
+
38
+ actions.each(&method(:print_action))
39
+ end
40
+
41
+ def validate_http_verbs(actions, resource_name)
42
+ http_verbs = actions.keys.map do |action|
43
+ action.scan(/\[([A-Z]+)\]/).flatten[0]
44
+ end
45
+
46
+ return if http_verbs.length == http_verbs.uniq.length
47
+
48
+ raise "Action HTTP verbs are not unique #{actions.keys.inspect} for "\
49
+ "resource: '#{resource_name}'"
50
+ end
51
+
52
+ def validate_resource_name(resource_name)
53
+ return if resource_name =~ %r{^[^\[\]]*\[/[^\]]+\]}
54
+ raise "resource: '#{resource_name}' is invalid. :resource needs to be "\
55
+ 'specified according to https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#resource-section'
56
+ end
57
+
58
+ def print_action(action_name, action_meta_data)
59
+ output.puts "## #{action_name}\n" \
60
+ "\n" \
61
+ "#{action_meta_data[:description]}\n" \
62
+ "\n" \
63
+
64
+ action_meta_data[:examples].each(&method(:print_example))
65
+ end
66
+
67
+ def print_example(example_description, example_metadata)
68
+ print_request(example_description, example_metadata)
69
+ print_source(example_metadata)
70
+ print_response(example_metadata)
71
+ end
72
+
73
+ def print_resource_parameters(resource_name)
74
+ return unless @output_collector.resource_parameters[resource_name]
75
+
76
+ output.puts "\n"
77
+ output.puts '+ Parameters'
78
+
79
+ @output_collector.resource_parameters[resource_name].each do |param, desc|
80
+ output.puts " + #{param} #{desc}\n"
81
+ end
82
+
83
+ output.puts "\n"
84
+ end
85
+
86
+ def print_request(example_description, example_metadata)
87
+ output.puts "+ Request #{example_description}\n" \
88
+ "\n" \
89
+ " #{example_metadata[:request][:parameters]}\n" \
90
+ " \n"
91
+ end
92
+
93
+ def print_source(example_metadata)
94
+ return if @configuration.output_source
95
+
96
+ output.puts " Location: #{example_metadata[:location]}\n" \
97
+ " Source code:\n" \
98
+ " \n" \
99
+ "#{indent_lines(8, example_metadata[:source])}\n" \
100
+ "\n"
101
+ end
102
+
103
+ def print_response(example_metadata)
104
+ output.puts "+ Response #{example_metadata[:response][:status]} "\
105
+ "(#{example_metadata[:response][:content_type]})\n" \
106
+ "\n" \
107
+ " #{example_metadata[:response][:body]}\n" \
108
+ "\n"
109
+ end
110
+
111
+ def indent_lines(number_of_spaces, string)
112
+ string
113
+ .split("\n")
114
+ .map { |a| a.prepend(' ' * number_of_spaces) }
115
+ .join("\n")
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,60 @@
1
+ require 'rspec/core/formatters'
2
+ require 'rspec/core/formatters/base_formatter'
3
+
4
+ require_relative 'output_collector'
5
+ require_relative 'output_printer'
6
+ require_relative 'configurable'
7
+ require_relative 'configuration'
8
+
9
+ module APIBlueprint
10
+ # RSpec formatter for API blueprint
11
+ class RspecFormatter < RSpec::Core::Formatters::BaseFormatter
12
+ VERSION = '0.1.0'.freeze
13
+
14
+ extend Configurable
15
+
16
+ RSpec::Core::Formatters.register self, :example_passed, :example_started,
17
+ :stop
18
+
19
+ def initialize(output)
20
+ super
21
+
22
+ configure_rspec
23
+
24
+ @output_collector = OutputCollector.new(configuration)
25
+ end
26
+
27
+ def example_started(notification)
28
+ @example_group_instance = notification.example.example_group_instance
29
+ end
30
+
31
+ def example_passed(passed)
32
+ metadata = passed.example.metadata
33
+
34
+ if metadata[:apidoc] && metadata[:resource_group] &&
35
+ metadata[:resource] && metadata[:action] &&
36
+ metadata[:action_description]
37
+
38
+ @output_collector
39
+ .add_example(metadata,
40
+ passed.example.instance_variable_get(:@example_block),
41
+ @example_group_instance.request,
42
+ @example_group_instance.response)
43
+ end
44
+ end
45
+
46
+ def stop(_notification)
47
+ OutputPrinter.new(configuration, @output_collector, output).print
48
+ end
49
+
50
+ private
51
+
52
+ def configure_rspec
53
+ RSpec.configuration.silence_filter_announcements = true
54
+ end
55
+
56
+ def configuration
57
+ self.class.configuration
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApiBlueprint do
4
+ it 'has a version number' do
5
+ expect(ApiBlueprint::VERSION).not_to be nil
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'api_blueprint'
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: td_rspec-api-blueprint-formatter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Nam Chu Hoai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.45'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.45'
69
+ description: Use your Rspec tests to build your API documentation
70
+ email:
71
+ - nambrot@googlemail.com
72
+ executables:
73
+ - console
74
+ - setup
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - bin/console
79
+ - bin/setup
80
+ - lib/api_blueprint.rb
81
+ - lib/api_blueprint/configurable.rb
82
+ - lib/api_blueprint/configuration.rb
83
+ - lib/api_blueprint/output_collector.rb
84
+ - lib/api_blueprint/output_printer.rb
85
+ - lib/api_blueprint/rspec_formatter.rb
86
+ - spec/rspec/api/blueprint/formatter_spec.rb
87
+ - spec/spec_helper.rb
88
+ homepage: https://github.com/nambrot/rspec-api-blueprint-formatter
89
+ licenses:
90
+ - MIT
91
+ metadata:
92
+ allowed_push_host: https://rubygems.org
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.5
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Use your Rspec tests to build your API documentation
113
+ test_files:
114
+ - spec/rspec/api/blueprint/formatter_spec.rb
115
+ - spec/spec_helper.rb