hal_decorator 0.2.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
+ SHA1:
3
+ metadata.gz: 1fdb9a14bc7ef54528db8017406dd28708f6543b
4
+ data.tar.gz: 3daea7984ca3962708d013f493c5f913a6308551
5
+ SHA512:
6
+ metadata.gz: e572bb50f4e82435f9760cb19cfcece75f68810ae2d517c83a6e8876751918d822cae1d90f9e5b0371ca9ab12c89ea9f2f6b3b687a59112763b02a2459a0cebb
7
+ data.tar.gz: d891bc22668258cc50a0e0863338245ed9d24c4e9e73dd4948a9752e31b972731043e22c9b4f5eeba9282d586f1d4061c4eaf1fb84fd9a53227b0a479be2c2c2
checksums.yaml.gz.sig ADDED
Binary file
@@ -0,0 +1,15 @@
1
+ require 'hal_decorator/property'
2
+
3
+ module HALDecorator
4
+ module Attributes
5
+ def attribute(*args, &block)
6
+ @_attributes ||= []
7
+ @_attributes << Property.new(*args, &block)
8
+ end
9
+
10
+ def attributes
11
+ @_attributes ||= []
12
+ @_attributes.dup
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ require 'hal_decorator/property'
2
+
3
+ module HALDecorator
4
+ module Collection
5
+
6
+ attr_reader :collection_name
7
+
8
+ class CollectionParameters
9
+ include Attributes
10
+ include Links
11
+ include Curies
12
+
13
+ attr_reader :name
14
+
15
+ def initialize(name, &block)
16
+ @name = name
17
+ instance_exec(&block)
18
+ end
19
+ end
20
+
21
+ def collection(of:, &block)
22
+ @_parameters = CollectionParameters.new(of, &block)
23
+ end
24
+
25
+ def collection_parameters
26
+ @_parameters ||= nil
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ require 'hal_decorator/property'
2
+
3
+ module HALDecorator
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 ||= []
10
+ @_curies << Property.new(rel, value, &block)
11
+ end
12
+
13
+ def curies
14
+ @_curies ||= []
15
+ @_curies.dup
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,86 @@
1
+ require 'json'
2
+
3
+ module HALDecorator
4
+
5
+ module Deserializer
6
+
7
+ class Error < StandardError; end
8
+
9
+ def from_hal(payload, resource = nil)
10
+ hash = JSON.parse(payload)
11
+ from_hash(hash, resource)
12
+ end
13
+
14
+ protected
15
+
16
+ def from_hash(hash, resource)
17
+ as_collection = deserialize_as_collection?(hash)
18
+
19
+ if resource.nil?
20
+ model = HALDecorator.lookup_model self
21
+ raise Error, "No model for #{self.class}" unless model
22
+ resource = as_collection ? [] : model.new
23
+ elsif as_collection
24
+ resource.clear
25
+ end
26
+
27
+ if as_collection
28
+ deserialize_collection(hash, resource)
29
+ else
30
+ deserialize_attributes(hash, resource)
31
+ deserialize_embedded(hash, resource)
32
+ end
33
+ resource
34
+ end
35
+
36
+ def deserialize_attributes(hash, resource)
37
+ attributes.each do |attribute|
38
+ setter_method = setter_method_name(attribute.name)
39
+ next unless resource.respond_to? setter_method
40
+ resource.send(setter_method, hash[attribute.name.to_s])
41
+ end
42
+ end
43
+
44
+ def deserialize_embedded(hash, resource)
45
+ embedded.each do |embed|
46
+ setter_method = setter_method_name(embed.name) or next
47
+ next unless resource.respond_to? setter_method
48
+ decorator = embed.decorator_class or next
49
+
50
+ embedded_hash = hash.dig('_embedded', embed.name.to_s)
51
+ next unless embedded_hash&.any?
52
+
53
+ embedded_resource = resource.send(embed.name)
54
+ embedded_resource =
55
+ if embedded_hash.is_a? Array
56
+ embedded_hash.map { |h| decorator.from_hash(h, embedded_resource) }
57
+ else
58
+ decorator.from_hash(embedded_hash, embedded_resource)
59
+ end
60
+ resource.send(setter_method, embedded_resource)
61
+ end
62
+ end
63
+
64
+ def deserialize_collection(hash, resource)
65
+ hash['_embedded'][collection_parameters.name].each do |resource_hash|
66
+ resource << from_hash(resource_hash, nil)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def deserialize_as_collection?(hash)
73
+ name = collection_parameters&.name
74
+ # return true/false (Hash#key? returns nil if not found..)
75
+ name && hash['_embedded']&.key?(name) || false
76
+ end
77
+
78
+ def setter_method_name(attr)
79
+ "#{attr}=".to_sym
80
+ end
81
+
82
+ end
83
+ end
84
+
85
+
86
+
@@ -0,0 +1,24 @@
1
+ require 'hal_decorator/property'
2
+
3
+ module HALDecorator
4
+ module Embedded
5
+ class Embed < HALDecorator::Property
6
+ attr_reader :decorator_class
7
+
8
+ def initialize(name, value = nil, decorator_class: nil, &block)
9
+ super(name, value, &block)
10
+ @decorator_class = decorator_class
11
+ end
12
+ end
13
+
14
+ def embed(*args, &block)
15
+ @_embedded ||= []
16
+ @_embedded << Embed.new(*args, &block)
17
+ end
18
+
19
+ def embedded
20
+ @_embedded ||= []
21
+ @_embedded.dup
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ require 'hal_decorator/property'
2
+
3
+ module HALDecorator
4
+ module Links
5
+
6
+ class Link < HALDecorator::Property
7
+ attr_reader :http_method
8
+ def initialize(rel, value = nil, http_method: nil, &block)
9
+ if value.nil? && !block_given?
10
+ raise 'link must be called with non nil value or be given a block'
11
+ end
12
+ @http_method = http_method
13
+ super(rel, value, &block)
14
+ end
15
+ end
16
+
17
+ def link(rel, value = nil, method: nil, methods: nil, &block)
18
+ @_links ||= []
19
+ @_links << Link.new(rel, value, http_method: method || methods, &block)
20
+ end
21
+
22
+ def links
23
+ @_links ||= []
24
+ @_links.dup
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ module HALDecorator
2
+ @decorators = {}
3
+
4
+ def HALDecorator.register(model:, decorator:)
5
+ @decorators[decorator] = model
6
+ end
7
+
8
+ def HALDecorator.unregister(decorator)
9
+ @decorators.delete_if { |d,_| d == decorator }
10
+ end
11
+
12
+ def HALDecorator.lookup_model(decorator)
13
+ @decorators[decorator]
14
+ end
15
+
16
+ def HALDecorator.lookup_decorator(model)
17
+ clazz = model.class == Class ? model : model.class
18
+ decorators = @decorators.select { |d, m| m == clazz }.keys.compact
19
+ decorators.empty? ? nil : decorators
20
+ end
21
+
22
+ module Model
23
+ def model(clazz)
24
+ HALDecorator.register(model: clazz, decorator: self)
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,50 @@
1
+ module HALDecorator
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 method_missing(method, *args, &block)
31
+ if @scope&.respond_to? method
32
+ define_singleton_method(method) { |*a, &b| @scope.send method, *a, &b }
33
+ return send(method, *args, &block)
34
+ end
35
+ super
36
+ end
37
+
38
+ def respond_to_missing?(method, include_private = false)
39
+ return true if @scope&.respond_to? method
40
+ super
41
+ end
42
+
43
+ private
44
+
45
+ def reset
46
+ @resource = nil
47
+ @options = nil
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,111 @@
1
+ require 'json'
2
+
3
+ module HALDecorator
4
+
5
+ module Serializer
6
+
7
+ class Error < StandardError; end
8
+
9
+ def to_hal(resource, options = {})
10
+ hash = to_hash(resource, options)
11
+ JSON.generate(hash)
12
+ end
13
+
14
+ def to_hash(object, options, embed: true)
15
+ {}.tap do |serialized|
16
+ serialized.merge! serialize_attributes(object, options)
17
+ serialized.merge! serialize_links(object, options)
18
+ serialized.merge! serialize_embedded(object, options) if embed
19
+ end
20
+ end
21
+
22
+ def to_collection(resources, options = {})
23
+ parameters = collection_parameters
24
+ if parameters.nil?
25
+ raise Error,
26
+ "Trying to serialize a collection using #{self} which has no collection info. " \
27
+ "Add a 'collection' spec to the serializer or use another serializer"
28
+ end
29
+ links = parameters.links
30
+ curies = parameters.curies
31
+ serialized = _serialize_attributes(parameters.attributes, resources, options)
32
+ serialized.merge! _serialize_links(links, curies, resources, options)
33
+
34
+ serialized_resources = resources.map do |resource|
35
+ to_hash(resource, embed: false)
36
+ end
37
+ serialized[:_embedded] = { parameters.name => serialized_resources }
38
+ JSON.generate(serialized)
39
+ end
40
+
41
+ protected
42
+
43
+ def serialize_attributes(object, options)
44
+ _serialize_attributes(attributes, object, options)
45
+ end
46
+
47
+ def serialize_links(object, options)
48
+ _serialize_links(links, curies, object, options)
49
+ end
50
+
51
+ def serialize_curies(object, options)
52
+ _serialize_curies(curies, object, options)
53
+ end
54
+
55
+ def serialize_embedded(object, options)
56
+ _serialize_embedded(embedded, object, options)
57
+ end
58
+
59
+ private
60
+
61
+ def _serialize_attributes(attributes, object, options)
62
+ attributes.each_with_object({}) do |attribute, hash|
63
+ hash[attribute.name] = attribute.value(object, options)
64
+ end
65
+ end
66
+
67
+ def _serialize_links(links, curies, object, options)
68
+ serialized = links.each_with_object({}) do |link, hash|
69
+ value = link.value(object, options) or next
70
+ hash[link.name] = { href: value }.tap do |s|
71
+ s[:method] = link.http_method if link.http_method
72
+ end
73
+ end
74
+ curies = _serialize_curies(curies, object, options)
75
+ serialized[:curies] = curies if curies.any?
76
+ return {} if serialized.empty?
77
+ { _links: serialized }
78
+ end
79
+
80
+ def _serialize_curies(curies, object, options)
81
+ curies.each_with_object([]) do |curie, array|
82
+ array << {
83
+ name: curie.name,
84
+ href: curie.value(object, options),
85
+ templated: true
86
+ }
87
+ end
88
+ end
89
+
90
+ def _serialize_embedded(embedded, object, options)
91
+ serialized = embedded.each_with_object({}) do |embed, hash|
92
+ resource = embed.value(object, options) or next
93
+ decorator = embed.decorator_class
94
+ hash[embed.name] =
95
+ if resource.respond_to? :each
96
+ decorator ||= HALDecorator.lookup_decorator(resource.first).first
97
+ resource.map do |resrc|
98
+ decorator.to_hash(resrc, options, embed: false)
99
+ end
100
+ else
101
+ decorator ||= HALDecorator.lookup_decorator(resource).first
102
+ decorator.to_hash(resource, options, embed: false)
103
+ end
104
+ end
105
+ return {} if serialized.empty?
106
+ { _embedded: serialized }
107
+ end
108
+ end
109
+ end
110
+
111
+
@@ -0,0 +1,47 @@
1
+ require 'hal_decorator/attributes'
2
+ require 'hal_decorator/embedded'
3
+ require 'hal_decorator/links'
4
+ require 'hal_decorator/curies'
5
+ require 'hal_decorator/model'
6
+ require 'hal_decorator/serializer'
7
+ require 'hal_decorator/deserializer'
8
+ require 'hal_decorator/collection'
9
+
10
+
11
+ module HALDecorator
12
+
13
+ def self.included(base)
14
+ base.extend ClassMethods
15
+ end
16
+
17
+ module ClassMethods
18
+ include HALDecorator::Attributes
19
+ include HALDecorator::Links
20
+ include HALDecorator::Curies
21
+ include HALDecorator::Embedded
22
+ include HALDecorator::Collection
23
+ include HALDecorator::Model
24
+ include HALDecorator::Serializer
25
+ include HALDecorator::Deserializer
26
+ end
27
+
28
+ def self.to_hal(resource, options = {})
29
+ raise Serializer::Error, "Resource is nil" if resource.nil?
30
+ decorator = options.delete(:decorator)
31
+ decorator ||= HALDecorator.lookup_decorator(resource)&.first
32
+ raise Serializer::Error, "No decorator for #{resource}" unless decorator
33
+ decorator.to_hal(resource, options)
34
+ end
35
+
36
+ def self.to_collection(resources, options = {})
37
+ raise Serializer::Error, "resources is nil" if resources.nil?
38
+ decorator = options.delete(:decorator)
39
+ decorator ||= HALDecorator.lookup_decorator(resources.first)&.first
40
+ raise Serializer::Error, "No decorator for #{resources.first}" unless decorator
41
+ decorator.to_collection(resources, options)
42
+ end
43
+
44
+ def self.from_hal(decorator, payload, resource = nil)
45
+ decorator.from_hal(payload, resource)
46
+ end
47
+ end
data.tar.gz.sig ADDED
@@ -0,0 +1 @@
1
+ ���Ts����A�� ��V�����pH*.�\n��)�]�c������6԰̾H�� z��5yהcEp@�HN*أ�n�;_x��?A����?y(:"�֦��c�c��ŗ��4�C��>FJ� �*ɝk�m)F�.ӌw(B�ӯ �v���PK
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hal_decorator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.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: 2017-07-27 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.1'
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.1'
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
+ description: |
117
+ Serialize resources according to
118
+ HypertextApplicationLanguage.
119
+ email: sammy.henningsson@gmail.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - lib/hal_decorator.rb
125
+ - lib/hal_decorator/attributes.rb
126
+ - lib/hal_decorator/collection.rb
127
+ - lib/hal_decorator/curies.rb
128
+ - lib/hal_decorator/deserializer.rb
129
+ - lib/hal_decorator/embedded.rb
130
+ - lib/hal_decorator/links.rb
131
+ - lib/hal_decorator/model.rb
132
+ - lib/hal_decorator/property.rb
133
+ - lib/hal_decorator/serializer.rb
134
+ homepage: https://github.com/sammyhenningsson/hal_decorator
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.6.11
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: HAL serializer
158
+ test_files: []
metadata.gz.sig ADDED
Binary file