hal_presenter 1.2.1 → 1.3.0

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