media_types-serialization 0.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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/dictionaries/Derk_Jan.xml +7 -0
- data/.idea/inspectionProfiles/Project_Default.xml +6 -0
- data/.idea/media_types-serialization.iml +55 -0
- data/.idea/misc.xml +7 -0
- data/.idea/modules.xml +8 -0
- data/.idea/runConfigurations/test.xml +20 -0
- data/.idea/vcs.xml +6 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.txt +21 -0
- data/README.md +209 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/media_types/serialization.rb +194 -0
- data/lib/media_types/serialization/base.rb +138 -0
- data/lib/media_types/serialization/error.rb +7 -0
- data/lib/media_types/serialization/migrations_command.rb +38 -0
- data/lib/media_types/serialization/migrations_support.rb +41 -0
- data/lib/media_types/serialization/mime_type_support.rb +55 -0
- data/lib/media_types/serialization/no_content_type_given.rb +11 -0
- data/lib/media_types/serialization/no_media_type_serializers.rb +11 -0
- data/lib/media_types/serialization/no_serializer_for_content_type.rb +15 -0
- data/lib/media_types/serialization/renderer.rb +31 -0
- data/lib/media_types/serialization/renderer/register.rb +4 -0
- data/lib/media_types/serialization/version.rb +5 -0
- data/lib/media_types/serialization/wrapper.rb +15 -0
- data/lib/media_types/serialization/wrapper/html_wrapper.rb +42 -0
- data/lib/media_types/serialization/wrapper/media_collection_wrapper.rb +48 -0
- data/lib/media_types/serialization/wrapper/media_index_wrapper.rb +48 -0
- data/lib/media_types/serialization/wrapper/media_object_wrapper.rb +45 -0
- data/lib/media_types/serialization/wrapper/media_wrapper.rb +32 -0
- data/lib/media_types/serialization/wrapper/root_key.rb +20 -0
- data/media_types-serialization.gemspec +48 -0
- metadata +212 -0
@@ -0,0 +1,55 @@
|
|
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
|
+
mattr_accessor :media_type_constructable, :serializes_html_flag, :media_type_versions
|
12
|
+
end
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
def current_mime_type(view: nil)
|
16
|
+
media_type_constructable&.view(view)
|
17
|
+
end
|
18
|
+
|
19
|
+
def media_types(view: nil)
|
20
|
+
media_type_view = current_mime_type(view: view)
|
21
|
+
|
22
|
+
suffixes = [].tap do |result|
|
23
|
+
result << :json if self.instance_methods(false).include?(:to_json)
|
24
|
+
result << :xml if self.instance_methods(false).include?(:to_xml)
|
25
|
+
end
|
26
|
+
|
27
|
+
additionals = [].tap do |result|
|
28
|
+
result << 'text/html' if serializes_html_flag || self.instance_methods(false).include?(:to_html)
|
29
|
+
end
|
30
|
+
|
31
|
+
[media_type_view].concat(
|
32
|
+
media_type_versions.map { |version| media_type_view&.version(version) },
|
33
|
+
media_type_versions.flat_map do |version|
|
34
|
+
(suffixes).map { |suffix| media_type_view&.suffix(suffix)&.version(version) }
|
35
|
+
end,
|
36
|
+
additionals
|
37
|
+
).compact.uniq
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :media_type, :media_types
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def serializes_media_type(media_type, additional_versions: [])
|
45
|
+
self.media_type_constructable = media_type&.to_constructable
|
46
|
+
self.media_type_versions = additional_versions
|
47
|
+
end
|
48
|
+
|
49
|
+
def serializes_html
|
50
|
+
self.serializes_html_flag = true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,11 @@
|
|
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
|
@@ -0,0 +1,11 @@
|
|
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
|
@@ -0,0 +1,15 @@
|
|
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: given.inspect,
|
10
|
+
supported: supported.map(&:to_s)
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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
|
+
module MediaTypes
|
6
|
+
module Serialization
|
7
|
+
# noinspection RubyConstantNamingConvention
|
8
|
+
Renderer = lambda do |obj, options|
|
9
|
+
content_type = options[:content_type] || options[:mime_type]&.to_s || self.content_type&.to_s || obj.current_media_type.to_s
|
10
|
+
raise NoContentTypeGiven if content_type.blank?
|
11
|
+
|
12
|
+
self.content_type ||= content_type
|
13
|
+
|
14
|
+
if content_type.ends_with?('+json') || Mime::Type.lookup(content_type) == Mime[:json]
|
15
|
+
obj.class.instance_methods(false).include?(:to_json) ? obj.to_json(options) : obj.to_hash.to_json(options)
|
16
|
+
elsif content_type.ends_with?('+xml') || Mime::Type.lookup(content_type) == Mime[:xml]
|
17
|
+
obj.class.instance_methods(false).include?(:to_xml) ? obj.to_xml(options) : obj.to_hash.to_xml(options)
|
18
|
+
elsif Mime::Type.lookup(content_type) == Mime[:html]
|
19
|
+
obj.class.instance_methods(false).include?(:to_html) ? obj.to_html : obj.to_s
|
20
|
+
else
|
21
|
+
obj.to_body(content_type: options.delete(:content_type) || content_type, **options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module_function
|
26
|
+
|
27
|
+
def register_renderer
|
28
|
+
::ActionController::Renderers.add :media, &Renderer
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/serialization/wrapper/root_key'
|
4
|
+
require 'media_types/serialization/wrapper/html_wrapper'
|
5
|
+
require 'media_types/serialization/wrapper/media_wrapper'
|
6
|
+
require 'media_types/serialization/wrapper/media_object_wrapper'
|
7
|
+
require 'media_types/serialization/wrapper/media_index_wrapper'
|
8
|
+
require 'media_types/serialization/wrapper/media_collection_wrapper'
|
9
|
+
|
10
|
+
module MediaTypes
|
11
|
+
module Serialization
|
12
|
+
module Wrapper
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'action_controller'
|
6
|
+
|
7
|
+
require 'media_types/serialization/base'
|
8
|
+
require 'media_types/serialization/wrapper/root_key'
|
9
|
+
|
10
|
+
module MediaTypes
|
11
|
+
module Serialization
|
12
|
+
module Wrapper
|
13
|
+
class HtmlWrapper < DelegateClass(Base)
|
14
|
+
|
15
|
+
delegate :to_s, to: :to_html
|
16
|
+
delegate :class, to: :__getobj__
|
17
|
+
|
18
|
+
def initialize(serializer, view: nil, **render_options)
|
19
|
+
super serializer
|
20
|
+
self.view = view
|
21
|
+
self.render_options = render_options
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_html
|
25
|
+
return super if __getobj__.respond_to?(:to_html)
|
26
|
+
ActionController::Base.render(
|
27
|
+
'serializers/wrapper/html_wrapper',
|
28
|
+
assigns: {
|
29
|
+
serializer: self,
|
30
|
+
view: view,
|
31
|
+
**render_options
|
32
|
+
}
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_accessor :view, :render_options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
6
|
+
|
7
|
+
require 'media_types/serialization/base'
|
8
|
+
require 'media_types/serialization/wrapper/root_key'
|
9
|
+
|
10
|
+
module MediaTypes
|
11
|
+
module Serialization
|
12
|
+
module Wrapper
|
13
|
+
class MediaCollectionWrapper < DelegateClass(Base)
|
14
|
+
|
15
|
+
delegate :to_json, to: :to_hash
|
16
|
+
delegate :class, to: :__getobj__
|
17
|
+
|
18
|
+
def initialize(serializer)
|
19
|
+
super serializer
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
{ Wrapper::RootKey.new(__getobj__.class).pluralize => {
|
24
|
+
'_embedded': auto_wrap_serializable.map(&method(:item_hash)),
|
25
|
+
'_links': {}
|
26
|
+
} }
|
27
|
+
end
|
28
|
+
alias to_h to_hash
|
29
|
+
|
30
|
+
def collect_links
|
31
|
+
{}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def auto_wrap_serializable
|
37
|
+
Array(serializable)
|
38
|
+
end
|
39
|
+
|
40
|
+
def item_hash(item)
|
41
|
+
__getobj__.instance_exec do
|
42
|
+
set(item).send(:to_hash)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
6
|
+
|
7
|
+
require 'media_types/serialization/base'
|
8
|
+
require 'media_types/serialization/wrapper/root_key'
|
9
|
+
|
10
|
+
module MediaTypes
|
11
|
+
module Serialization
|
12
|
+
module Wrapper
|
13
|
+
class MediaIndexWrapper < DelegateClass(Base)
|
14
|
+
|
15
|
+
delegate :to_json, to: :to_hash
|
16
|
+
delegate :class, to: :__getobj__
|
17
|
+
|
18
|
+
def initialize(serializer)
|
19
|
+
super serializer
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
{ Wrapper::RootKey.new(__getobj__.class).pluralize => {
|
24
|
+
'_index': auto_wrap_serializable.map(&method(:item_hash)),
|
25
|
+
'_links': {}
|
26
|
+
} }
|
27
|
+
end
|
28
|
+
alias to_h to_hash
|
29
|
+
|
30
|
+
def collect_links
|
31
|
+
{}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def auto_wrap_serializable
|
37
|
+
Array(serializable)
|
38
|
+
end
|
39
|
+
|
40
|
+
def item_hash(item)
|
41
|
+
__getobj__.instance_exec do
|
42
|
+
set(item).send(:extract_self)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
6
|
+
|
7
|
+
require 'media_types/serialization/base'
|
8
|
+
require 'media_types/serialization/wrapper/media_wrapper'
|
9
|
+
require 'media_types/serialization/wrapper/root_key'
|
10
|
+
|
11
|
+
module MediaTypes
|
12
|
+
module Serialization
|
13
|
+
module Wrapper
|
14
|
+
class MediaObjectWrapper < DelegateClass(Base)
|
15
|
+
|
16
|
+
delegate :to_json, to: :to_hash
|
17
|
+
delegate :class, to: :__getobj__
|
18
|
+
|
19
|
+
def initialize(serializer)
|
20
|
+
super serializer
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_hash
|
24
|
+
unwrapped = auto_unwrap_serializable.tap { |u| set(u) }
|
25
|
+
{ RootKey.new(__getobj__.class).singularize => unwrapped && super || nil }
|
26
|
+
end
|
27
|
+
alias to_h to_hash
|
28
|
+
|
29
|
+
def collect_links
|
30
|
+
return {} unless serializable
|
31
|
+
__getobj__.send(:header_links)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
AUTO_UNWRAP_KLAZZES = [Array, defined?(ActiveRecord) ? ActiveRecord::Relation : nil].compact.freeze
|
37
|
+
|
38
|
+
def auto_unwrap_serializable
|
39
|
+
return serializable unless AUTO_UNWRAP_KLAZZES.any? { |klazz| serializable.is_a?(klazz) }
|
40
|
+
serializable.first
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types'
|
4
|
+
|
5
|
+
require 'media_types/serialization/wrapper/media_index_wrapper'
|
6
|
+
require 'media_types/serialization/wrapper/media_collection_wrapper'
|
7
|
+
require 'media_types/serialization/wrapper/media_object_wrapper'
|
8
|
+
|
9
|
+
module MediaTypes
|
10
|
+
module Serialization
|
11
|
+
module Wrapper
|
12
|
+
class MediaWrapper
|
13
|
+
|
14
|
+
AUTO_WRAPPER_MAPPING = {
|
15
|
+
::MediaTypes::INDEX_VIEW => MediaIndexWrapper,
|
16
|
+
::MediaTypes::COLLECTION_VIEW => MediaCollectionWrapper,
|
17
|
+
::MediaTypes::CREATE_VIEW => MediaObjectWrapper,
|
18
|
+
nil => MediaObjectWrapper
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
DEFAULT_WRAPPER = MediaObjectWrapper
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def new(serializer, view: nil)
|
25
|
+
wrapper = AUTO_WRAPPER_MAPPING.fetch(String(view)) { DEFAULT_WRAPPER }
|
26
|
+
wrapper.new(serializer)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'active_support/core_ext/object/blank'
|
6
|
+
require 'active_support/core_ext/string/inflections'
|
7
|
+
|
8
|
+
module MediaTypes
|
9
|
+
module Serialization
|
10
|
+
module Wrapper
|
11
|
+
class RootKey < DelegateClass(String)
|
12
|
+
def initialize(klazz)
|
13
|
+
base = klazz.name.demodulize.chomp(MediaTypes::Serialization.common_suffix || 'Serializer').presence ||
|
14
|
+
klazz.parent.name.demodulize
|
15
|
+
super base.underscore
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +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.0'
|
41
|
+
spec.add_dependency 'oj', '>= 3.5.0'
|
42
|
+
spec.add_dependency 'http_headers-accept', '< 1.0.0'
|
43
|
+
spec.add_dependency 'http_headers-link', '< 1.0.0'
|
44
|
+
|
45
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
46
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
47
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
48
|
+
end
|