presentability 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/History.md +8 -0
- data/lib/presentability/presenter.rb +11 -1
- data/lib/presentability.rb +12 -5
- data/spec/presentability/presenter_spec.rb +24 -5
- data/spec/presentability_spec.rb +94 -1
- data.tar.gz.sig +1 -3
- metadata +3 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1802a45773fa3f0e4662926ae1067ae729e81b88201437f7e4bf9705ceac5ec
|
4
|
+
data.tar.gz: c0dedfb32ced019e2105b4434816610598f5f7b560ffab7353144955b9f4a9f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3c3eb84c0c800517b515defb1b8717f1f940d4c582cb63baf5f7689df9aeb47d63a956f997ba64238301f1db88447cffff972a23faa3fad0026b37b3a0b872b
|
7
|
+
data.tar.gz: 9305b383f0f617186e27411fd153a0aa65b430ae96264c70bc3cc6b9ba99e4d9da77a4e817a9cb2e0870be2c057ed17249a6fad5c1fdd4985edb94b3b4232d3e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/History.md
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
# Release History for presentability
|
2
2
|
|
3
3
|
---
|
4
|
+
## v0.5.0 [2023-11-06] Michael Granger <ged@faeriemud.org>
|
5
|
+
|
6
|
+
Improvements:
|
7
|
+
|
8
|
+
- Add recursive presentation
|
9
|
+
- Allow presenters to be defined for objects with no instance variables
|
10
|
+
|
11
|
+
|
4
12
|
## v0.4.0 [2023-02-02] Michael Granger <ged@faeriemud.org>
|
5
13
|
|
6
14
|
Improvements:
|
@@ -57,6 +57,14 @@ class Presentability::Presenter
|
|
57
57
|
end
|
58
58
|
|
59
59
|
|
60
|
+
### Set up an exposure of a collection with the given +name+. This means it will
|
61
|
+
### have the :in_collection option set by default.
|
62
|
+
def self::expose_collection( name, **options, &block )
|
63
|
+
options = options.merge( unless: :in_collection )
|
64
|
+
self.expose( name, **options, &block )
|
65
|
+
end
|
66
|
+
|
67
|
+
|
60
68
|
### Generate the body an exposure method that delegates to a method with the
|
61
69
|
### same +name+ on its subject.
|
62
70
|
def self::generate_expose_method( name, **options )
|
@@ -107,12 +115,14 @@ class Presentability::Presenter
|
|
107
115
|
|
108
116
|
|
109
117
|
### Apply the exposures to the subject and return the result.
|
110
|
-
def apply
|
118
|
+
def apply( presenters )
|
111
119
|
result = self.empty_representation
|
112
120
|
|
113
121
|
self.class.exposures.each do |name, exposure_options|
|
114
122
|
next if self.skip_exposure?( name )
|
115
123
|
value = self.method( name ).call
|
124
|
+
value = presenters.present( value, **exposure_options ) unless
|
125
|
+
value.is_a?( result.class )
|
116
126
|
key = exposure_options.key?( :as ) ? exposure_options[:as] : name
|
117
127
|
result[ key.to_sym ] = value
|
118
128
|
end
|
data/lib/presentability.rb
CHANGED
@@ -9,7 +9,7 @@ module Presentability
|
|
9
9
|
|
10
10
|
|
11
11
|
# Package version
|
12
|
-
VERSION = '0.
|
12
|
+
VERSION = '0.5.0'
|
13
13
|
|
14
14
|
|
15
15
|
# Automatically load subordinate components
|
@@ -41,8 +41,15 @@ module Presentability
|
|
41
41
|
### Return a representation of the +object+ by applying a declared presentation.
|
42
42
|
def present( object, **options )
|
43
43
|
representation = self.present_by_class( object, **options ) ||
|
44
|
-
self.present_by_classname( object, **options )
|
45
|
-
|
44
|
+
self.present_by_classname( object, **options )
|
45
|
+
|
46
|
+
unless representation
|
47
|
+
if object.instance_variables.empty?
|
48
|
+
return object
|
49
|
+
else
|
50
|
+
raise NoMethodError, "no presenter found for %p" % [ object ]
|
51
|
+
end
|
52
|
+
end
|
46
53
|
|
47
54
|
return representation
|
48
55
|
end
|
@@ -66,7 +73,7 @@ module Presentability
|
|
66
73
|
presenter_class = self.presenters[ object.class ] or return nil
|
67
74
|
presenter = presenter_class.new( object, **presentation_options )
|
68
75
|
|
69
|
-
return presenter.apply
|
76
|
+
return presenter.apply( self )
|
70
77
|
end
|
71
78
|
|
72
79
|
|
@@ -77,7 +84,7 @@ module Presentability
|
|
77
84
|
presenter_class = self.presenters[ classname ] or return nil
|
78
85
|
presenter = presenter_class.new( object, **presentation_options )
|
79
86
|
|
80
|
-
return presenter.apply
|
87
|
+
return presenter.apply( self )
|
81
88
|
end
|
82
89
|
|
83
90
|
end # module Presentability
|
@@ -9,7 +9,18 @@ require 'presentability/presenter'
|
|
9
9
|
RSpec.describe( Presentability::Presenter ) do
|
10
10
|
|
11
11
|
let( :presenter_subject ) do
|
12
|
-
OpenStruct.new(
|
12
|
+
OpenStruct.new(
|
13
|
+
country: 'Philippines',
|
14
|
+
export: 'Copper',
|
15
|
+
flower: 'Sampaguita',
|
16
|
+
cities: ['Quezon City', 'Cagayan de Oro', 'Roxas']
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
let( :presenters ) do
|
21
|
+
mod = Module.new
|
22
|
+
mod.extend( Presentability )
|
23
|
+
return mod
|
13
24
|
end
|
14
25
|
|
15
26
|
|
@@ -27,7 +38,7 @@ RSpec.describe( Presentability::Presenter ) do
|
|
27
38
|
|
28
39
|
it "can be created with just a subject" do
|
29
40
|
presenter = subclass.new( presenter_subject )
|
30
|
-
expect( presenter.apply ).to eq( {} )
|
41
|
+
expect( presenter.apply(presenters) ).to eq( {} )
|
31
42
|
end
|
32
43
|
|
33
44
|
|
@@ -35,7 +46,7 @@ RSpec.describe( Presentability::Presenter ) do
|
|
35
46
|
subclass.expose( :country )
|
36
47
|
presenter = subclass.new( presenter_subject )
|
37
48
|
|
38
|
-
expect( presenter.apply ).to eq({ country: 'Philippines' })
|
49
|
+
expect( presenter.apply(presenters) ).to eq({ country: 'Philippines' })
|
39
50
|
end
|
40
51
|
|
41
52
|
|
@@ -47,9 +58,9 @@ RSpec.describe( Presentability::Presenter ) do
|
|
47
58
|
financial_presenter = subclass.new( presenter_subject, financial: true )
|
48
59
|
cultural_presenter = subclass.new( presenter_subject, financial: false )
|
49
60
|
|
50
|
-
expect( financial_presenter.apply ).
|
61
|
+
expect( financial_presenter.apply(presenters) ).
|
51
62
|
to eq({ country: 'Philippines', export: 'Copper' })
|
52
|
-
expect( cultural_presenter.apply ).
|
63
|
+
expect( cultural_presenter.apply(presenters) ).
|
53
64
|
to eq({ country: 'Philippines', flower: 'Sampaguita' })
|
54
65
|
end
|
55
66
|
|
@@ -86,6 +97,14 @@ RSpec.describe( Presentability::Presenter ) do
|
|
86
97
|
end
|
87
98
|
|
88
99
|
|
100
|
+
it "can expose an attribute as a collection" do
|
101
|
+
subclass.expose( :country )
|
102
|
+
subclass.expose_collection( :cities )
|
103
|
+
|
104
|
+
expect( subclass.exposures[:cities] ).to include( unless: :in_collection )
|
105
|
+
end
|
106
|
+
|
107
|
+
|
89
108
|
it "has useful #inspect output" do
|
90
109
|
presenter = subclass.new( presenter_subject )
|
91
110
|
expect( presenter.inspect ).to match( /Presentability::Presenter\S+ for /i )
|
data/spec/presentability_spec.rb
CHANGED
@@ -43,6 +43,23 @@ RSpec.describe Presentability do
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
let( :complex_entity_class ) do
|
47
|
+
Class.new do
|
48
|
+
def self::name
|
49
|
+
return 'Acme::Pair'
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize( user:, entity:, overridden: false, locked: true )
|
53
|
+
@user = user
|
54
|
+
@entity = entity
|
55
|
+
@overridden = overridden
|
56
|
+
@locked = locked
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_accessor :user, :entity, :overridden, :locked
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
46
63
|
let( :entity_instance ) { entity_class.new }
|
47
64
|
let( :other_entity_instance ) do
|
48
65
|
other_entity_class.new(
|
@@ -52,6 +69,9 @@ RSpec.describe Presentability do
|
|
52
69
|
Faker::Internet.password
|
53
70
|
)
|
54
71
|
end
|
72
|
+
let( :complex_entity_instance ) do
|
73
|
+
complex_entity_class.new( user: other_entity_instance, entity: entity_instance )
|
74
|
+
end
|
55
75
|
|
56
76
|
|
57
77
|
describe "when used to extend a module" do
|
@@ -162,6 +182,42 @@ RSpec.describe Presentability do
|
|
162
182
|
end
|
163
183
|
|
164
184
|
|
185
|
+
it "doesn't try to present objects with no instance variables by default" do
|
186
|
+
object = 'a string'
|
187
|
+
expect( extended_module.present(object) ).to be( object )
|
188
|
+
|
189
|
+
object = 8
|
190
|
+
expect( extended_module.present(object) ).to be( object )
|
191
|
+
|
192
|
+
object = :a_symbol
|
193
|
+
expect( extended_module.present(object) ).to be( object )
|
194
|
+
|
195
|
+
object = Time.now
|
196
|
+
expect( extended_module.present(object) ).to be( object )
|
197
|
+
|
198
|
+
object = %[an array of strings]
|
199
|
+
expect( extended_module.present(object) ).to be( object )
|
200
|
+
|
201
|
+
object = Object.new
|
202
|
+
expect( extended_module.present(object) ).to be( object )
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
it "allows presenters to be defined for objects with no instance variables" do
|
207
|
+
extended_module.presenter_for( Time ) do
|
208
|
+
expose :sec
|
209
|
+
expose :usec
|
210
|
+
end
|
211
|
+
|
212
|
+
object = Time.at( 1699287281.336554 )
|
213
|
+
|
214
|
+
expect( extended_module.present(object) ).to eq({
|
215
|
+
sec: object.sec,
|
216
|
+
usec: object.usec
|
217
|
+
})
|
218
|
+
end
|
219
|
+
|
220
|
+
|
165
221
|
it "errors usefully if asked to present an object it knows nothing about" do
|
166
222
|
expect {
|
167
223
|
extended_module.present( entity_instance )
|
@@ -230,7 +286,7 @@ RSpec.describe Presentability do
|
|
230
286
|
end
|
231
287
|
|
232
288
|
|
233
|
-
it "handles a
|
289
|
+
it "handles a heterogeneous collection" do
|
234
290
|
extended_module.presenter_for( entity_class ) do
|
235
291
|
expose :foo
|
236
292
|
expose :bar
|
@@ -307,6 +363,43 @@ RSpec.describe Presentability do
|
|
307
363
|
|
308
364
|
end
|
309
365
|
|
366
|
+
|
367
|
+
describe "and used to present a complex object" do
|
368
|
+
|
369
|
+
it "uses registered presenters for sub-objects" do
|
370
|
+
extended_module.presenter_for( entity_class ) do
|
371
|
+
expose :foo
|
372
|
+
expose :bar
|
373
|
+
end
|
374
|
+
extended_module.presenter_for( other_entity_class ) do
|
375
|
+
expose :firstname
|
376
|
+
expose :lastname
|
377
|
+
expose :email
|
378
|
+
end
|
379
|
+
extended_module.presenter_for( complex_entity_class ) do
|
380
|
+
expose :user
|
381
|
+
expose :entity
|
382
|
+
expose :locked
|
383
|
+
end
|
384
|
+
|
385
|
+
result = extended_module.present( complex_entity_instance )
|
386
|
+
|
387
|
+
expect( result ).to eq({
|
388
|
+
user: {
|
389
|
+
firstname: other_entity_instance.firstname,
|
390
|
+
lastname: other_entity_instance.lastname,
|
391
|
+
email: other_entity_instance.email,
|
392
|
+
},
|
393
|
+
entity: {
|
394
|
+
foo: entity_instance.foo,
|
395
|
+
bar: entity_instance.bar
|
396
|
+
},
|
397
|
+
locked: complex_entity_instance.locked,
|
398
|
+
})
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
|
310
403
|
end
|
311
404
|
|
312
405
|
end
|
data.tar.gz.sig
CHANGED
@@ -1,3 +1 @@
|
|
1
|
-
"�
|
2
|
-
j{�����:g�#�O��f��?�;:TK��dF�J�ՂaS�] ��_;����[�Ս��Z�-B����
|
3
|
-
շ�`���%`�_�Ԃ8lqM�|Y� ����3��b? 3���Dixŗ����B�{�n��+��9��vas�� :�0��1�O�sM7�9�*+N��A�����!�T�#C�џ5nd�a�g�]:����j��QV��*�_�#Q�d��}o5���a;iIO���!�j�8�
|
1
|
+
�B6s��m�{����Μ��'�*'7�tD�!�&CI&��57|�Me�aU®��|!��@~9�"z-��Xz�4[(�M������^Z��������s&�ˀ��|�]��C��fʰ�%_�ߦ�"rz4ER��l�݃G�T�<"��E���滌6ڠ��#J]';kh�9e���V�R=.�`:�d�T��>z�k5m�/�h��ak�FV3�!ʢ�`�w�rued-���~��ן��(=��H*���8j�AU��_���Tw�i�v���G�.��dDW����˵����E���L-�7���_�Ӷ���-���}���_�}�Hb"��4d%WR�.,д����S��vt�������J���KR�r��
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: presentability
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Granger
|
@@ -33,7 +33,7 @@ cert_chain:
|
|
33
33
|
WmEdESlpWhzFECctefIF2lsN9vaOuof57RM77t2otrtcscDtNarIqjZsIyqtDvtL
|
34
34
|
DttITiit0Vwz7bY0
|
35
35
|
-----END CERTIFICATE-----
|
36
|
-
date: 2023-
|
36
|
+
date: 2023-11-06 00:00:00.000000000 Z
|
37
37
|
dependencies:
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: loggability
|
@@ -134,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
134
|
- !ruby/object:Gem::Version
|
135
135
|
version: '0'
|
136
136
|
requirements: []
|
137
|
-
rubygems_version: 3.4.
|
137
|
+
rubygems_version: 3.4.21
|
138
138
|
signing_key:
|
139
139
|
specification_version: 4
|
140
140
|
summary: Facade-based presenters with minimal assumptions.
|
metadata.gz.sig
CHANGED
Binary file
|