hal_presenter 1.2.1 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12895524e08462462b15f197216f20f3fd24a53e7e6a60e7c5876639cd72dba5
4
- data.tar.gz: b74e9c2084b7652447ee5bf90a56212fd5fc3829ee0e7a842187889b71ded750
3
+ metadata.gz: 10e6de4d852aa101cc4318eb88d152996adeb2e288359a0c9a53f6efcc5e9cc6
4
+ data.tar.gz: 89080e06a6c5a756bc00d9620663fb8e6eb97906059285666d13f007a4e6b4fa
5
5
  SHA512:
6
- metadata.gz: c6f920f2d359fba3dd088f217dd94b7ecce8a5023a2f59ddf5daf3be012f81205663f6be10263ba7d67435a6c220f6904aa121fc13381eeda1d890444ada3ee2
7
- data.tar.gz: '08041d1884286f31bfd1e2b14014b518ec2979081c222703f435b0b26c83d13c53f630cd9da4593c4e6be5b14af60289e0e66d7ac8e50e40a0783ad9aee664be'
6
+ metadata.gz: ab43657fea638457294930e648d5a33b2a6055ff02e511ba239d55b0b38bb8bad9ea6a3816b2c01674efa38c816876681af075dc5fc6ccb67ad1ebe44a891ae6
7
+ data.tar.gz: 84f44aa7ef856dbec2e23c56a1aceef04b35b253ebc1dd574c7379aa574f72b57d6b8e969e4a53eff8b7be5da54ecc23ca8d0411d87dbdb2f52a694bb7dcd8a7
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -0,0 +1,158 @@
1
+ require 'set'
2
+
3
+ module HALPresenter
4
+ class CurieCollection
5
+ class CurieWithReferences
6
+ attr_reader :name, :href, :templated, :rels, :references
7
+
8
+ def initialize(curie)
9
+ @name = curie.fetch(:name)
10
+ @href = curie.fetch(:href)
11
+ @templated = curie.fetch(:templated, true)
12
+ @rels = Hash.new { |hash, key| hash[key] = Set.new }
13
+ @references = Hash.new do |hash, key|
14
+ hash[key] = Set.new.compare_by_identity
15
+ end
16
+ end
17
+
18
+ def add_reference(rel, reference, type)
19
+ rels[type] << rel
20
+ references[type] << reference
21
+ end
22
+
23
+ def <<(other)
24
+ other.rels.each do |type, rels|
25
+ self.rels[type] += rels
26
+ end
27
+ other.references.each do |type, references|
28
+ self.references[type] += references
29
+ end
30
+ end
31
+
32
+ def rename(name)
33
+ self.name = name
34
+
35
+ rels.each do |type, rels|
36
+ rels.each do |rel|
37
+ new_rel = replace_curie(name, rel)
38
+ references[type].each do |reference|
39
+ reference[new_rel] = reference.delete(rel)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def to_h
46
+ {
47
+ name: name,
48
+ href: href,
49
+ templated: templated
50
+ }
51
+ end
52
+
53
+ private
54
+
55
+ attr_writer :name
56
+
57
+ def replace_curie(name, rel)
58
+ _, rest = rel.to_s.split(':', 2)
59
+ :"#{name}:#{rest}"
60
+ end
61
+ end
62
+
63
+ attr_reader :collection
64
+
65
+ def self.extract_from!(hash, resolve_collisions: true)
66
+ new.tap do |curies|
67
+ curies.add_curies(hash[:_links]&.delete(:curies))
68
+ curies.send(:add_references, hash[:_links], :links)
69
+ curies.send(:add_references, hash[:_embedded], :embedded)
70
+ curies.resolve_collisions! if resolve_collisions
71
+ end
72
+ end
73
+
74
+ def initialize
75
+ @collection = []
76
+ end
77
+
78
+ def add_curies(curies)
79
+ return unless curies
80
+
81
+ curies.each do |curie|
82
+ next if find(curie[:name])
83
+ collection << CurieWithReferences.new(curie)
84
+ end
85
+ end
86
+
87
+ def generate_curie_name(base)
88
+ name = "#{base}0"
89
+ name = name.next while find(name.to_sym)
90
+ name.to_sym
91
+ end
92
+
93
+ def resolve_collisions!
94
+ collection.reverse_each do |curie|
95
+ next if collection.none? { |c| c.name == curie.name && c.href != curie.href }
96
+ new_name = generate_curie_name(curie.name)
97
+ curie.rename new_name
98
+ end
99
+
100
+ self
101
+ end
102
+
103
+ def to_a
104
+ collection.map(&:to_h)
105
+ end
106
+
107
+ def empty?
108
+ collection.empty?
109
+ end
110
+
111
+ def each
112
+ return collection.each unless block_given?
113
+ collection.each { |c| yield c }
114
+ end
115
+
116
+ private
117
+
118
+ def find(name, href = nil)
119
+ return unless name
120
+
121
+ collection.find do |c|
122
+ next unless c.name.to_sym == name.to_sym
123
+ href.nil? || c.href == href
124
+ end
125
+ end
126
+
127
+ def curie_from(rel)
128
+ parts = rel.to_s.split(':')
129
+ parts.first if parts.size > 1
130
+ end
131
+
132
+ def concat(other)
133
+ other.each do |curie|
134
+ if existing = find(curie.name, curie.href)
135
+ existing << curie
136
+ else
137
+ collection << curie
138
+ end
139
+ end
140
+ end
141
+
142
+ def add_references(reference, type)
143
+ return unless reference
144
+
145
+ reference.each do |rel, values|
146
+ curie_name = curie_from(rel)
147
+ curie = find(curie_name)
148
+ curie&.add_reference(rel, reference, type)
149
+
150
+ values = [values] if values.is_a? Hash
151
+ values.each do |value|
152
+ nested = self.class.extract_from!(value, resolve_collisions: false)
153
+ concat nested.collection
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -1,15 +1,20 @@
1
1
  require 'json'
