hal_presenter 0.4.0

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