hal_presenter 0.4.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9322632b515fd679f3bde7bcd8f0c3725330a66f3e4d8439f3223c3bc70f2e4e
4
+ data.tar.gz: 56e87ad4268c7bf247d9bf593fb0c6c91358a2d0d61a39824085075c4cbd9944
5
+ SHA512:
6
+ metadata.gz: 7c1663fc6ba1427c4a30162892dd3ccc4ae551b299990657d3cbac06566d32483112a77527baab2c05316c57bc2ac65e3b5cc4ee91ddfb8317fd8b0afaf4da51
7
+ data.tar.gz: f99e735e37406ec5c7e370e9cc08fa74babbd135c6ceb60160c980e5a132c8c70d1412c7e0b585c868f6eece599bd2e88220ef2be26e8671e08b5236472c3200
checksums.yaml.gz.sig ADDED
@@ -0,0 +1 @@
1
+ ��?t�=����rF5���=�<��cя�H�s1U��ϯ���Kcb��qy����ⵃ�Y����п��Me�G'U��;W�I������yBi�:&��y��ݠc�v� p��5�Ɗ���'scL��/ś��]���Y�ᓵ/ҭ��S�Z�L���� �&�)����1��d �2N��`y{�<)u��n�E*��n�C��lP�!��`��n�EԲ�;��8=����qΡ$�OB�fW��e �x�
data.tar.gz.sig ADDED
Binary file
@@ -0,0 +1,33 @@
1
+ require 'hal_presenter/model'
2
+ require 'hal_presenter/policy'
3
+ require 'hal_presenter/policy/dsl'
4
+ require 'hal_presenter/attributes'
5
+ require 'hal_presenter/links'
6
+ require 'hal_presenter/embedded'
7
+ require 'hal_presenter/curies'
8
+ require 'hal_presenter/serializer'
9
+ require 'hal_presenter/deserializer'
10
+ require 'hal_presenter/collection'
11
+ require 'hal_presenter/serialize_hooks'
12
+
13
+ module HALPresenter
14
+ include HALPresenter::Attributes
15
+ include HALPresenter::Links
16
+ include HALPresenter::Curies
17
+ include HALPresenter::Embedded
18
+ include HALPresenter::Collection
19
+ include HALPresenter::SerializeHooks
20
+ include HALPresenter::Model
21
+ include HALPresenter::Serializer
22
+ include HALPresenter::Deserializer
23
+ include HALPresenter::Policy
24
+ end
25
+
26
+ # Keeping this module for backward compatibility!
27
+ module HALDecorator
28
+ include HALPresenter
29
+
30
+ def self.method_missing(m, *args, &block)
31
+ HALPresenter.send(m, *args, &block)
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ require 'hal_presenter/property'
2
+
3
+ module HALPresenter
4
+ module Attributes
5
+ def attribute(*args, &block)
6
+ @_attributes ||= init_attributes
7
+ @_attributes = @_attributes.reject { |attr| attr.name == args.first }
8
+ @_attributes << Property.new(*args, &block)
9
+ end
10
+
11
+ protected
12
+
13
+ def attributes
14
+ @_attributes ||= init_attributes
15
+ end
16
+
17
+ private
18
+
19
+ def init_attributes
20
+ return [] unless is_a? Class
21
+ return [] unless superclass.respond_to?(:attributes, true)
22
+ superclass.attributes.each do |attr|
23
+ attr.change_scope(self)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ require 'hal_presenter/property'
2
+
3
+ module HALPresenter
4
+ module Collection
5
+
6
+ class CollectionParameters
7
+ include Attributes
8
+ include Links
9
+ include Curies
10
+
11
+ attr_reader :name
12
+
13
+ def initialize(name, &block)
14
+ @name = name
15
+ instance_exec(&block) if block_given?
16
+ end
17
+ end
18
+
19
+ def collection(of:, &block)
20
+ @_parameters = CollectionParameters.new(of, &block)
21
+ end
22
+
23
+ protected
24
+
25
+ def collection_parameters
26
+ @_parameters ||= init_collection_params
27
+ end
28
+
29
+ def can_serialize_collection?
30
+ !collection_parameters.nil?
31
+ end
32
+
33
+ private
34
+
35
+ def init_collection_params
36
+ return unless is_a? Class
37
+ if superclass.respond_to?(:collection_parameters, true)
38
+ superclass.collection_parameters
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,30 @@
1
+ require 'hal_presenter/property'
2
+
3
+ module HALPresenter
4
+ module Curies
5
+ def curie(rel, value = nil, &block)
6
+ if value.nil? && !block_given?
7
+ raise 'curie must be called with non nil value or be given a block'
8
+ end
9
+ @_curies ||= init_curies
10
+ @_curies = @_curies.reject { |curie| curie.name == rel }
11
+ @_curies << Property.new(rel, value, &block)
12
+ end
13
+
14
+ protected
15
+
16
+ def curies
17
+ @_curies ||= init_curies
18
+ end
19
+
20
+ private
21
+
22
+ def init_curies
23
+ return [] unless is_a? Class
24
+ return [] unless superclass.respond_to?(:curies, true)
25
+ superclass.curies.each do |curie|
26
+ curie.change_scope(self)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,91 @@
1
+ require 'json'
2
+
3
+ module HALPresenter
4
+
5
+ def self.from_hal(presenter, payload, resource = nil)
6
+ presenter.from_hal(payload, resource)
7
+ end
8
+
9
+ module Deserializer
10
+
11
+ class Error < StandardError; end
12
+
13
+ def from_hal(payload, resource = nil)
14
+ return if payload.nil? || payload.empty?
15
+ hash = JSON.parse(payload)
16
+ from_hash(hash, resource)
17
+ end
18
+
19
+ protected
20
+
21
+ def from_hash(hash, resource)
22
+ as_collection = deserialize_as_collection?(hash)
23
+
24
+ if resource.nil?
25
+ model = HALPresenter.lookup_model self
26
+ raise Error, "No model for #{self.class}" unless model
27
+ resource = as_collection ? [] : model.new
28
+ elsif as_collection
29
+ resource.clear
30
+ end
31
+
32
+ if as_collection
33
+ deserialize_collection(hash, resource)
34
+ else
35
+ deserialize_attributes(hash, resource)
36
+ deserialize_embedded(hash, resource)
37
+ end
38
+ resource
39
+ end
40
+
41
+ def deserialize_attributes(hash, resource)
42
+ attributes.each do |attribute|
43
+ setter_method = setter_method_name(attribute.name)
44
+ next unless resource.respond_to? setter_method
45
+ resource.public_send(setter_method, hash[attribute.name.to_s])
46
+ end
47
+ end
48
+
49
+ def deserialize_embedded(hash, resource)
50
+ embedded.each do |embed|
51
+ setter_method = setter_method_name(embed.name) or next
52
+ next unless resource.respond_to? setter_method
53
+ presenter = embed.presenter_class or next
54
+
55
+ embedded_hash = hash.dig('_embedded', embed.name.to_s)
56
+ next unless embedded_hash&.any?
57
+
58
+ embedded_resource = resource.public_send(embed.name)
59
+ embedded_resource =
60
+ if embedded_hash.is_a? Array
61
+ embedded_hash.map { |h| presenter.from_hash(h, embedded_resource) }
62
+ else
63
+ presenter.from_hash(embedded_hash, embedded_resource)
64
+ end
65
+ resource.public_send(setter_method, embedded_resource)
66
+ end
67
+ end
68
+
69
+ def deserialize_collection(hash, resource)
70
+ hash['_embedded'][collection_parameters.name].each do |resource_hash|
71
+ resource << from_hash(resource_hash, nil)
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def deserialize_as_collection?(hash)
78
+ name = collection_parameters&.name
79
+ # return true/false (Hash#key? returns nil if not found..)
80
+ name && hash['_embedded']&.key?(name) || false
81
+ end
82
+
83
+ def setter_method_name(attr)
84
+ "#{attr}=".to_sym
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+
91
+
@@ -0,0 +1,36 @@
1
+ require 'hal_presenter/property'
2
+
3
+ module HALPresenter
4
+ module Embedded
5
+ class Embed < HALPresenter::Property
6
+ attr_reader :presenter_class
7
+
8
+ def initialize(name, value = nil, presenter_class: nil, &block)
9
+ super(name, value, &block)
10
+ @presenter_class = presenter_class
11
+ end
12
+ end
13
+
14
+ def embed(*args, &block)
15
+ @_embedded ||= init_embedded
16
+ @_embedded = @_embedded.reject { |embed| embed.name == args.first }
17
+ @_embedded << Embed.new(*args, &block)
18
+ end
19
+
20
+ protected
21
+
22
+ def embedded
23
+ @_embedded ||= init_embedded
24
+ end
25
+
26
+ private
27
+
28
+ def init_embedded
29
+ return [] unless is_a? Class
30
+ return [] unless superclass.respond_to?(:embedded, true)
31
+ superclass.embedded.each do |embed|
32
+ embed.change_scope(self)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,54 @@
1
+ require 'hal_presenter/property'
2
+
3
+ module HALPresenter
4
+
5
+ def self.base_href=(base)
6
+ @base_href = base&.sub(%r(/*$), '')
7
+ end
8
+
9
+ def self.href(href)
10
+ return href if (@base_href ||= '').empty?
11
+ return href if href =~ %r(\A(\w+://)?[^/])
12
+ @base_href + href
13
+ end
14
+
15
+ module Links
16
+
17
+ class Link < HALPresenter::Property
18
+ attr_reader :http_method
19
+ def initialize(rel, value = nil, http_method: nil, &block)
20
+ if value.nil? && !block_given?
21
+ raise 'link must be called with non nil value or be given a block'
22
+ end
23
+ @http_method = http_method
24
+ super(rel, value, &block)
25
+ end
26
+
27
+ def rel
28
+ name
29
+ end
30
+ end
31
+
32
+ def link(rel, value = nil, method: nil, methods: nil, &block)
33
+ @_links ||= init_links
34
+ @_links = @_links.reject { |link| link.rel == rel }
35
+ @_links << Link.new(rel, value, http_method: method || methods, &block)
36
+ end
37
+
38
+ protected
39
+
40
+ def links
41
+ @_links ||= init_links
42
+ end
43
+
44
+ private
45
+
46
+ def init_links
47
+ return [] unless is_a? Class
48
+ return [] unless superclass.respond_to?(:links, true)
49
+ superclass.links.each do |link|
50
+ link.change_scope(self)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ module HALPresenter
2
+ @presenters = {}
3
+
4
+ def HALPresenter.register(model:, presenter:)
5
+ @presenters[presenter] = model
6
+ end
7
+
8
+ def HALPresenter.unregister(presenter)
9
+ @presenters.delete_if { |d,_| d == presenter }
10
+ end
11
+
12
+ def HALPresenter.lookup_model(presenter)
13
+ @presenters[presenter]
14
+ end
15
+
16
+ def HALPresenter.lookup_presenter(model)
17
+ clazz = model.is_a?(Class) ? model : model.class
18
+ presenters = @presenters.select { |d, m| m == clazz }.keys.compact
19
+ presenters.empty? ? nil : presenters
20
+ end
21
+
22
+ module Model
23
+ def model(clazz)
24
+ HALPresenter.register(model: clazz, presenter: self)
25
+ end
26
+
27
+ def inherited(subclass)
28
+ if model = HALPresenter.lookup_model(self)
29
+ HALPresenter.register(model: model, presenter: subclass)
30
+ end
31
+ super
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,126 @@
1
+ module HALPresenter
2
+ class << self
3
+ attr_accessor :paginate
4
+ end
5
+
6
+ # TODO: Support Kaminari and Will_paginate
7
+
8
+ class Pagination
9
+
10
+ class Uri
11
+ def self.parse(str)
12
+ uri = nil
13
+ query = nil
14
+ unless str.nil? || str.empty?
15
+ if m = str.match(%r(\A([^\?]+)\??([^\#]+)?.*\Z))
16
+ uri = m[1]
17
+ query = query_from_string m[2]
18
+ end
19
+ end
20
+ new(uri, query)
21
+ end
22
+
23
+ def self.query_from_string(str)
24
+ return {} if str.nil? || str.empty?
25
+ str.split('&').each_with_object({}) do |pair, q|
26
+ key_value = pair.split('=')
27
+ q[key_value[0]] = key_value[1];
28
+ end
29
+ end
30
+
31
+ def initialize(uri, query)
32
+ @uri = uri
33
+ @query = query
34
+ end
35
+
36
+ def +(query)
37
+ self.class.new(@uri, @query.merge(query))
38
+ end
39
+
40
+ def to_s
41
+ return if @uri.nil?
42
+ @uri.dup.tap do |uri|
43
+ next if @query.nil? || @query.empty?
44
+ uri << "?" + @query.map { |k,v| "#{k}=#{v}" }.join('&')
45
+ end
46
+ end
47
+ end
48
+
49
+ def self.paginate!(serialized, collection)
50
+ new(serialized, collection).call
51
+ end
52
+
53
+ def initialize(serialized, collection)
54
+ @serialized = serialized
55
+ @collection = collection
56
+ @self_uri = Uri.parse serialized.dig(:_links, :self, :href)
57
+ end
58
+
59
+ def call
60
+ return unless should_paginate?
61
+ add_query_to_self
62
+ add_prev_link
63
+ add_next_link
64
+ end
65
+
66
+ private
67
+
68
+ attr_accessor :serialized, :collection, :self_uri
69
+
70
+ def should_paginate?
71
+ self_uri && current_page
72
+ end
73
+
74
+ def query(page)
75
+ return {} unless page
76
+ {
77
+ page: page,
78
+ per_page: per_page,
79
+ }
80
+ end
81
+
82
+ def add_query_to_self
83
+ serialized[:_links][:self][:href] = (self_uri + query(current_page)).to_s
84
+ end
85
+
86
+ def add_prev_link
87
+ return if serialized[:_links][:prev] || !prev_page
88
+ serialized[:_links][:prev] = {
89
+ href: (self_uri + query(prev_page)).to_s
90
+ }
91
+ end
92
+
93
+ def add_next_link
94
+ return if serialized[:_links][:next] || !next_page
95
+ serialized[:_links][:next] = {
96
+ href: (self_uri + query(next_page)).to_s
97
+ }
98
+ end
99
+
100
+ # Supported by Sequel, Kaminari and will_paginate
101
+ def current_page
102
+ collection.respond_to?(:current_page) && collection.current_page
103
+ end
104
+
105
+ def prev_page
106
+ return collection.prev_page if collection.respond_to?(:prev_page) # Sequel and Kaminari
107
+ return collection.previous_page if collection.respond_to?(:previous_page) # will_paginate
108
+ end
109
+
110
+ # Supported by Sequel, Kaminari and will_paginate
111
+ def next_page
112
+ collection.respond_to?(:next_page) && collection.next_page
113
+ end
114
+
115
+ def per_page
116
+ if collection.respond_to?(:page_size)
117
+ collection.page_size # Supported by Sequel
118
+ elsif collection.respond_to?(:max_per_page)
119
+ collection.max_per_page # Supported by Kaminari
120
+ elsif collection.respond_to?(:per_page)
121
+ collection.per_page # Supported by will_paginate
122
+ end
123
+ end
124
+ end
125
+ end
126
+
@@ -0,0 +1,22 @@
1
+ module HALPresenter
2
+ module Policy
3
+
4
+ def policy(clazz)
5
+ @_policy = clazz
6
+ end
7
+
8
+ protected
9
+
10
+ def policy_class
11
+ @_policy ||= init_policy
12
+ end
13
+
14
+ private
15
+
16
+ def init_policy
17
+ return unless is_a? Class
18
+ return unless superclass.respond_to?(:policy_class, true)
19
+ superclass.policy_class
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,112 @@
1
+ module HALPresenter
2
+ module Policy
3
+ module DSL
4
+
5
+ class Rules
6
+
7
+ def attributes
8
+ @attributes ||= Hash.new(Proc.new { false })
9
+ end
10
+
11
+ def links
12
+ @links ||= Hash.new(Proc.new { false })
13
+ end
14
+
15
+ def embedded
16
+ @embedded ||= Hash.new(Proc.new { false })
17
+ end
18
+
19
+ private :attributes, :links, :embedded
20
+
21
+ def defaults(*types, value: false)
22
+ types.each do |t|
23
+ send(t).default= Proc.new { value }
24
+ end
25
+ end
26
+
27
+ def attribute_rule_for(name)
28
+ attributes[name]
29
+ end
30
+
31
+ def add_attribute(name, block)
32
+ attributes[name] = block
33
+ end
34
+
35
+ def link_rule_for(rel)
36
+ links[rel]
37
+ end
38
+
39
+ def add_link(rel, block)
40
+ links[rel] = block
41
+ end
42
+
43
+ def embed_rule_for(name)
44
+ embedded[name]
45
+ end
46
+
47
+ def add_embed(name, block)
48
+ embedded[name] = block
49
+ end
50
+
51
+ end
52
+
53
+ module ClassMethods
54
+
55
+ def allow_by_default(*types)
56
+ rules.defaults(*types, value: true)
57
+ end
58
+
59
+ def attribute(*names)
60
+ b = block_given? ? Proc.new : Proc.new { true }
61
+ names.each { |name| rules.add_attribute(name, b) }
62
+ end
63
+
64
+ def link(*rels)
65
+ b = block_given? ? Proc.new : Proc.new { true }
66
+ rels.each { |rel| rules.add_link(rel, b) }
67
+ end
68
+
69
+ def embed(*names)
70
+ b = block_given? ? Proc.new : Proc.new { true }
71
+ names.each { |name| rules.add_embed(name, b) }
72
+ end
73
+
74
+ def rules
75
+ @rules ||= Rules.new
76
+ end
77
+
78
+ end
79
+
80
+ def self.included(mod)
81
+ mod.extend(ClassMethods)
82
+ end
83
+
84
+ def initialize(current_user, resource, options = {})
85
+ @current_user = current_user
86
+ @resource = resource
87
+ @options = options
88
+ end
89
+
90
+ def attribute?(name)
91
+ run self.class.rules.attribute_rule_for(name)
92
+ end
93
+
94
+ def link?(rel)
95
+ run self.class.rules.link_rule_for(rel)
96
+ end
97
+
98
+ def embed?(name)
99
+ run self.class.rules.embed_rule_for(name)
100
+ end
101
+
102
+ private
103
+
104
+ attr_reader :current_user, :resource, :options
105
+
106
+ def run(block)
107
+ instance_eval(&block) && true || false
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,57 @@
1
+ module HALPresenter
2
+ class Property
3
+ attr_reader :name, :resource, :options
4
+
5
+ alias :resources :resource
6
+
7
+ def initialize(name, value = nil, &block)
8
+ @name = name
9
+ @value = value
10
+ @scope = nil
11
+ return unless block_given?
12
+ @scope = eval 'self', block.binding
13
+ define_singleton_method(:value_from_block, &block)
14
+ end
15
+
16
+ def value(resource = nil, options = {})
17
+ @resource = resource
18
+ @options = options
19
+ if scope
20
+ value_from_block
21
+ elsif resource && @value.nil?
22
+ resource.public_send(name) if resource.respond_to?(name)
23
+ else
24
+ @value
25
+ end
26
+ ensure
27
+ reset
28
+ end
29
+
30
+ def change_scope(new_scope)
31
+ return unless scope
32
+ @scope = new_scope
33
+ end
34
+
35
+ def method_missing(method, *args, &block)
36
+ if scope&.respond_to? method
37
+ define_singleton_method(method) { |*a, &b| scope.public_send method, *a, &b }
38
+ return public_send(method, *args, &block)
39
+ end
40
+ super
41
+ end
42
+
43
+ def respond_to_missing?(method, include_private = false)
44
+ return true if scope&.respond_to? method
45
+ super
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :scope
51
+
52
+ def reset
53
+ @resource = nil
54
+ @options = nil
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,41 @@
1
+ require 'hal_presenter/property'
2
+
3
+ module HALPresenter
4
+ module SerializeHooks
5
+
6
+ class Hook
7
+ attr_reader :name, :resource, :options
8
+
9
+ def initialize(&block)
10
+ @block = block
11
+ end
12
+
13
+ def run(resource, options, arg)
14
+ @resource = resource
15
+ @options = options
16
+ instance_exec(arg, &@block) if @block
17
+ ensure
18
+ @resource = nil
19
+ @options = nil
20
+ end
21
+ end
22
+
23
+ def post_serialize(&block)
24
+ @_post_serialize_hook = Hook.new(&block)
25
+ end
26
+
27
+ protected
28
+
29
+ def post_serialize_hook
30
+ @_post_serialize_hook ||= init_post_serialize_hook
31
+ end
32
+
33
+ private
34
+
35
+ def init_post_serialize_hook
36
+ return unless is_a? Class
37
+ return unless superclass.respond_to?(:post_serialize_hook, true)
38
+ superclass.post_serialize_hook
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,162 @@
1
+ require 'json'
2
+ require 'hal_presenter/pagination'
3
+
4
+ module HALPresenter
5
+
6
+ def self.to_hal(resource, options = {})
7
+ raise Serializer::Error, "Resource is nil" if resource.nil?
8
+ presenter = options.delete(:presenter)
9
+ presenter ||= HALPresenter.lookup_presenter(resource)&.last
10
+ raise Serializer::Error, "No presenter for #{resource}" unless presenter
11
+ presenter.to_hal(resource, options)
12
+ end
13
+
14
+ def self.to_collection(resources, options = {})
15
+ raise Serializer::Error, "resources is nil" if resources.nil?
16
+ presenter = options.delete(:presenter)
17
+ presenter ||= HALPresenter.lookup_presenter(resources.first)&.last
18
+ raise Serializer::Error, "No presenter for #{resources.first}" unless presenter
19
+ presenter.to_collection(resources, options)
20
+ end
21
+
22
+ module Serializer
23
+
24
+ class Error < StandardError; end
25
+
26
+ def to_hal(resource = nil, options = {})
27
+ hash = to_hash(resource, options)
28
+ JSON.generate(hash)
29
+ end
30
+
31
+ def to_collection(resources = [], options = {})
32
+ unless can_serialize_collection?
33
+ raise Error,
34
+ "Trying to serialize a collection using #{self} which has no collection info. " \
35
+ "Add a 'collection' spec to the serializer or use another serializer"
36
+ end
37
+ options[:paginate] = HALPresenter.paginate unless options.key? :paginate
38
+ hash = to_collection_hash(resources, options)
39
+ JSON.generate(hash)
40
+ end
41
+
42
+ protected
43
+
44
+ def to_hash(resource, options)
45
+ policy = policy_class&.new(options[:current_user], resource, options)
46
+
47
+ {}.tap do |serialized|
48
+ serialized.merge! serialize_attributes(resource, policy, options)
49
+ serialized.merge! serialize_links(resource, policy, options)
50
+ serialized.merge! serialize_embedded(resource, policy, options)
51
+
52
+ run_post_serialize_hook!(resource, options, serialized)
53
+ end
54
+ end
55
+
56
+ def to_collection_hash(resources, options)
57
+ parameters = collection_parameters
58
+ links = parameters.links
59
+ curies = parameters.curies
60
+ {}.tap do |serialized|
61
+ serialized.merge! _serialize_attributes(parameters.attributes, resources, nil, options)
62
+ serialized.merge! _serialize_links(links, curies, resources, nil, options)
63
+ Pagination.paginate!(serialized, resources) if options[:paginate]
64
+
65
+ serialized_resources = resources.map { |resource| to_hash(resource, options) }
66
+ serialized[:_embedded] = { parameters.name => serialized_resources }
67
+ end
68
+ end
69
+
70
+ def serialize_attributes(resource, policy, options)
71
+ _serialize_attributes(attributes, resource, policy, options)
72
+ end
73
+
74
+ def serialize_links(resource, policy, options)
75
+ _serialize_links(links, curies, resource, policy, options)
76
+ end
77
+
78
+ def serialize_curies(resource, policy, options)
79
+ _serialize_curies(curies, resource, policy, options)
80
+ end
81
+
82
+ def serialize_embedded(resource, policy, options)
83
+ _serialize_embedded(embedded, resource, policy, options)
84
+ end
85
+
86
+ def run_post_serialize_hook!(resource, options, serialized)
87
+ hook = post_serialize_hook
88
+ hook&.run(resource, options, serialized)
89
+ end
90
+
91
+ private
92
+
93
+ def _serialize_attributes(attributes, resource, policy, options)
94
+ attributes.each_with_object({}) do |attribute, hash|
95
+ next if policy && !policy.attribute?(attribute.name)
96
+ hash[attribute.name] = attribute.value(resource, options)
97
+ end
98
+ end
99
+
100
+ def _serialize_links(links, curies, resource, policy, options)
101
+ serialized = links.each_with_object({}) do |link, hash|
102
+ next if policy && !policy.link?(link.rel)
103
+ href = link.value(resource, options) or next
104
+ hash[link.rel] = { href: HALPresenter.href(href) }.tap do |s|
105
+ s[:method] = link.http_method if link.http_method
106
+ end
107
+ end
108
+ curies = _serialize_curies(curies, resource, policy, options)
109
+ serialized[:curies] = curies if curies.any?
110
+ return {} if serialized.empty?
111
+ { _links: serialized }
112
+ end
113
+
114
+ def _serialize_curies(curies, resource, policy, options)
115
+ curies.each_with_object([]) do |curie, array|
116
+ next if policy && !policy.link?(curie.name)
117
+ href = curie.value(resource, options) or next
118
+ array << {
119
+ name: curie.name,
120
+ href: HALPresenter.href(href),
121
+ templated: true
122
+ }
123
+ end
124
+ end
125
+
126
+ def _serialize_embedded(embedded, object, policy, options)
127
+ serialized = embedded.each_with_object({}) do |embed, hash|
128
+ next if policy && !policy.embed?(embed.name)
129
+ resource = embed.value(object, options) or next
130
+ presenter = embed.presenter_class
131
+ hash[embed.name] =
132
+ if resource.respond_to? :each
133
+ _serialize_embedded_collection(resource, presenter, options)
134
+ else
135
+ presenter ||= HALPresenter.lookup_presenter(resource).first
136
+ presenter.to_hash(resource, options)
137
+ end
138
+ end
139
+ return {} if serialized.empty?
140
+ { _embedded: serialized }
141
+ end
142
+
143
+ def _serialize_embedded_collection(resources, presenter, options)
144
+ clazz = resources.first.class
145
+ presenter ||= HALPresenter.lookup_presenter(clazz)&.first
146
+ if presenter.nil?
147
+ raise Serializer::Error,
148
+ "No presenter specified to handle serializing embedded #{clazz}"
149
+ end
150
+ if presenter.respond_to?(:can_serialize_collection?, true) &&
151
+ presenter.can_serialize_collection?
152
+ presenter.to_collection_hash(resources, options)
153
+ else
154
+ resources.map do |resrc|
155
+ presenter.to_hash(resrc, options)
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+
metadata ADDED
@@ -0,0 +1,202 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hal_presenter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Sammy Henningsson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDljCCAn6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBIMRowGAYDVQQDDBFzYW1t
14
+ eS5oZW5uaW5nc3NvbjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
15
+ LGQBGRYDY29tMB4XDTE3MDcwODE0MDUxOVoXDTE4MDcwODE0MDUxOVowSDEaMBgG
16
+ A1UEAwwRc2FtbXkuaGVubmluZ3Nzb24xFTATBgoJkiaJk/IsZAEZFgVnbWFpbDET
17
+ MBEGCgmSJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
18
+ ggEBAK+SDC1mfyhucJ6Va21rIHUGscEtQrdvyBqxFG1s2TgPMAv4RbqwdJVPa7kj
19
+ tbCzslADlUE1oru2C+rcJsMtVGX02ukMIPHT1OjTyy0/EMqLqSy3WeRI8APyDSxC
20
+ Vbe+h5BMf3zZnYfddR6AeG7ln09T1P/tX+9lTMc+I+DW1fUlQgY48CNUayvtJR61
21
+ svXvXMrhLhi29SQig1qmH6Zoe22/JgH+m2JksPndY5Ep3gqfDc6Imwu2vGvmGErJ
22
+ D63FB0XQ/wb4WVH4l7sHQSTfKDp8SImCt1xqNgIyjw578ZG2geGLoncuxgDrbQ/U
23
+ FIJ11lDZd4vLevMhnIxTSJpPr2cCAwEAAaOBijCBhzAJBgNVHRMEAjAAMAsGA1Ud
24
+ DwQEAwIEsDAdBgNVHQ4EFgQUukjj1Cd2ea6IOHDLZe0ymzs2jWkwJgYDVR0RBB8w
25
+ HYEbc2FtbXkuaGVubmluZ3Nzb25AZ21haWwuY29tMCYGA1UdEgQfMB2BG3NhbW15
26
+ Lmhlbm5pbmdzc29uQGdtYWlsLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAGBReofO0
27
+ CDGI8uQNn/btde+zQBiCX0mONJMQ6An760MzOHMlWFi6VaVeeKmYj5xCpI2nWaJN
28
+ 49ALW/v7GZ+JIW5YmbzWIuD8YfwHJYCFPoH0llzg3GCFCMxbYqI3nDtHX3dMxpOc
29
+ 01A72okERe0ne/O5Z4Ym/fOECkyXr0fYondhDQcSuqxU22jsXs1ZjISN8IuHmVkA
30
+ GtGteOIqmA05lP+eCXGvyARGLg1GwZDTjnlH5OfpiT1Oy7Dghewi58Gn4/AlZ4bY
31
+ CNZdF8Vavp6xMQbPHZwqjaeZz2WRXYS7jyYSvCunjwa3OtvXtfbIEGEWE6IM+t9k
32
+ H1g6Q+B6qk9O6g==
33
+ -----END CERTIFICATE-----
34
+ date: 2018-01-28 00:00:00.000000000 Z
35
+ dependencies:
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '12.0'
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '10.0'
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '12.0'
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '10.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: activesupport
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '5.0'
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '4.0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: '5.0'
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '4.0'
76
+ - !ruby/object:Gem::Dependency
77
+ name: minitest
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.10'
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '5.0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: '5.10'
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '5.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: byebug
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '9.0'
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '9.0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '9.0'
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '9.0'
116
+ - !ruby/object:Gem::Dependency
117
+ name: kaminari
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '1.1'
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: 1.1.1
126
+ type: :development
127
+ prerelease: false
128
+ version_requirements: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '1.1'
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: 1.1.1
136
+ - !ruby/object:Gem::Dependency
137
+ name: will_paginate
138
+ requirement: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '3.1'
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 3.1.6
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.1'
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: 3.1.6
156
+ description: |
157
+ A DSL for serializing resources according to
158
+ HypertextApplicationLanguage.
159
+ email: sammy.henningsson@gmail.com
160
+ executables: []
161
+ extensions: []
162
+ extra_rdoc_files: []
163
+ files:
164
+ - lib/hal_presenter.rb
165
+ - lib/hal_presenter/attributes.rb
166
+ - lib/hal_presenter/collection.rb
167
+ - lib/hal_presenter/curies.rb
168
+ - lib/hal_presenter/deserializer.rb
169
+ - lib/hal_presenter/embedded.rb
170
+ - lib/hal_presenter/links.rb
171
+ - lib/hal_presenter/model.rb
172
+ - lib/hal_presenter/pagination.rb
173
+ - lib/hal_presenter/policy.rb
174
+ - lib/hal_presenter/policy/dsl.rb
175
+ - lib/hal_presenter/property.rb
176
+ - lib/hal_presenter/serialize_hooks.rb
177
+ - lib/hal_presenter/serializer.rb
178
+ homepage: https://github.com/sammyhenningsson/hal_presenter
179
+ licenses:
180
+ - MIT
181
+ metadata: {}
182
+ post_install_message:
183
+ rdoc_options: []
184
+ require_paths:
185
+ - lib
186
+ required_ruby_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ required_rubygems_version: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ requirements: []
197
+ rubyforge_project:
198
+ rubygems_version: 2.7.3
199
+ signing_key:
200
+ specification_version: 4
201
+ summary: HAL serializer
202
+ test_files: []
metadata.gz.sig ADDED
@@ -0,0 +1,3 @@
1
+ �Bsv��wJ�����T���=��f@�����]| ���3�n�e-i��M���r�sE���$��
2
+ �x�]�ĥ#
3
+ s<���E��w���1�OI���mz2