presentability 0.4.0 → 0.5.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 +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
|