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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67ea66c2c975ecb5064c9ddb9c0be1a203b0e9e9944dfc3f3821f07e360fc726
4
- data.tar.gz: c13fdaccf960dc87d3feaed73e2a22a96ad805aed83cadbadeeee9cc60e80e49
3
+ metadata.gz: c1802a45773fa3f0e4662926ae1067ae729e81b88201437f7e4bf9705ceac5ec
4
+ data.tar.gz: c0dedfb32ced019e2105b4434816610598f5f7b560ffab7353144955b9f4a9f3
5
5
  SHA512:
6
- metadata.gz: 550a710c7ae589dfc6ebb1ed801508c8e2d347dbd37e1cdd19e3f3699b5b203ad38b7f2dc6a917e71d3e3dd3711e4eefe8c01594a1f4d92f10b120c4fe1e0a82
7
- data.tar.gz: 07d9674d3ad188b6b19923153accddf6175bf61d238d79867b7237a34a10595e066e10f4d37cc702bbcca8b7dcf65fd7df75ad0eb2fa0c4557716be3a370090b
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
@@ -9,7 +9,7 @@ module Presentability
9
9
 
10
10
 
11
11
  # Package version
12
- VERSION = '0.4.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 ) or
45
- raise NoMethodError, "no presenter found for %p" % [ object ]
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( country: 'Philippines', export: 'Copper', flower: 'Sampaguita' )
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 )
@@ -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 hetergeneous collection" do
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
- "�Y�i��$ua_ZV�������j�KoVp
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-��Xz4[(�M������^Z��������s &�ˀ��|�]��C��fʰ�%_�ߦ� "rz4ER��l�݃G�T�<"��E���滌6ڠ��#J]';kh�9e���V�R=.�`:�d�T��>z�k5 m�/�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.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-02-02 00:00:00.000000000 Z
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.6
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