media_types-serialization 0.8.1 → 1.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +10 -1
  3. data/.gitignore +12 -12
  4. data/.idea/.rakeTasks +5 -5
  5. data/.idea/inspectionProfiles/Project_Default.xml +5 -5
  6. data/.idea/runConfigurations/test.xml +19 -19
  7. data/CHANGELOG.md +18 -0
  8. data/CODE_OF_CONDUCT.md +74 -74
  9. data/Gemfile +4 -4
  10. data/Gemfile.lock +58 -61
  11. data/LICENSE.txt +21 -21
  12. data/README.md +640 -173
  13. data/Rakefile +10 -10
  14. data/bin/console +14 -14
  15. data/bin/setup +8 -8
  16. data/lib/media_types/problem.rb +64 -0
  17. data/lib/media_types/serialization.rb +431 -172
  18. data/lib/media_types/serialization/base.rb +111 -91
  19. data/lib/media_types/serialization/error.rb +178 -0
  20. data/lib/media_types/serialization/fake_validator.rb +52 -0
  21. data/lib/media_types/serialization/serialization_dsl.rb +117 -0
  22. data/lib/media_types/serialization/serialization_registration.rb +235 -0
  23. data/lib/media_types/serialization/serializers/api_viewer.rb +133 -0
  24. data/lib/media_types/serialization/serializers/common_css.rb +168 -0
  25. data/lib/media_types/serialization/serializers/endpoint_description_serializer.rb +80 -0
  26. data/lib/media_types/serialization/serializers/fallback_not_acceptable_serializer.rb +85 -0
  27. data/lib/media_types/serialization/serializers/fallback_unsupported_media_type_serializer.rb +58 -0
  28. data/lib/media_types/serialization/serializers/input_validation_error_serializer.rb +89 -0
  29. data/lib/media_types/serialization/serializers/problem_serializer.rb +87 -0
  30. data/lib/media_types/serialization/version.rb +1 -1
  31. data/media_types-serialization.gemspec +50 -50
  32. metadata +40 -43
  33. data/.travis.yml +0 -17
  34. data/lib/generators/media_types/serialization/api_viewer/api_viewer_generator.rb +0 -25
  35. data/lib/generators/media_types/serialization/api_viewer/templates/api_viewer.html.erb +0 -98
  36. data/lib/generators/media_types/serialization/api_viewer/templates/initializer.rb +0 -33
  37. data/lib/generators/media_types/serialization/api_viewer/templates/template_controller.rb +0 -23
  38. data/lib/media_types/serialization/media_type/register.rb +0 -4
  39. data/lib/media_types/serialization/migrations_command.rb +0 -38
  40. data/lib/media_types/serialization/migrations_support.rb +0 -50
  41. data/lib/media_types/serialization/mime_type_support.rb +0 -64
  42. data/lib/media_types/serialization/no_content_type_given.rb +0 -11
  43. data/lib/media_types/serialization/no_media_type_serializers.rb +0 -11
  44. data/lib/media_types/serialization/no_serializer_for_content_type.rb +0 -15
  45. data/lib/media_types/serialization/renderer.rb +0 -41
  46. data/lib/media_types/serialization/renderer/register.rb +0 -4
  47. data/lib/media_types/serialization/wrapper.rb +0 -13
  48. data/lib/media_types/serialization/wrapper/html_wrapper.rb +0 -45
  49. data/lib/media_types/serialization/wrapper/media_collection_wrapper.rb +0 -61
  50. data/lib/media_types/serialization/wrapper/media_index_wrapper.rb +0 -61
  51. data/lib/media_types/serialization/wrapper/media_object_wrapper.rb +0 -55
  52. data/lib/media_types/serialization/wrapper_support.rb +0 -38