2
2
 
3
3
  module HALPresenter
4
-
5
- def self.from_hal(presenter, payload, resource = nil)
6
- presenter.from_hal(payload, resource)
4
+ module ClassMethods
5
+ def from_hal(presenter, payload, resource = nil)
6
+ presenter.from_hal(payload, resource)
7
+ end
7
8
  end
8
9
 
9
10
  module Deserializer
10
11
 
11
12
  class Error < StandardError; end
12
13
 
14
+ def self.included(base)
15
+ base.extend ClassMethods
16
+ end
17
+
13
18
  def from_hal(payload, resource = nil)
14
19
  return if payload.nil? || payload.empty?
15
20
  hash = JSON.parse(payload)
@@ -3,14 +3,16 @@ require 'hal_presenter/super_init'
3
3
 
4
4
  module HALPresenter
5
5
 
6
- def self.base_href=(base)
7
- @base_href = base&.sub(%r(/*$), '')
8
- end
6
+ module ClassMethods
7
+ def base_href=(base)
8
+ @base_href = base&.sub(%r(/*$), '')
9
+ end
9
10
 
10
- def self.href(href)
11
- return href if (@base_href ||= '').empty?
12
- return href if href =~ %r(\A(\w+://)?[^/])
13
- @base_href + href
11
+ def href(href)
12
+ return href if (@base_href ||= '').empty?
13
+ return href if href =~ %r(\A(\w+://)?[^/])
14
+ @base_href + href
15
+ end
14
16
  end
15
17
 
16
18
  module Links
@@ -56,6 +58,10 @@ module HALPresenter
56
58
  end
57
59
  end
58
60
 
61
+ def self.included(base)
62
+ base.extend ClassMethods
63
+ end
64
+
59
65
  def link(rel, value = nil, **kwargs, &block)
60
66
  if value.nil? && !block_given?
61
67
  raise 'link must be called with non nil value or be given a block'
@@ -1,34 +1,40 @@
1
1
  module HALPresenter
2
2
  @presenters = {}
3
3
 
4
- def self.register(model:, presenter:)
5
- return unless presenter && model
6
- @presenters[presenter] = model
7
- end
4
+ module ClassMethods
5
+ def register(model:, presenter:)
6
+ return unless presenter && model
7
+ @presenters[presenter] = model
8
+ end
8
9
 
9
- def self.unregister(presenter)
10
- @presenters.delete_if { |d,_| d == presenter }
11
- end
10
+ def unregister(presenter)
11
+ @presenters.delete_if { |d,_| d == presenter }
12
+ end
12
13
 
13
- def self.lookup_model(presenter)
14
- @presenters[presenter]
15
- end
14
+ def lookup_model(presenter)
15
+ @presenters[presenter]
16
+ end
16
17
 
17
- def self.lookup_presenter(model)
18
- presenters = lookup_presenters(model)
19
- return presenters.last unless presenters.empty?
20
- lookup_presenters(model.first).last if model.respond_to? :first
21
- end
18
+ def lookup_presenter(model)
19
+ presenters = lookup_presenters(model)
20
+ return presenters.last unless presenters.empty?
21
+ lookup_presenters(model.first).last if model.respond_to? :first
22
+ end
22
23
 
23
- def self.lookup_presenters(model)
24
- clazz = model.is_a?(Class) ? model : model.class
25
- presenters = @presenters.select { |_d, m| m == clazz }.keys
26
- return presenters unless presenters.empty?
27
- return [] unless clazz < BasicObject
28
- lookup_presenters(clazz.superclass)
24
+ def lookup_presenters(model)
25
+ clazz = model.is_a?(Class) ? model : model.class
26
+ presenters = @presenters.select { |_d, m| m == clazz }.keys
27
+ return presenters unless presenters.empty?
28
+ return [] unless clazz < BasicObject
29
+ lookup_presenters(clazz.superclass)
30
+ end
29
31
  end
30
32
 
31
33
  module Model
34
+ def self.included(base)
35
+ base.extend ClassMethods
36
+ end
37
+
32
38
  def model(clazz)
33
39
  HALPresenter.register(model: clazz, presenter: self)
34
40
  end
@@ -1,11 +1,12 @@
1
1
  module HALPresenter
2
- class << self
2
+ module ClassMethods
3
3
  attr_accessor :paginate
4
4
  end
5
5
 
6
- # TODO: Support Kaminari and Will_paginate
7
-
8
6
  class Pagination
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ end
9
10
 
10
11
  class Uri
11
12
  def self.parse(str)
@@ -1,32 +1,42 @@
1
1
  require 'json'
2
2
  require 'hal_presenter/pagination'
3
+ require 'hal_presenter/curie_collection'
3
4
 
4
5
  module HALPresenter
5
6
 
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)
10
- raise Serializer::Error, "No presenter for #{resource.class}" 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)
18
- raise Serializer::Error, "No presenter for #{resources.first.class}" unless presenter
19
- presenter.to_collection(resources, options)
7
+ module ClassMethods
8
+ def to_hal(resource, options = {})
9
+ raise Serializer::Error, "Resource is nil" if resource.nil?
10
+ options = options.dup
11
+ presenter = options.delete(:presenter)
12
+ presenter ||= HALPresenter.lookup_presenter(resource)
13
+ raise Serializer::Error, "No presenter for #{resource.class}" unless presenter
14
+ presenter.to_hal(resource, options)
15
+ end
16
+
17
+ def to_collection(resources, options = {})
18
+ raise Serializer::Error, "resources is nil" if resources.nil?
19
+ options = options.dup
20
+ presenter = options.delete(:presenter)
21
+ presenter ||= HALPresenter.lookup_presenter(resources)
22
+ raise Serializer::Error, "No presenter for #{resources.first.class}" unless presenter
23
+ presenter.to_collection(resources, options)
24
+ end
20
25
  end
21
26
 
22
27
  module Serializer
23
28
 
24
29
  class Error < StandardError; end
25
30
 
31
+ def self.included(base)
32
+ base.extend(ClassMethods)
33
+ end
34
+
26
35
  def to_hal(resource = nil, options = {})
36
+ options = options.dup
27
37
  options[:_depth] ||= 0
28
38
  hash = to_hash(resource, options)
29
- move_curies_to_top! hash
39
+ move_curies_to_root! hash
30
40
  JSON.generate(hash)
31
41
  end
32
42
 
@@ -36,10 +46,11 @@ module HALPresenter
36
46
  "Trying to serialize a collection using #{self} which has no collection info. " \
37
47
  "Add a 'collection' spec to the serializer or use another serializer"
38
48
  end
49
+ options = options.dup
39
50
  options[:paginate] = HALPresenter.paginate unless options.key? :paginate
40
51
  options[:_depth] ||= 0
41
52
  hash = to_collection_hash(resources, options)
42
- move_curies_to_top! hash
53
+ move_curies_to_root! hash
43
54
  JSON.generate(hash)
44
55
  end
45
56
 
@@ -72,8 +83,9 @@ module HALPresenter
72
83
  serialized.merge! _serialize_embedded(embedded, resources, policy, options)
73
84
 
74
85
  # Embedded resources
75
- options[:_depth] += 1
76
- serialized_resources = resources.map { |resource| to_hash(resource, options) }
86
+ embed_options = options.dup
87
+ embed_options[:_depth] += 1
88
+ serialized_resources = resources.map { |resource| to_hash(resource, embed_options) }
77
89
  serialized[:_embedded] ||= {}
78
90
  serialized[:_embedded].merge!(properties.name => serialized_resources)
79
91
  end
@@ -97,32 +109,16 @@ module HALPresenter
97
109
 
98
110
  private
99
111
 
100
- def move_curies_to_top!(hash)
101
- curies = {}
102
- find_curies(hash).each do |curie|
103
- name = curie[:name]
104
- curies[name] = curie
105
- end
112
+ def move_curies_to_root!(hash)
113
+ return if Hash(hash).empty?
106
114
 
107
- return if curies.empty?
115
+ curie_collection = CurieCollection.extract_from!(hash)
116
+ return if curie_collection.empty?
108
117
 
109
118
  hash[:_links] ||= {}
110
- hash[:_links][:curies] = curies.values
119
+ hash[:_links][:curies] = curie_collection.to_a
111
120
  end
112
121
 
113
- def find_curies(hash)
114
- return [] if Hash(hash).empty?
115
-
116
- curies = hash[:_links].delete(:curies) if hash.key? :_links
117
- curies ||= []
118
-
119
- hash.fetch(:_embedded, {}).values.each do |embedded|
120
- collection = embedded.is_a?(Array) ? embedded : [embedded]
121
- collection.each { |resrc| curies += find_curies(resrc) }
122
- end
123
-
124
- curies
125
- end
126
122
 
127
123
  def run_post_serialize_hook!(resource, options, serialized)
128
124
  hook = post_serialize_hook
@@ -163,13 +159,14 @@ module HALPresenter
163
159
  next if policy && !policy.embed?(embed.name)
164
160
  resource = embed.value(object, options) or next
165
161
  presenter = embed.presenter_class
166
- options[:_depth] += 1
162
+ embed_options = options.dup
163
+ embed_options[:_depth] += 1
167
164
  hash[embed.name] =
168
165
  if resource.is_a? Array
169
- _serialize_embedded_collection(resource, presenter, options)
166
+ _serialize_embedded_collection(resource, presenter, embed_options)
170
167
  else
171
168
  presenter ||= HALPresenter.lookup_presenter(resource)
172
- presenter.to_hash(resource, options)
169
+ presenter.to_hash(resource, embed_options)
173
170
  end
174
171
  end
175
172
  return {} if serialized.empty?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hal_presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sammy Henningsson
@@ -30,7 +30,7 @@ cert_chain:
30
30
  ZMhjYR7sRczGJx+GxGU2EaR0bjRsPVlC4ywtFxoOfRG3WaJcpWGEoAoMJX6Z0bRv
31
31
  M40=
32
32
  -----END CERTIFICATE-----
33
- date: 2019-08-31 00:00:00.000000000 Z
33
+ date: 2019-10-29 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: rake
@@ -163,6 +163,7 @@ files:
163
163
  - lib/hal_presenter.rb
164
164
  - lib/hal_presenter/attributes.rb
165
165
  - lib/hal_presenter/collection.rb
166
+ - lib/hal_presenter/curie_collection.rb
166
167
  - lib/hal_presenter/curies.rb
167
168
  - lib/hal_presenter/deserializer.rb
168
169
  - lib/hal_presenter/embedded.rb
@@ -197,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
198
  - !ruby/object:Gem::Version
198
199
  version: '0'
199
200
  requirements: []
200
- rubygems_version: 3.0.1
201
+ rubygems_version: 3.0.6
201
202
  signing_key:
202
203
  specification_version: 4
203
204
  summary: JSON HAL serializer
metadata.gz.sig CHANGED
Binary file