fictium 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5023f7f89561a94d98509eef482cac700d07c72e298ebbb206c99d8c6511a3b4
4
- data.tar.gz: 2056592729db8666e2681b4ea0a113a27d780cf8e57d497b60363c5b18dacc93
3
+ metadata.gz: 421d8ab0b9aa1bb7982edf6cf39a156179f2ec1f02c72b7b79bb96e8cfc33f05
4
+ data.tar.gz: 936e8e3849fe1ef47f5c4af212cb7b3985def25284c1f755188f0b42a545b4e7
5
5
  SHA512:
6
- metadata.gz: 9dc29def264f1cd01f92cebdd443bb8cec8cbccd284795b466ed290b7d847e2bb56b05d6bd83f802f25bd4e39f50d16fb31d013a436773f8ad646ab45d779230
7
- data.tar.gz: a1dbe9bb9ad3439640669d29c18f43e423fdd96e1f4030e7a7da9921567856aaa1c989c419993a99b0805fc04778ea4a53de7903352f0625a2ab6072c78a2273
6
+ metadata.gz: 0cc22b7038a90471b18054314feaa182d558fcf29c6f79926ec9c1ccdce4267b69e1fda8b416b1605abbf3b0395c7210c1a17e11c29939fb86ef68e7a491f371
7
+ data.tar.gz: 29044b70d6d0ad7ad05f08eaa565979d43f0827dc554ab81c3c87926bede9b3ecd4b06c171e0071970c76017a5d0b7bfc0b43ca31a10b9b231b3d289614df54a
data/.gitignore CHANGED
@@ -20,3 +20,5 @@ coverage
20
20
  .byebug_history
21
21
 
22
22
  coverage
23
+
24
+ fictium-*.gem
data/CHANGELOG.md CHANGED
@@ -17,6 +17,15 @@
17
17
 
18
18
  ## LOG
19
19
 
20
+ ### 0.2.0 (2019-11-12)
21
+
22
+ New exporter: API Blueprint.
23
+
24
+ #### Changes
25
+
26
+ - Fixed some autocomplete from RSpec.
27
+ - Created the new API Blueprint exporter.
28
+
20
29
  ### 0.1.0 (2019-11-06)
21
30
 
22
31
  Initial release, with Swagger exporter and RSpec integration.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fictium (0.1.0)
4
+ fictium (0.2.0)
5
5
  activesupport (>= 5.3, < 6.2)
6
6
  json-schema (~> 2.8)
7
7
  verbs (~> 2.1.4)
@@ -71,7 +71,7 @@ GEM
71
71
  builder (3.2.3)
72
72
  byebug (11.0.1)
73
73
  concurrent-ruby (1.1.5)
74
- crass (1.0.4)
74
+ crass (1.0.5)
75
75
  diff-lcs (1.3)
76
76
  docile (1.3.2)
77
77
  erubi (1.9.0)
@@ -85,7 +85,7 @@ GEM
85
85
  json (2.2.0)
86
86
  json-schema (2.8.1)
87
87
  addressable (>= 2.4)
88
- loofah (2.2.3)
88
+ loofah (2.3.1)
89
89
  crass (~> 1.0.2)
90
90
  nokogiri (>= 1.5.9)
91
91
  mail (2.7.1)
@@ -98,7 +98,7 @@ GEM
98
98
  mini_portile2 (2.4.0)
99
99
  minitest (5.12.0)
100
100
  nio4r (2.5.2)
101
- nokogiri (1.10.4)
101
+ nokogiri (1.10.5)
102
102
  mini_portile2 (~> 2.4.0)
103
103
  parallel (1.18.0)
104
104
  parser (2.6.5.0)
data/README.md CHANGED
@@ -7,7 +7,17 @@ you can then transform it into easy REST documentation.
7
7
 
