media_types-serialization 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|