media_types-serialization 0.8.0 → 1.1.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +16 -3
  3. data/.prettierrc +1 -0
  4. data/CHANGELOG.md +42 -0
  5. data/CODE_OF_CONDUCT.md +74 -74
  6. data/Gemfile.lock +74 -83
  7. data/README.md +691 -179
  8. data/lib/media_types/problem.rb +64 -0
  9. data/lib/media_types/serialization.rb +497 -173
  10. data/lib/media_types/serialization/base.rb +115 -91
  11. data/lib/media_types/serialization/error.rb +186 -0
  12. data/lib/media_types/serialization/fake_validator.rb +52 -0
  13. data/lib/media_types/serialization/serialization_dsl.rb +117 -0
  14. data/lib/media_types/serialization/serialization_registration.rb +245 -0
  15. data/lib/media_types/serialization/serializers/api_viewer.rb +133 -0
  16. data/lib/media_types/serialization/serializers/common_css.rb +168 -0
  17. data/lib/media_types/serialization/serializers/endpoint_description_serializer.rb +80 -0
  18. data/lib/media_types/serialization/serializers/fallback_not_acceptable_serializer.rb +85 -0
  19. data/lib/media_types/serialization/serializers/fallback_unsupported_media_type_serializer.rb +58 -0
  20. data/lib/media_types/serialization/serializers/input_validation_error_serializer.rb +89 -0
  21. data/lib/media_types/serialization/serializers/problem_serializer.rb +100 -0
  22. data/lib/media_types/serialization/utils/accept_header.rb +77 -0
  23. data/lib/media_types/serialization/utils/accept_language_header.rb +82 -0
  24. data/lib/media_types/serialization/utils/header_list.rb +89 -0
  25. data/lib/media_types/serialization/version.rb +1 -1
  26. data/media_types-serialization.gemspec +48 -50
  27. metadata +48 -79
  28. data/.travis.yml +0 -17
  29. data/lib/generators/media_types/serialization/api_viewer/api_viewer_generator.rb +0 -25
  30. data/lib/generators/media_types/serialization/api_viewer/templates/api_viewer.html.erb +0 -98
  31. data/lib/generators/media_types/serialization/api_viewer/templates/initializer.rb +0 -33
  32. data/lib/generators/media_types/serialization/api_viewer/templates/template_controller.rb +0 -23
  33. data/lib/media_types/serialization/media_type/register.rb +0 -4
  34. data/lib/media_types/serialization/migrations_command.rb +0 -38
  35. data/lib/media_types/serialization/migrations_support.rb +0 -50
  36. data/lib/media_types/serialization/mime_type_support.rb +0 -64
  37. data/lib/media_types/serialization/no_content_type_given.rb +0 -11
  38. data/lib/media_types/serialization/no_media_type_serializers.rb +0 -11
  39. data/lib/media_types/serialization/no_serializer_for_content_type.rb +0 -15
  40. data/lib/media_types/serialization/renderer.rb +0 -41
  41. data/lib/media_types/serialization/renderer/register.rb +0 -4
  42. data/lib/media_types/serialization/wrapper.rb +0 -13
  43. data/lib/media_types/serialization/wrapper/html_wrapper.rb +0 -45
  44. data/lib/media_types/serialization/wrapper/media_collection_wrapper.rb +0 -59
  45. data/lib/media_types/serialization/wrapper/media_index_wrapper.rb +0 -59
  46. data/lib/media_types/serialization/wrapper/media_object_wrapper.rb +0 -55
  47. data/lib/media_types/serialization/wrapper_support.rb +0 -38
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'media_types/serialization/base'
5
+ require 'media_types/serialization/utils/accept_language_header'
6
+
7
+ module MediaTypes
8
+ module Serialization
9
+ module Serializers
10
+ class ProblemSerializer < MediaTypes::Serialization::Base
11
+
12
+ unvalidated 'application/vnd.delftsolutions.problem'
13
+ disable_wildcards
14
+
15
+ output do |problem, _, context|
16
+ raise 'No translations defined, add at least one title' unless problem.translations.keys.any?
17
+
18
+ accept_language_header = Utils::AcceptLanguageHeader.new(context.request.get_header(HEADER_ACCEPT_LANGUAGE) || '')
19
+ translation_entry = accept_language_header.map do |locale|
20
+ problem.translations.keys.find do |l|
21
+ l.start_with? locale.locale
22
+ end
23
+ end.compact.first || problem.translations.keys.first
24
+ translation = problem.translations[translation_entry]
25
+
26
+ title = translation[:title]
27
+ detail = translation[:detail] || problem.error.message
28
+
29
+ problem.custom_attributes.each do |key, value|
30
+ attribute key, value
31
+ end
32
+
33
+ attribute :type, problem.type
34
+ attribute :title, title unless title.nil?
35
+ attribute :detail, detail unless detail.nil?
36
+ attribute :instance, problem.instance unless problem.instance.nil?
37
+
38
+ emit
39
+ end
40
+ output_alias 'application/problem+json'
41
+
42
+ output_raw view: :html do |problem, _, context|
43
+ accept_language_header = Utils::AcceptLanguageHeader.new(context.request.get_header(HEADER_ACCEPT_LANGUAGE) || '')
44
+ translation_entry = accept_language_header.map do |locale|
45
+ problem.translations.keys.find do |l|
46
+ l.starts_with? locale.locale
47
+ end
48
+ end.compact.first || problem.translations.keys.first
49
+ translation = problem.translations[translation_entry]
50
+
51
+ title = translation[:title]
52
+ detail = translation[:detail] || problem.error.message
53
+
54
+ detail_lang = translation[:detail].nil? ? 'en' : translation_entry
55
+
56
+ input = OpenStruct.new(
57
+ title: title,
58
+ detail: detail,
59
+ help_url: problem.type,
60
+ css: CommonCSS.css,
61
+ )
62
+
63
+ template = ERB.new <<-TEMPLATE
64
+ <html lang="en">
65
+ <head>
66
+ <title>Error - <%= CGI::escapeHTML(title) %></title>
67
+ <style>
68
+ <%= css.split("\n").join("\n ") %>
69
+ </style>
70
+ </head>
71
+ <body>
72
+ <header>
73
+ <div id="logo"></div>
74
+ <h1>Error</h1>
75
+ </header>
76
+ <section id="content">
77
+ <nav>
78
+ <section id="description" lang="#{translation_entry}">
79
+ <h2><a href="<%= help_url %>"><%= CGI::escapeHTML(title) %></a></h2>
80
+ </section>
81
+ </nav>
82
+ <main lang="#{detail_lang}">
83
+ <p><%= detail %>
84
+ </main>
85
+ </section>
86
+ <!-- Made with ❤ by: https://delftsolutions.com -->
87
+ </body>
88
+ </html>
89
+ TEMPLATE
90
+ template.result(input.instance_eval { binding })
91
+ end
92
+
93
+ enable_wildcards
94
+
95
+ output_alias_optional 'text/html', view: :html
96
+
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,77 @@
1
+ =begin
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2019 Derk-Jan Karrenbeld
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ =end
24
+
25
+ require 'media_types/serialization/utils/header_list'
26
+
27
+ module MediaTypes
28
+ module Serialization
29
+ module Utils
30
+ class AcceptHeader < DelegateClass(Array)
31
+ def initialize(value)
32
+ __setobj__ HeaderList.new(value, entry_klazz: AcceptHeader::Entry)
33
+ end
34
+
35
+ class Entry
36
+ def initialize(media_type, index:, parameters:)
37
+ self.media_type = media_type
38
+ self.parameters = parameters
39
+ self.index = index
40
+
41
+ freeze
42
+ end
43
+
44
+ attr_reader :media_type
45
+
46
+ # noinspection RubyInstanceMethodNamingConvention
47
+ def q
48
+ parameters.fetch(:q) { 1.0 }.to_f
49
+ end
50
+
51
+ def <=>(other)
52
+ quality = other.q <=> q
53
+ return quality unless quality.zero?
54
+ index <=> other.send(:index)
55
+ end
56
+
57
+ def [](parameter)
58
+ parameters.fetch(String(parameter).to_sym)
59
+ end
60
+
61
+ def to_header
62
+ to_s
63
+ end
64
+
65
+ def to_s
66
+ [media_type].concat(parameters.map { |k, v| "#{k}=#{v}" }).compact.reject(&:empty?).join('; ')
67
+ end
68
+
69
+ private
70
+
71
+ attr_writer :media_type
72
+ attr_accessor :parameters, :index
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,82 @@
1
+ =begin
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2019 Derk-Jan Karrenbeld
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ =end
24
+
25
+ require 'media_types/serialization/utils/header_list'
26
+
27
+ module MediaTypes
28
+ module Serialization
29
+ module Utils
30
+ class AcceptLanguageHeader < DelegateClass(Array)
31
+ def initialize(value)
32
+ __setobj__ HeaderList.new(value, entry_klazz: Entry)
33
+ end
34
+
35
+ class Entry
36
+
37
+ DELIMITER = '-'
38
+
39
+ attr_reader :locale, :region, :language
40
+
41
+ def initialize(locale, index:, parameters:)
42
+ self.locale = locale
43
+ # TODO: support extlang correctly, maybe we don't even need this
44
+ self.language, self.region = locale.split(DELIMITER)
45
+ self.parameters = parameters
46
+ self.index = index
47
+
48
+ freeze
49
+ end
50
+
51
+ # noinspection RubyInstanceMethodNamingConvention
52
+ def q
53
+ parameters.fetch(:q) { 1.0 }.to_f
54
+ end
55
+
56
+ def <=>(other)
57
+ quality = other.q <=> q
58
+ return quality unless quality.zero?
59
+ index <=> other.send(:index)
60
+ end
61
+
62
+ def [](parameter)
63
+ parameters.fetch(String(parameter).to_sym)
64
+ end
65
+
66
+ def to_header
67
+ to_s
68
+ end
69
+
70
+ def to_s
71
+ [locale].concat(parameters.map { |k, v| "#{k}=#{v}" }).compact.reject(&:empty?).join('; ')
72
+ end
73
+
74
+ private
75
+
76
+ attr_writer :locale, :region, :language
77
+ attr_accessor :parameters, :index
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,89 @@
1
+ =begin
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2019 Derk-Jan Karrenbeld
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ =end
24
+
25
+ module MediaTypes
26
+ module Serialization
27
+ module Utils
28
+ ##
29
+ # @example Accept values
30
+ #
31
+ # class AcceptHeader < DelegateClass(Array)
32
+ # def initialize(value)
33
+ # super MediaTypes::Serialization::Utils::HeaderList.new(value, entry_klazz: AcceptHeader::Entry)
34
+ # end
35
+ #
36
+ # class Entry
37
+ # def initialize(media_type, index: parameters:)
38
+ # ...
39
+ # end
40
+ #
41
+ # def q
42
+ # parameters.fetch(:q) { 1.0 }.to_f
43
+ # end
44
+ #
45
+ # def <=>(other)
46
+ # quality = other.q <=> q
47
+ # return quality unless quality.zero?
48
+ # index <=> other.index
49
+ # end
50
+ # end
51
+ # end
52
+ #
53
+ # Accept.new(['*/*; q=0.1', 'application/json, text/html; q=0.8'])
54
+ # # => List['application/json', 'text/html', '*/*']
55
+ #
56
+ module HeaderList
57
+ HEADER_DELIMITER = ','
58
+ PARAMETER_DELIMITER = ';'
59
+
60
+ module_function
61
+
62
+ def parse(combined, entry_klazz:)
63
+ Array(combined).map { |line| line.split(HEADER_DELIMITER) }.flatten.each_with_index.map do |entry, index|
64
+ value, *parameters = entry.strip.split(PARAMETER_DELIMITER)
65
+ indexed_parameters = ::Hash[Array(parameters).map { |p| p.strip.split('=') }].transform_keys!(&:to_sym)
66
+ entry_klazz.new(value, index: index, parameters: indexed_parameters)
67
+ end
68
+ end
69
+
70
+ def new(combined, entry_klazz:)
71
+ result = parse(combined, entry_klazz: entry_klazz)
72
+ entry_klazz.instance_methods(false).include?(:<=>) ? result.sort! : result
73
+ end
74
+
75
+ def to_header(list)
76
+ # noinspection RubyBlockToMethodReference
77
+ list.map { |entry| stringify_entry(entry) }
78
+ .join("#{HEADER_DELIMITER} ")
79
+ end
80
+
81
+ def stringify_entry(entry)
82
+ return entry.to_header if entry.respond_to?(:to_header)
83
+ return entry.to_s if entry.respond_to?(:to_s)
84
+ entry.inspect
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -1,5 +1,5 @@
1
1
  module MediaTypes