8
8
  For the initial release, the support is focused explicitly on generating documentation from [RSpec](https://rspec.info/) tests, and generate an [OpenAPI](https://github.com/OAI/OpenAPI-Specification) [V3.0.2](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md) Document.
9
9
 
10
- Future versions may allow to export to other OpenAPI versions, or even API Bluepint formats.
10
+ The current Gem version allows to export into the following formats:
11
+
12
+ | Exporter | Class name | Notes |
13
+ |--- |--- |--- |
14
+ | [OpenApi 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md) | `Fictium::OpenApi::V3Exporter` | The default exporter for the current Gem version. It doesn't include 100% of the OpenAPI specification, but it works for the most common use cases. It's the format [Swagger](https://swagger.io/) uses for documentation. |
15
+ | [API Blueprint](https://apiblueprint.org/documentation/specification.html) | `Fictium::ApiBlueprintExporter`| Used by [Apiary](https://apiary.io/). A superset of Markdown with special keywords and controls. |
16
+
17
+ Future versions may provide:
18
+
19
+ - OpenApi 1.0 and 2.0
20
+ - Custom HTML exporter
11
21
 
12
22
  ## Installation
13
23
 
@@ -0,0 +1,31 @@
1
+ module Fictium
2
+ class Configuration
3
+ class ApiBlueprint
4
+ attr_accessor :resources_group_name, :host, :footer_header,
5
+ :api_version_formatter, :terms_of_service_formatter, :license_formatter
6
+
7
+ def initialize
8
+ self.host = 'https://change.me.at.api_blueprint.config'
9
+ self.resources_group_name = 'Resources'
10
+ self.footer_header = 'Information and references'
11
+ self.api_version_formatter = method(:format_api_version)
12
+ self.terms_of_service_formatter = method(:format_terms_of_service)
13
+ self.license_formatter = method(:format_license)
14
+ end
15
+
16
+ private
17
+
18
+ def format_api_version(api_version)
19
+ "API version: #{api_version}"
20
+ end
21
+
22
+ def format_terms_of_service(terms_of_service)
23
+ "[Terms of service](#{terms_of_service})."
24
+ end
25
+
26
+ def format_license(license)
27
+ "[#{license[:name]}](#{license[:url]}) license."
28
+ end
29
+ end
30
+ end
31
+ end
@@ -8,18 +8,19 @@ module Fictium
8
8
  http_cookie path_info x-frame-options x-xss-protection x-content-type-options
9
9
  x-download-options x-permitted-cross-domain-policies referrer-policy
10
10
  https script_name http_host remote_addr http_user_agent
11
- http_authorization content_length raw_post_data
11
+ http_authorization content_length raw_post_data referrer-policy
12
12
  ].freeze
13
13
  private_constant :VOWEL
14
14
 
15
- attr_reader :info
15
+ attr_reader :info, :api_blueprint
16
16
  attr_accessor :exporters, :summary_format, :default_action_descriptors,
17
17
  :unknown_action_descriptor, :default_subject, :fixture_path,
18
18
  :export_path, :default_response_content_type, :pretty_print,
19
- :ignored_header_values, :ignored_header_groups
19
+ :ignored_header_values, :ignored_header_groups, :default_person
20
20
 
21
21
  def initialize
22
22
  @info = Fictium::Configuration::Info.new
23
+ @api_blueprint = Fictium::Configuration::ApiBlueprint.new
23
24
  @exporters = [Fictium::OpenApi::V3Exporter.new]
24
25
 
25
26
  @summary_format = method(:default_summary_format)
@@ -36,6 +37,7 @@ module Fictium
36
37
  @default_action_descriptors = {
37
38
  default_summary_for_index: method(:default_summary_for_index),
38
39
  default_summary_for_show: method(:default_summary_for_show),
40
+ default_summary_for_create: method(:default_summary_for_create),
39
41
  default_summary_for_update: method(:default_summary_for_update),
40
42
  default_summary_for_destroy: method(:default_summary_for_destroy)
41
43
  }
@@ -43,9 +45,10 @@ module Fictium
43
45
  end
44
46
 
45
47
  def setup_strings
46
- @default_subject = 'This endpoint'
48
+ @default_subject = nil
47
49
  @export_path = 'doc'
48
50
  @default_response_content_type = 'text/plain'
51
+ @default_person = :second
49
52
  end
50
53
 
51
54
  def default_summary_format(resources)
@@ -54,30 +57,37 @@ module Fictium
54
57
 
55
58
  def default_unknown_action_descriptor(action, action_name)
56
59
  name = action_name.humanize
57
- "#{conjugate(name)} an existing #{action.resource.name}."
60
+ "#{conjugate(name)} #{get_preposition(name)} #{action.resource.name}."
58
61
  end
59
62
 
60
63
  def default_summary_for_index(action)
61
- "#{default_subject} lists all available #{action.resource.name.pluralize}"
64
+ "List all available #{action.resource.name.pluralize}"
62
65
  end
63
66
 
64
67
  def default_summary_for_show(action)
65
68
  name = action.resource.name
66
- "#{default_subject} shows details of #{get_preposition(name)} #{name}."
69
+ "Show details of #{get_preposition(name)} #{name}."
70
+ end
71
+
72
+ def default_summary_for_create(action)
73
+ name = action.resource.name
74
+ "Create a new #{name}."
67
75
  end
68
76
 
69
77
  def default_summary_for_update(action)
70
78
  name = action.resource.name
71
- "#{default_subject} updates #{get_preposition(name)} #{name}."
79
+ "Update #{get_preposition(name)} #{name}."
72
80
  end
73
81
 
74
82
  def default_summary_for_destroy(action)
75
83
  name = action.resource.name
76
- "#{default_subject} destroys #{get_preposition(name)} #{name}."
84
+ "Destroy #{get_preposition(name)} #{name}."
77
85
  end
78
86
 
79
87
  def conjugate(name)
80
- ::Verbs::Conjugator.conjugate name, subject: default_subject, tense: :present, person: :third
88
+ ::Verbs::Conjugator.conjugate name, subject: default_subject,
89
+ tense: :present,
90
+ person: default_person
81
91
  end
82
92
 
83
93
  def get_preposition(resource_name)
@@ -0,0 +1,65 @@
1
+ require_relative 'api_blueprint_exporter/base_formatter'
2
+
3
+ require_relative 'api_blueprint_exporter/header_formatter'
4
+ require_relative 'api_blueprint_exporter/resource_formatter'
5
+ require_relative 'api_blueprint_exporter/action_formatter'
6
+ require_relative 'api_blueprint_exporter/example_formatter'
7
+ require_relative 'api_blueprint_exporter/footer_formatter'
8
+
9
+ module Fictium
10
+ class ApiBlueprintExporter
11
+ def export(document)
12
+ result = process_file(document).presence || ''
13
+ FileUtils.mkdir_p(File.dirname(export_file))
14
+ File.write(export_file, result)
15
+ end
16
+
17
+ private
18
+
19
+ def process_file(document)
20
+ list = [build_header(document), build_footer(document), build_resources(document)]
21
+ clean_items(list)
22
+ end
23
+
24
+ def export_file
25
+ @export_file ||=
26
+ File.join(Fictium.configuration.export_path, 'api_blueprint', 'api.apib')
27
+ end
28
+
29
+ def build_header(document)
30
+ header_formatter.format(document)
31
+ end
32
+
33
+ def build_resources(document)
34
+ mapped_resources = document.resources.map do |resource|
35
+ resource_formatter.format(resource)
36
+ end
37
+ mapped_resources = clean_items(mapped_resources)
38
+ mapped_resources.present? ? "# Group #{resources_group_name} \n\n#{mapped_resources}" : ''
39
+ end
40
+
41
+ def build_footer(document)
42
+ footer_formatter.format(document)
43
+ end
44
+
45
+ def header_formatter
46
+ HeaderFormatter.new
47
+ end
48
+
49
+ def resource_formatter
50
+ @resource_formatter ||= ResourceFormatter.new
51
+ end
52
+
53
+ def footer_formatter
54
+ FooterFormatter.new
55
+ end
56
+
57
+ def resources_group_name
58
+ Fictium.configuration.api_blueprint.resources_group_name
59
+ end
60
+
61
+ def clean_items(items)
62
+ items.select(&:present?).join("\n\n")
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ module Fictium
2
+ class ApiBlueprintExporter
3
+ class ActionFormatter < Fictium::ApiBlueprintExporter::BaseFormatter
4
+ protected
5
+
6
+ def format_sections(action)
7
+ [build_header(action), build_examples(action)]
8
+ end
9
+
10
+ private
11
+
12
+ def build_header(action)
13
+ result = "## #{action.summary} [#{action.method.to_s.upcase} #{action.path}]"
14
+ description = action.description.present? ? "\n\n#{action.description}\n" : ''
15
+ "#{result}#{description}"
16
+ end
17
+
18
+ def build_examples(action)
19
+ default_example = action.default_example
20
+ examples = action.examples.reject { |example| example == default_example }
21
+ sections = ([default_example] + examples).map do |example|
22
+ example_formatter.format(example)
23
+ end
24
+ join_sections(sections)
25
+ end
26
+
27
+ def example_formatter
28
+ @example_formatter ||= ExampleFormatter.new
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ module Fictium
2
+ class ApiBlueprintExporter
3
+ class BaseFormatter
4
+ def format(subject)
5
+ sections = format_sections(subject)
6
+ join_sections(sections)
7
+ end
8
+
9
+ protected
10
+
11
+ def join_sections(sections)
12
+ sections.each(&:strip).select(&:present?).join("\n\n")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,67 @@
1
+ module Fictium
2
+ class ApiBlueprintExporter
3
+ class ExampleFormatter < Fictium::ApiBlueprintExporter::BaseFormatter
4
+ protected
5
+
6
+ def format_sections(example)
7
+ [format_request(example), format_response(example)]
8
+ end
9
+
10
+ private
11
+
12
+ def format_request(example)
13
+ return '' if example.request[:body].blank?
14
+
15
+ result = request_head(example)
16
+ result += "\n #{example.description}\n" if example.description.present?
17
+ result += parse_http_object(example.request)
18
+ result
19
+ end
20
+
21
+ def format_response(example)
22
+ result = response_head(example)
23
+ result += parse_http_object(example.response)
24
+ result
25
+ end
26
+
27
+ def parse_http_object(http_object)
28
+ "#{parse_header(http_object[:header])}#{parse_body(http_object)}"
29
+ end
30
+
31
+ def parse_header(header)
32
+ return '' if header.blank?
33
+
34
+ mapped_headers = header.map do |key, value|
35
+ " #{key}: #{value}"
36
+ end
37
+ <<~HEREDOC
38
+ \x20
39
+ + Header
40
+
41
+ #{mapped_headers.join("\n")}
42
+ HEREDOC
43
+ end
44
+
45
+ def request_head(example)
46
+ "+ Request #{example.summary} \n"
47
+ end
48
+
49
+ def response_head(example)
50
+ "+ Response #{example.response[:status]} (#{example.response[:content_type]})\n"
51
+ end
52
+
53
+ def parse_body(http_element)
54
+ return "\n + Body" if http_element[:body].blank?
55
+
56
+ <<~HEREDOC
57
+ \x20
58
+ + Body
59
+
60
+ ```
61
+ #{http_element[:body]}
62
+ ```
63
+ HEREDOC
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,43 @@
1
+ module Fictium
2
+ class ApiBlueprintExporter
3
+ class FooterFormatter < Fictium::ApiBlueprintExporter::BaseFormatter
4
+ delegate :configuration, to: :Fictium
5
+ delegate :info, :api_blueprint, to: :configuration
6
+ delegate :api_version_formatter, :terms_of_service_formatter, :license_formatter,
7
+ to: :api_blueprint
8
+
9
+ def format(document)
10
+ list = super(document)
11
+ list.present? ? "# #{api_blueprint.footer_header}\n\n#{list}" : ''
12
+ end
13
+
14
+ protected
15
+
16
+ def format_sections(_document)
17
+ [
18
+ api_version_reference,
19
+ terms_of_service_reference,
20
+ license_reference
21
+ ]
22
+ end
23
+
24
+ private
25
+
26
+ def api_version_reference
27
+ return '' if info.version.blank?
28
+
29
+ api_version_formatter.call(info.version)
30
+ end
31
+
32
+ def terms_of_service_reference
33
+ return '' if info.terms_of_service.blank?
34
+
35
+ terms_of_service_formatter.call(info.terms_of_service)
36
+ end
37
+
38
+ def license_reference
39
+ info.license.present? ? license_formatter.call(info.license) : ''
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ module Fictium
2
+ class ApiBlueprintExporter
3
+ class HeaderFormatter
4
+ delegate :title, :description, to: :info
5
+
6
+ def format(_document)
7
+ <<~HEREDOC
8
+ Format: 1A
9
+ Host: #{host}
10
+
11
+ # #{title}
12
+
13
+ #{description}
14
+ HEREDOC
15
+ end
16
+
17
+ private
18
+
19
+ def info
20
+ Fictium.configuration.info
21
+ end
22
+
23
+ def host
24
+ Fictium.configuration.api_blueprint.host
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module Fictium
2
+ class ApiBlueprintExporter
3
+ class ResourceFormatter < Fictium::ApiBlueprintExporter::BaseFormatter
4
+ protected
5
+
6
+ def format_sections(resource)
7
+ [build_header(resource), build_actions(resource)]
8
+ end
9
+
10
+ private
11
+
12
+ def build_header(resource)
13
+ header = "# #{resource.name&.pluralize&.humanize} [#{resource.base_path}]"
14
+ header += "\n#{resource.summary.capitalize}\n" if resource.summary.present?
15
+ description = resource.description.present? ? "\n#{resource.description}\n" : ''
16
+ "#{header}#{description}"
17
+ end
18
+
19
+ def build_actions(resource)
20
+ sections = resource.actions.map do |action|
21
+ action_formatter.format(action)
22
+ end
23
+ join_sections(sections)
24
+ end
25
+
26
+ def action_formatter
27
+ @action_formatter ||= ActionFormatter.new
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,6 @@
1
1
  require_relative 'configurations/configuration'
2
2
  require_relative 'configurations/info'
3
+ require_relative 'configurations/api_blueprint'
3
4
 
4
5
  require_relative 'evaluators/parameter_evaluator'
5
6
  require_relative 'evaluators/schema_evaluator'
@@ -13,3 +14,6 @@ require_relative 'poros/resource'
13
14
 
14
15
  # Require default (OpenApi v3) exporter
15
16
  require_relative 'exporters/open_api/v3_exporter'
17
+
18
+ # Other exporters created by this gem
19
+ require_relative 'exporters/api_blueprint_exporter'
@@ -20,6 +20,14 @@ module Fictium
20
20
  metadata[:fictium_action].add_params_in(section, &block)
21
21
  end
22
22
 
23
+ def action_summary(text)
24
+ metadata[:fictium_action].summary = text
25
+ end
26
+
27
+ def action_description(text)
28
+ metadata[:fictium_action].description = text
29
+ end
30
+
23
31
  def example(*args, **kwargs)
24
32
  Fictium::RSpec::Proxies::Example.new(self, args, kwargs)
25
33
  end
@@ -8,7 +8,8 @@ module Fictium
8
8
  example.response.merge!(
9
9
  status: response.status,
10
10
  body: response.body,
11
- content_type: response.content_type
11
+ content_type: response.content_type,
12
+ header: filter_header(response.header.to_h)
12
13
  )
13
14
  process_http_request(example, response.request)
14
15
  return unless example.default?
@@ -26,7 +27,8 @@ module Fictium
26
27
  example.request ||= {}
27
28
  example.request.merge!(
28
29
  content_type: request.content_type,
29
- body: request.body.string
30
+ body: request.body.string,
31
+ header: filter_header(request.headers.to_h)
30
32
  )
31
33
  extract_method(example, request)
32
34
  return unless example.default?
@@ -38,6 +40,30 @@ module Fictium
38
40
  action = example.action
39
41
  action.method = request.method.downcase.to_sym if action.method.blank?
40
42
  end
43
+
44
+ def filter_header(header)
45
+ valid_keys = header.keys.select { |name| valid_header?(name) }
46
+ header.slice(*valid_keys).transform_keys do |key|
47
+ (key.start_with?('HTTP_') ? key.sub('HTTP_', '') : key).sub('_', '-')
48
+ end
49
+ end
50
+
51
+ def valid_header?(name)
52
+ downcase_name = name.downcase
53
+ out_of_header_group?(downcase_name) && out_of_excluded_headers?(downcase_name)
54
+ end
55
+
56
+ def out_of_header_group?(name)
57
+ ignored_header_groups.none? { |group| name.start_with?(group) }
58
+ end
59
+
60
+ def out_of_excluded_headers?(name)
61
+ Fictium.configuration.ignored_header_values.exclude?(name)
62
+ end
63
+
64
+ def ignored_header_groups
65
+ Fictium.configuration.ignored_header_groups
66
+ end
41
67
  end
42
68
  end
43
69
  end
@@ -1,5 +1,5 @@
1
1
  module Fictium
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  RAILS_MIN_VERSION = '>= 5.3'.freeze
4
4
  RAILS_MAX_VERSION = '< 6.2'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fictium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ramiro Rojo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-06 00:00:00.000000000 Z
11
+ date: 2019-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -258,11 +258,19 @@ files:
258
258
  - bin/setup
259
259
  - fictium.gemspec
260
260
  - lib/fictium.rb
261
+ - lib/fictium/configurations/api_blueprint.rb
261
262
  - lib/fictium/configurations/configuration.rb
262
263
  - lib/fictium/configurations/info.rb
263
264
  - lib/fictium/engine.rb
264
265
  - lib/fictium/evaluators/parameter_evaluator.rb
265
266
  - lib/fictium/evaluators/schema_evaluator.rb
267
+ - lib/fictium/exporters/api_blueprint_exporter.rb
268
+ - lib/fictium/exporters/api_blueprint_exporter/action_formatter.rb
269
+ - lib/fictium/exporters/api_blueprint_exporter/base_formatter.rb
270
+ - lib/fictium/exporters/api_blueprint_exporter/example_formatter.rb
271
+ - lib/fictium/exporters/api_blueprint_exporter/footer_formatter.rb
272
+ - lib/fictium/exporters/api_blueprint_exporter/header_formatter.rb
273
+ - lib/fictium/exporters/api_blueprint_exporter/resource_formatter.rb
266
274
  - lib/fictium/exporters/open_api/schemas/3.0.0.json
267
275
  - lib/fictium/exporters/open_api/v3_exporter.rb
268
276
  - lib/fictium/exporters/open_api/v3_exporter/content_formatter.rb