@@ -1,33 +0,0 @@
1
- require 'media_types/serialization'
2
-
3
- # This registers the renderer as side-effect
4
- require 'media_types/serialization/renderer/register'
5
-
6
- # This registers the media type as side-effect
7
- require 'media_types/serialization/media_type/register'
8
-
9
- ##
10
- # The following options are breaking and therefore disabled by default.
11
- #
12
- # When these are true, the +header_links+ and +extract_links+ methods is called
13
- # when dealing with a .collection or .index view, respectively. It allows you
14
- # to define +_links+ for the root level from your serializer.
15
- #
16
- #
17
- MediaTypes::Serialization.collect_links_for_collection = true
18
- MediaTypes::Serialization.collect_links_for_index = true
19
-
20
- ##
21
- # The API Viewer template is provided if you used the generator. You can change
22
- # the view it renders by changing the path below.
23
- #
24
- #
25
- # ::MediaTypes::Serialization.api_viewer_layout = '/path/to/wrapper/layout'
26
-
27
- ##
28
- # When .to_html is not provided by a serializer, it will fall back to render
29
- # the API Viewer, but this template can be changed by changing the path
30
- # below.
31
- #
32
- #
33
- # ::MediaTypes::Serialization.html_wrapper_layout = '/path/to/wrapper/layout'
@@ -1,23 +0,0 @@
1
- module Api
2
- class TemplateController < ApiController
3
- def create
4
- href = params[:template].delete(:href)
5
- templated_values = params[:template].permit!.to_h
6
-
7
- response["Location"] = templated_values.reduce(href) do |result, (key, value)|
8
- result.sub!(%r{:#{key}|{#{key}}|%7B#{key}%7D}, value) || invalid_parameter(key, href)
9
- end
10
- head :temporary_redirect
11
- end
12
-
13
- private
14
-
15
- def invalid_parameter(key, href)
16
- raise ActionController::BadRequest, format(
17
- 'Received templated value for "%<key>s" which does not exist in templated link "%<href>s"',
18
- key: key,
19
- href: href.gsub('%7B', '{').gsub('%7D', '}')
20
- )
21
- end
22
- end
23
- end
@@ -1,4 +0,0 @@
1
- require 'action_dispatch/http/mime_type'
2
- require 'media_types/serialization'
3
-
4
- Mime::Type.register(MediaTypes::Serialization::MEDIA_TYPE_API_VIEWER, :api_viewer)
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- module MediaTypes
4
- module Serialization
5
- class MigrationsCommand
6
- def initialize(serializer)
7
- self.serializer = serializer
8
- self.migrations = {}
9
- end
10
-
11
- def call(result, mime_type, view)
12
- return result if mime_type.is_a?(String) || matches_current_mime_type?(view: view, mime_type: mime_type)
13
-
14
- migrations.reduce(result) do |migrated, (version, migration)|
15
- migrated = migration.call(migrated)
16
- next migrated unless matches_mime_type?(mime_type.version(version), mime_type)
17
- break migrated
18
- end
19
- end
20
-
21
- private
22
-
23
- attr_accessor :serializer, :migrations
24
-
25
- def version(version, &block)
26
- migrations[version] = ->(result) { serializer.instance_exec(result, &block) }
27
- end
28
-
29
- def matches_current_mime_type?(view:, mime_type:)
30
- serializer.class.current_mime_type(view: view) == mime_type.to_s
31
- end
32
-
33
- def matches_mime_type?(left, right)
34
- left.to_s == right.to_s
35
- end
36
- end
37
- end
38
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require 'media_types/serialization/migrations_command'
5
-
6
- module MediaTypes
7
- module Serialization
8
- module MigrationsSupport
9
- extend ActiveSupport::Concern
10
-
11
- included do
12
- # This is the same as doing matrr_accessor but have it isolated to the class. Subclass changes to change these
13
- # values, but this allows for definition as a concern.
14
-
15
- class << self
16
- attr_accessor :migrations
17
- end
18
-
19
- delegate :migrations,
20
- :migrations=,
21
- to: :class
22
- end
23
-
24
- class_methods do
25
- def migrator(serializer)
26
- return nil unless migrations
27
- migrations.call(serializer)
28
- end
29
-
30
- protected
31
-
32
- def backward_migrations(&block)
33
- self.migrations = lambda do |serializer|
34
- MigrationsCommand.new(serializer).tap do |callable|
35
- callable.instance_exec(&block)
36
- end
37
- end
38
- end
39
- end
40
-
41
- def migrate(result = nil, media_type = current_media_type, view = current_view)
42
- result ||= yield
43
-
44
- migrator = self.class.migrator(self)
45
- return result unless migrator
46
- migrator.call(result, media_type, view)
47
- end
48
- end
49
- end
50
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
-
5
- module MediaTypes
6
- module Serialization
7
- module MimeTypeSupport
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- # This is the same as doing matrr_accessor but have it isolated to the class. Subclass changes to change these
12
- # values, but this allows for definition as a concern.
13
-
14
- class << self
15
- attr_accessor :media_type_constructable, :serializes_html_flag, :media_type_versions
16
- end
17
-
18
- delegate :media_type_constructable, :serializes_html_flag, :media_type_versions,
19
- :media_type_constructable=, :serializes_html_flag=, :media_type_versions=,
20
- to: :class
21
- end
22
-
23
- class_methods do
24
- def current_mime_type(view: nil)
25
- media_type_constructable&.view(view)
26
- end
27
-
28
- def media_types(view: nil)
29
- media_type_view = current_mime_type(view: view)
30
-
31
- suffixes = [].tap do |result|
32
- result << :json if instance_methods.include?(:to_json)
33
- result << :xml if instance_methods.include?(:to_xml)
34
- end
35
-
36
- additionals = [].tap do |result|
37
- result << 'text/html' if serializes_html_flag || instance_methods.include?(:to_html)
38
- end
39
-
40
- [media_type_view].concat(
41
- media_type_versions.map { |version| media_type_view&.version(version) },
42
- media_type_versions.flat_map do |version|
43
- (suffixes).map { |suffix| media_type_view&.suffix(suffix)&.version(version) }
44
- end,
45
- additionals
46
- ).compact.uniq
47
- end
48
-
49
- alias_method :media_type, :media_types
50
-
51
- protected
52
-
53
- def serializes_media_type(media_type, additional_versions: [])
54
- self.media_type_constructable = media_type&.to_constructable
55
- self.media_type_versions = additional_versions
56
- end
57
-
58
- def serializes_html
59
- self.serializes_html_flag = true
60
- end
61
- end
62
- end
63
- end
64
- end
@@ -1,11 +0,0 @@
1
- require 'media_types/serialization/error'
2
-
3
- module MediaTypes
4
- module Serialization
5
- class NoContentTypeGiven < Error
6
- def initialize
7
- super 'Unable to render data because :content_type was not passed in to "render media: data, content_type: ..."'
8
- end
9
- end
10
- end
11
- end
@@ -1,11 +0,0 @@
1
- require 'media_types/serialization/error'
2
-
3
- module MediaTypes
4
- module Serialization
5
- class NoMediaTypeSerializers < Error
6
- def initialize
7
- super 'No serializer has been set up for this request. You can not fix this by changing the request headers.'
8
- end
9
- end
10
- end
11
- end
@@ -1,15 +0,0 @@
1
- require 'media_types/serialization/error'
2
-
3
- module MediaTypes
4
- module Serialization
5
- class NoSerializerForContentType < Error
6
- def initialize(given, supported)
7
- super format(
8
- 'Unable to serialize to requested Content-Type: %<given>s. I can give you: %<supported>s',
9
- given: Array(given).map(&:to_s).inspect,
10
- supported: supported.map(&:to_s)
11
- )
12
- end
13
- end
14
- end
15
- end
@@ -1,41 +0,0 @@
1
- require 'media_types/serialization/no_content_type_given'
2
- require 'active_support/core_ext/object/blank'
3
- require 'active_support/core_ext/hash/conversions'
4
-
5
- require 'media_types/serialization'
6
-
7
- module MediaTypes
8
- module Serialization
9
- # noinspection RubyConstantNamingConvention
10
- Renderer = lambda do |obj, options|
11
- content_type = options[:content_type] ||
12
- options[:mime_type]&.to_s ||
13
- self.content_type&.to_s ||
14
- obj.current_media_type.to_s
15
-
16
- raise NoContentTypeGiven if content_type.blank?
17
-
18
- self.content_type ||= content_type
19
-
20
- if content_type.ends_with?('+json') || Mime::Type.lookup(content_type) == Mime[:json]
21
- obj.respond_to?(:to_json) ? obj.to_json(options) : obj.to_hash.to_json(options)
22
- elsif content_type.ends_with?('+xml') || Mime::Type.lookup(content_type) == Mime[:xml]
23
- obj.respond_to?(:to_xml) ? obj.to_xml(options) : obj.to_hash.to_xml(options)
24
- elsif Mime::Type.lookup(content_type) == Mime[:html] && obj.respond_to?(:to_html)
25
- obj.to_html
26
- elsif content_type === MEDIA_TYPE_API_VIEWER && obj.respond_to?(:to_api_viewer)
27
- obj.to_api_viewer(content_type: options[:api_viewer_content_type]).tap do
28
- self.content_type = 'text/html' # because the api viewer will not be seen as
29
- end
30
- else
31
- obj.to_body(content_type: options.delete(:content_type) || content_type, **options)
32
- end
33
- end
34
-
35
- module_function
36
-
37
- def register_renderer
38
- ::ActionController::Renderers.add :media, &Renderer
39
- end
40
- end
41
- end
@@ -1,4 +0,0 @@
1
- require 'action_controller/metal/renderers'
2
- require 'media_types/serialization/renderer'
3
-
4
- ::MediaTypes::Serialization.register_renderer
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'media_types/serialization/wrapper/html_wrapper'
4
- require 'media_types/serialization/wrapper/media_object_wrapper'
5
- require 'media_types/serialization/wrapper/media_index_wrapper'
6
- require 'media_types/serialization/wrapper/media_collection_wrapper'
7
-
8
- module MediaTypes
9
- module Serialization
10
- module Wrapper
11
- end
12
- end
13
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'delegate'
4
-
5
- require 'action_controller'
6
-
7
- module MediaTypes
8
- module Serialization
9
- module Wrapper
10
- class HtmlWrapper < SimpleDelegator
11
-
12
- delegate :to_s, to: :to_html
13
- delegate :class, to: :__getobj__
14
-
15
- def initialize(serializer, view: nil, **render_options)
16
- __setobj__ serializer
17
-
18
- self.view = view
19
- self.render_options = render_options
20
- end
21
-
22
- def to_html
23
- return super if __getobj__.respond_to?(:to_html)
24
- to_api_viewer(layout: ::MediaTypes::Serialization.html_wrapper_layout)
25
- end
26
-
27
- def to_api_viewer(content_type: nil, layout: ::MediaTypes::Serialization.api_viewer_layout)
28
- ActionController::Base.render(
29
- layout || 'serializers/wrapper/html_wrapper',
30
- assigns: {
31
- serializer: self,
32
- view: view,
33
- content_type: content_type,
34
- **render_options
35
- }
36
- )
37
- end
38
-
39
- private
40
-
41
- attr_accessor :view, :render_options
42
- end
43
- end
44
- end
45
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'delegate'
4
-
5
- require 'active_support/core_ext/string/inflections'
6
-
7
- module MediaTypes
8
- module Serialization
9
- module Wrapper
10
- class MediaCollectionWrapper < SimpleDelegator
11
-
12
- delegate :to_json, to: :to_hash
13
- delegate :class, :set, :current_view, :current_media_type, to: :__getobj__
14
- attr_accessor :inner_serializer
15
-
16
- def initialize(serializer)
17
- self.inner_serializer = serializer.dup
18
- __setobj__ serializer
19
- end
20
-
21
- def to_hash
22
- {
23
- root_key => {
24
- '_embedded': wrapped_serializable.map(&method(:item_hash)),
25
- '_links': extract_links
26
- }
27
- }
28
- end
29
- alias to_h to_hash
30
-
31
- def header_links(view: current_view)
32
- return __getobj__.send(:header_links, view: view) if ::MediaTypes::Serialization.collect_links_for_collection
33
- {}
34
- end
35
-
36
- protected
37
-
38
- def extract_links(view: current_view)
39
- return __getobj__.send(:extract_set_links, view: view) if ::MediaTypes::Serialization.collect_links_for_collection
40
- {}
41
- end
42
-
43
- def wrapped_serializable
44
- return __getobj__.wrapped_serializable if __getobj__.respond_to?(:wrapped_serializable)
45
- return [serializable] if serializable.is_a?(::Hash)
46
- Array(serializable)
47
- end
48
-
49
- def item_hash(item)
50
- inner_serializer.instance_exec do
51
- set(item).to_hash
52
- end
53
- end
54
-
55
- def root_key(view: current_view)
56
- __getobj__.class.root_key(view: view)
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'delegate'
4
-
5
- require 'active_support/core_ext/string/inflections'
6
-
7
- module MediaTypes
8
- module Serialization
9
- module Wrapper
10
- class MediaIndexWrapper < SimpleDelegator
11
-
12
- delegate :to_json, to: :to_hash
13
- delegate :class, :set, :current_view, :current_media_type, to: :__getobj__
14
- attr_accessor :inner_serializer
15
-
16
- def initialize(serializer)
17
- self.inner_serializer = serializer.dup
18
- __setobj__ serializer
19
- end
20
-
21
- def to_hash
22
- {
23
- root_key => {
24
- '_index': wrapped_serializable.map(&method(:item_hash)),
25
- '_links': extract_links
26
- }
27
- }
28
- end
29
- alias to_h to_hash
30
-
31
- def header_links(view: current_view)
32
- return __getobj__.send(:header_links, view: view) if ::MediaTypes::Serialization.collect_links_for_index
33
- {}
34
- end
35
-
36
- protected
37
-
38
- def wrapped_serializable
39
- return __getobj__.wrapped_serializable if __getobj__.respond_to?(:wrapped_serializable)
40
- return [serializable] if serializable.is_a?(::Hash)
41
- Array(serializable)
42
- end
43
-
44
- def item_hash(item)
45
- inner_serializer.instance_exec do
46
- set(item).send(:extract_self)
47
- end
48
- end
49
-
50
- def extract_links(view: current_view)
51
- return __getobj__.send(:extract_set_links, view: view) if ::MediaTypes::Serialization.collect_links_for_index
52
- {}
53
- end
54
-
55
- def root_key(view: current_view)
56
- __getobj__.class.root_key(view: view)
57
- end
58
- end
59
- end
60
- end
61
- end