2
2
  module Serialization
3
- VERSION = '0.8.0'
3
+ VERSION = '1.1.0'.freeze
4
4
  end
5
5
  end
@@ -1,50 +1,48 @@
1
-
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'media_types/serialization/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = 'media_types-serialization'
8
- spec.version = MediaTypes::Serialization::VERSION
9
- spec.authors = ['Derk-Jan Karrenbeld']
10
- spec.email = ['derk-jan@xpbytes.com']
11
-
12
- spec.summary = 'Add media types supported serialization using your favourite serializer'
13
- spec.homepage = 'https://github.com/XPBytes/media_types-serialization'
14
- spec.license = 'MIT'
15
-
16
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
- # to allow pushing to a single host or delete this section to allow pushing to any host.
18
- if spec.respond_to?(:metadata)
19
- # spec.metadata['allowed_push_host'] = 'TODO: Set to 'http://mygemserver.com''
20
-
21
- spec.metadata['homepage_uri'] = spec.homepage
22
- spec.metadata['source_code_uri'] = spec.homepage
23
- spec.metadata['changelog_uri'] = spec.homepage + '/CHANGELOG.md'
24
- else
25
- raise 'RubyGems 2.0 or newer is required to protect against ' \
26
- 'public gem pushes.'
27
- end
28
-
29
- # Specify which files should be added to the gem when it is released.
30
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
- end
34
- spec.bindir = 'exe'
35
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
- spec.require_paths = ['lib']
37
-
38
- spec.add_dependency 'actionpack', '>= 4.0.0'
39
- spec.add_dependency 'activesupport', '>= 4.0.0'
40
- spec.add_dependency 'media_types', '>= 0.6.2'
41
- spec.add_dependency 'oj', '>= 3.5.0'
42
- spec.add_dependency 'http_headers-accept', '>= 0.2.2', '< 1.0.0'
43
- spec.add_dependency 'http_headers-link', '< 1.0.0'
44
-
45
- spec.add_development_dependency 'awesome_print'
46
- spec.add_development_dependency 'bundler', '~> 2.0'
47
- spec.add_development_dependency 'rails', '~> 5.2'
48
- spec.add_development_dependency 'rake', '~> 10.0'
49
- spec.add_development_dependency 'minitest', '~> 5.0'
50
- end
1
+
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'media_types/serialization/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'media_types-serialization'
8
+ spec.version = MediaTypes::Serialization::VERSION
9
+ spec.authors = ['Derk-Jan Karrenbeld', 'Max Maton']
10
+ spec.email = ['derk-jan@xpbytes.com', 'max@delftsolutions.nl']
11
+
12
+ spec.summary = 'Add media types supported serialization using your favourite serializer'
13
+ spec.homepage = 'https://github.com/XPBytes/media_types-serialization'
14
+ spec.license = 'MIT'
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ # spec.metadata['allowed_push_host'] = 'TODO: Set to 'http://mygemserver.com''
20
+
21
+ spec.metadata['homepage_uri'] = spec.homepage
22
+ spec.metadata['source_code_uri'] = spec.homepage
23
+ spec.metadata['changelog_uri'] = spec.homepage + '/CHANGELOG.md'
24
+ else
25
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
26
+ 'public gem pushes.'
27
+ end
28
+
29
+ # Specify which files should be added to the gem when it is released.
30
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
+ end
34
+ spec.bindir = 'exe'
35
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ['lib']
37
+
38
+ spec.add_dependency 'actionpack', '>= 4.0.0'
39
+ spec.add_dependency 'activesupport', '>= 4.0.0'
40
+ spec.add_dependency 'media_types', '>= 2.0.0', '< 3.0.0'
41
+
42
+ spec.add_development_dependency 'awesome_print'
43
+ spec.add_development_dependency 'bundler'
44
+ spec.add_development_dependency 'rails', '~> 5.2'
45
+ spec.add_development_dependency 'rake', '~> 13.0'
46
+ spec.add_development_dependency 'minitest', '~> 5.0'
47
+ spec.add_development_dependency 'oj'
48
+ end