fictium 0.1.0 → 0.2.0

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 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