u-struct 0.11.0 → 0.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0150b18cb61a38d042a0ad2f22c9588c7269c8362a708a8ebf156cc927a312dd
4
- data.tar.gz: 0fc97adb461433c7382ee524d3a58c4d134624013c4afa974cade30b46f09109
3
+ metadata.gz: 7788d4a3412a447721c8d375a1844b9f000816ade2780925835e2a6ca222329c
4
+ data.tar.gz: 938d1c98f2f495aeca842a29a352e62a5f6565e96d7aed9ad01f27e8cadd08a8
5
5
  SHA512:
6
- metadata.gz: 708cbec302ef69ec3d6f465ebca2d07eff62ff1a3746b4e7034b21a571e693b27b9e4dad1ada934f4ac37f2565212cc83f111d384eb84573209578aab77beac6
7
- data.tar.gz: cec5b4750bcc98d9955fbe27bcca952b2139c36dd90d6f6c98fd3b107f9ef577fdb2e72f34c78871e15d608cdab06c034427efeab67caa5bc2c869bbaeee8026
6
+ metadata.gz: a9c449b61842f114a0d90ce5b8c69d07c33599727cfb26e15a9d1455f79f7cd308d15b3d7d85f508a30c8b6717bf182b5a00dd960fb814d9cc648823fc882f01
7
+ data.tar.gz: b201b72d06fde813dfca89ff231a742c1d6fba92883133c4f12419e924830238c507d85f6d8e2e06c303033d3812df42e147acabe7742d5bfbc360163c739d34
data/CHANGELOG.md CHANGED
@@ -1,45 +1,496 @@
1
+ # Changelog <!-- omit in toc -->
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ - [[Unreleased]](#unreleased)
9
+ - [[0.12.0] - 2021-12-22](#0120---2021-12-22)
10
+ - [Added](#added)
11
+ - [[0.11.0] - 2021-12-19](#0110---2021-12-19)
12
+ - [Added](#added-1)
13
+ - [[0.10.0] - 2021-12-15](#0100---2021-12-15)
14
+ - [Changed](#changed)
15
+ - [[0.9.0] - 2021-12-14](#090---2021-12-14)
16
+ - [Added](#added-2)
17
+ - [Changed](#changed-1)
18
+ - [[0.8.0] - 2021-12-05](#080---2021-12-05)
19
+ - [Added](#added-3)
20
+ - [[0.7.0] - 2021-12-04](#070---2021-12-04)
21
+ - [Added](#added-4)
22
+ - [Changed](#changed-2)
23
+ - [[0.6.0] - 2021-12-03](#060---2021-12-03)
24
+ - [Added](#added-5)
25
+ - [[0.5.0] - 2021-12-02](#050---2021-12-02)
26
+ - [Added](#added-6)
27
+ - [[0.4.0] - 2021-12-02](#040---2021-12-02)
28
+ - [Added](#added-7)
29
+ - [[0.3.1] - 2021-12-02](#031---2021-12-02)
30
+ - [Fixed](#fixed)
31
+ - [[0.3.0] - 2021-12-02](#030---2021-12-02)
32
+ - [Added](#added-8)
33
+ - [[0.2.0] - 2021-12-02](#020---2021-12-02)
34
+ - [Added](#added-9)
35
+ - [[0.1.0] - 2021-12-02](#010---2021-12-02)
36
+ - [Added](#added-10)
37
+
1
38
  ## [Unreleased]
2
39
 
40
+ [⬆️ &nbsp;Back to Top](#changelog-)
41
+
42
+ ## [0.12.0] - 2021-12-22
43
+
44
+ ### Added
45
+
46
+ - Add `Micro::Struct.instance` to create a struct instance from a given hash.
47
+ This could be useful to create constants or a singleton value.
48
+
49
+ ```ruby
50
+ person1 = Micro::Struct.instance(first_name: 'Rodrigo', last_name: 'Serradura')
51
+ # => #<struct first_name="Rodrigo", last_name="Serradura">
52
+
53
+ person1.first_name = 'John'
54
+
55
+ person1.first_name # => "John"
56
+ ```
57
+
58
+ You can use the instance method after defining some struct feature.
59
+
60
+ ```ruby
61
+ person2 = Micro::Struct.with(:readonly).instance(first_name: 'Rodrigo', last_name: 'Serradura')
62
+ # => #<struct first_name="Rodrigo", last_name="Serradura">
63
+
64
+ person2.first_name = 'John'
65
+ # NoMethodError (private method `first_name=' called for #<struct first_name="Rodrigo", last_name="Serradura">)
66
+ ```
67
+ You can use pass a block to define some custom behavior to the struct instance.
68
+
69
+ ```ruby
70
+ person3 = Micro::Struct.instance(first_name: 'Rodrigo', last_name: 'Serradura') do
71
+ def name
72
+ "#{first_name} #{last_name}"
73
+ end
74
+ end
75
+
76
+ person4 = Micro::Struct.with(:readonly).instance(first_name: 'Rodrigo', last_name: 'Serradura') do
77
+ def name
78
+ "#{first_name} #{last_name}"
79
+ end
80
+ end
81
+
82
+ person3.name # => "Rodrigo Serradura"
83
+ person4.name # => "Rodrigo Serradura"
84
+ ```
85
+
86
+ - Add `Micro::Struct.with(:exposed_features)` to expose the struct's configured features.
87
+ Via the methods: `.features` and `.__features__`.
88
+
89
+ ```ruby
90
+ Person = Micro::Struct.with(:exposed_features, :readonly, :to_proc).new(:name)
91
+
92
+ Person.features
93
+ # => #<struct Micro::Struct::Features::Exposed
94
+ # names=[:readonly, :to_proc],
95
+ # options={:to_ary=>false, :to_hash=>false, :to_proc=>true, :readonly=>true, :instance_copy=>false}>
96
+
97
+ Person.__features__.equal?(Person.features) # `.__features__` is an alias of `.features` method
98
+
99
+ Person.features.names # => [:readonly, :to_proc]
100
+ Person.features.options # => {:to_ary=>false, :to_hash=>false, :to_proc=>true, :readonly=>true, :instance_copy=>false}
101
+
102
+ Person.features.option?(:to_proc) # => true
103
+ Person.features.option?(:readonly) # => true
104
+
105
+ Person.features.options?(:to_proc) # => true
106
+ Person.features.options?(:readonly) # => true
107
+
108
+ Person.features.options?(:to_proc, :readonly) # => true
109
+ Person.features.options?(:to_ary, :readonly) # => false
110
+ ```
111
+
112
+ [⬆️ &nbsp;Back to Top](#changelog-)
113
+
114
+ ## [0.11.0] - 2021-12-19
115
+
116
+ ### Added
117
+
118
+ - Reduce the required Ruby version to `>= 2.2.0`.
119
+ - Set up a CI with Github actions.
120
+ - Test the codebase against the Ruby versions: `2.2`, `2.3`, `2.4`, `2.5`, `2.6`, `2.7`, `3.0` and `3.1.0-preview1`.
121
+
122
+ [⬆️ &nbsp;Back to Top](#changelog-)
123
+
3
124
  ## [0.10.0] - 2021-12-15
4
125
 
5
- - To-do
126
+ ### Changed
127
+
128
+ - Make `Micro::Struct.new` return a Ruby struct instead of a module.
129
+
130
+ ```ruby
131
+ module RGB
132
+ Number = ::Struct.new(:value) { def to_s; '%02x' % value; end }
133
+
134
+ Color = Micro::Struct.new(:red, :green, :blue) do
135
+ def self.new(r, g, b)
136
+ __new__(
137
+ red: Number.new(r),
138
+ green: Number.new(g),
139
+ blue: Number.new(b),
140
+ )
141
+ end
142
+
143
+ def to_hex
144
+ "##{red}#{green}#{blue}"
145
+ end
146
+ end
147
+ end
148
+
149
+ rgb_color = RGB::Color.new(1,5,255)
150
+ # => #<struct RGB::Color red=#<struct RGB::Number value=1>, green=#<struct RGB::Number value=5>, blue=#<struct RGB::Number value=255>>
151
+
152
+ rgb_color.to_hex
153
+ # => "#0105ff"
154
+ ```
155
+
156
+ [⬆️ &nbsp;Back to Top](#changelog-)
6
157
 
7
158
  ## [0.9.0] - 2021-12-14
8
159
 
9
- - To-do
160
+ ### Added
161
+
162
+ - Add `__new__` method and make `.new` its alias. You can use `__new__` when overwriting the module's `new`.
163
+
164
+ ```ruby
165
+ module RGB
166
+ Number = ::Struct.new(:value) { def to_s; '%02x' % value; end }
167
+
168
+ Color = Micro::Struct.new(:red, :green, :blue) do
169
+ def to_hex
170
+ "##{red}#{green}#{blue}"
171
+ end
172
+ end
173
+
174
+ module Color
175
+ def self.new(r, g, b)
176
+ __new__(
177
+ red: Number.new(r),
178
+ green: Number.new(g),
179
+ blue: Number.new(b),
180
+ )
181
+ end
182
+ end
183
+ end
184
+
185
+ rgb_color = RGB::Color.new(1,5,255)
186
+ # => #<struct RGB::Color::Struct red=#<struct RGB::Number value=1>, green=#<struct RGB::Number value=5>, blue=#<struct RGB::Number value=255>>
187
+
188
+ rgb_color.to_hex
189
+ # => "#0105ff"
190
+ ```
191
+
192
+ ### Changed
193
+
194
+ - Change `:readonly` feature, now it doesn't require the `:instance_copy` by default.
195
+ So, If you want both features, you will need to declare them together.
196
+
197
+ ```ruby
198
+ Person = Micro::Struct.with(:readonly).new(:name)
199
+ Persona = Micro::Struct.with(:readonly, :instance_copy).new(:name)
200
+
201
+ person = Person.new(name: 'Rodrigo')
202
+ persona = Persona.new(name: 'Serradura')
203
+
204
+ person.respond_to?(:name=) # false
205
+ persona.respond_to?(:name=) # false
206
+
207
+ person.respond_to?(:with) # false
208
+ persona.respond_to?(:with) # true
209
+ ```
210
+
211
+ - Change `:to_ary` to invoke the `#to_a` method instead of defining it as an alias.
212
+ - Change `:to_hash` to invoke the `#to_h` method instead of defining it as an alias.
213
+
214
+ ```ruby
215
+ module RGB
216
+ Number = ::Struct.new(:value) { def to_s; '%02x' % value; end }
217
+
218
+ Color = Micro::Struct.with(:readonly, :to_ary, :to_hash).new(:red, :green, :blue) do
219
+ def initialize(r, g, b)
220
+ super(Number.new(r), Number.new(g), Number.new(b))
221
+ end
222
+
223
+ def to_hex
224
+ "##{red}#{green}#{blue}"
225
+ end
226
+
227
+ def to_a
228
+ [red, green, blue].map(&:value)
229
+ end
230
+
231
+ def to_h
232
+ { r: red.value, g: green.value, b: blue.value }
233
+ end
234
+ end
235
+ end
236
+
237
+ rgb_color = RGB::Color.new(red: 1, green: 5, blue: 255)
238
+ # => #<struct RGB::Color::Struct red=#<struct RGB::Number value=1>, green=#<struct RGB::Number value=5>, blue=#<struct RGB::Number value=255>>
239
+
240
+ rgb_color.to_hex # => "#0105ff"
241
+ rgb_color.to_ary # => [1, 5, 255]
242
+ rgb_color.to_hash # => {:r=>1, :g=>5, :b=>255}
243
+ ```
244
+
245
+ [⬆️ &nbsp;Back to Top](#changelog-)
10
246
 
11
247
  ## [0.8.0] - 2021-12-05
12
248
 
13
- - To-do
249
+ ### Added
250
+
251
+ - Add `.===` to the module, it delegates the calling to its struct.
252
+
253
+ ```ruby
254
+ Person = Micro::Struct.new(:name)
255
+
256
+ person = Person.new(name: 'Rodrigo Serradura')
257
+ # => #<struct Person::Struct name="Rodrigo Serradura">
258
+
259
+ Person === person
260
+ # => true
261
+ ```
262
+
263
+ [⬆️ &nbsp;Back to Top](#changelog-)
14
264
 
15
265
  ## [0.7.0] - 2021-12-04
16
266
 
17
- - To-do
267
+ ### Added
268
+
269
+ - Add `required:` option to define required struct members.
270
+
271
+ ```ruby
272
+ # All of the alternatives have the same equivalence.
273
+
274
+ Person = Micro::Struct.new(:first_name, :last_name)
275
+
276
+ Person = Micro::Struct.new(required: [:first_name, :last_name])
277
+
278
+ Person = Micro::Struct.new(:first_name, required: :last_name)
279
+ ```
280
+
281
+ ### Changed
282
+
283
+ - Remove the `_` from the `optional:` option.
284
+
285
+ ```ruby
286
+ Person = Micro::Struct.new(
287
+ required: [:first_name, :last_name],
288
+ optional: :age
289
+ )
290
+ ```
291
+
292
+ [⬆️ &nbsp;Back to Top](#changelog-)
18
293
 
19
294
  ## [0.6.0] - 2021-12-03
20
295
 
21
- - To-do
296
+ ### Added
297
+
298
+ - Add the capability to create a struct with optional members.
299
+
300
+ ```ruby
301
+ Person = Micro::Struct.new(:first_name, _optional: :last_name)
302
+
303
+ Person.new
304
+ # ArgumentError (missing keyword: :first_name)
305
+
306
+ Person.new(first_name: 'Rodrigo')
307
+ # => #<struct Person::Struct first_name="Rodrigo", last_name=nil>
308
+
309
+ # --
310
+
311
+ Persona = Micro::Struct.new(_optional: [:first_name, :last_name])
312
+
313
+ Persona.new
314
+ # => #<struct Persona::Struct first_name=nil, last_name=nil>
315
+ ```
316
+
317
+ [⬆️ &nbsp;Back to Top](#changelog-)
318
+
319
+ ## [0.5.0] - 2021-12-02
320
+
321
+ ### Added
322
+
323
+ - Add new feature `:instance_copy`. It instantiates a struct of the same kind from its current state.
324
+
325
+ ```ruby
326
+ Person = Micro::Struct.with(:instance_copy).new(:first_name, :last_name)
327
+
328
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
329
+ # => #<struct Person::Struct first_name="Rodrigo", last_name="Serradura">
330
+
331
+ person.first_name = 'John'
332
+ # => "John"
333
+
334
+ person.inspect
335
+ # => #<struct Person::Struct first_name="John", last_name="Serradura">
336
+
337
+ new_person = person.with(last_name: 'Doe')
338
+ # => #<struct Person::Struct first_name="John", last_name="Doe">
339
+
340
+ person === new_person # => false
341
+ person.equal?(new_person) # => false
342
+
343
+ person.last_name # => "Serradura"
344
+ new_person.last_name # => "Doe"
345
+ ```
346
+
347
+ - Add new feature `:readonly`. It sets members' writers private and requires the `:instance_copy` feature.
348
+
349
+ ```ruby
350
+ Person = Micro::Struct.with(:readonly).new(:name)
351
+
352
+ person = Person.new(name: 'Rodrigo Serradura')
353
+ # => #<struct Person::Struct name="Rodrigo Serradura">
354
+
355
+ person.name = 'John Doe'
356
+ # NoMethodError (private method `name=' called for #<struct Person::Struct name="Rodrigo Serradura">)
357
+
358
+ new_person = person.with(name: 'John Doe')
359
+ # => #<struct Person::Struct name="John Doe">
360
+
361
+ person === new_person # => false
362
+ person.equal?(new_person) # => false
363
+
364
+ person.name # => "Rodrigo Serradura"
365
+ new_person.name # => "John Doe"
366
+ ```
367
+
368
+ [⬆️ &nbsp;Back to Top](#changelog-)
369
+
370
+ ## [0.4.0] - 2021-12-02
371
+
372
+ ### Added
22
373
 
23
- ## [0.5.0] - 2021-12-03
374
+ - Add `.members` to the module, it delegates the calling to its struct.
24
375
 
25
- - To-do
376
+ ```ruby
377
+ Person = Micro::Struct.new(:first_name, :last_name)
26
378
 
27
- ## [0.4.0] - 2021-12-03
379
+ Person.members # => [:first_name, :last_name]
380
+ ```
28
381
 
29
- - To-do
382
+ - Add `Micro::Struct.with()` to enable or disable the creation of structs with custom features.
383
+ So now, you can create the structs with one, some, or all features. They are: `to_ary`, `to_hash`, `to_proc`.
384
+
385
+ ```ruby
386
+ Person = Micro::Struct.with(:to_ary).new(:name)
387
+
388
+ person = Person.new(name: 'Rodrigo')
389
+ # => #<struct Person::Struct name="Rodrigo">
390
+
391
+ person.respond_to?(:to_ary) # => true
392
+ person.respond_to?(:to_hash) # => false
393
+
394
+ Person.respond_to?(:to_proc) # => false
395
+ ```
396
+
397
+ [⬆️ &nbsp;Back to Top](#changelog-)
30
398
 
31
399
  ## [0.3.1] - 2021-12-02
32
400
 
33
- - To-do
401
+ ### Fixed
402
+
403
+ - Fix the spec.files config of `u-struct.gemspec`.
404
+
405
+ [⬆️ &nbsp;Back to Top](#changelog-)
34
406
 
35
407
  ## [0.3.0] - 2021-12-02
36
408
 
37
- - To-do
409
+ ### Added
410
+
411
+ - Add `lib/u-struct.rb` to allow the bundler to require the gem in an automatic way.
412
+
413
+ [⬆️ &nbsp;Back to Top](#changelog-)
38
414
 
39
415
  ## [0.2.0] - 2021-12-02
40
416
 
41
- - To-do
417
+ ### Added
418
+
419
+ - Add `to_hash` as an alias of Struct's `to_h`.
420
+
421
+ ```ruby
422
+ Person = Micro::Struct.new(:first_name, :last_name)
423
+
424
+ def print_first_and_last_name(first_name:, last_name:)
425
+ puts "#{first_name} #{last_name}"
426
+ end
427
+
428
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
429
+
430
+ print_first_and_last_name(**person) # Rodrigo Serradura
431
+ ```
432
+
433
+ [⬆️ &nbsp;Back to Top](#changelog-)
42
434
 
43
435
  ## [0.1.0] - 2021-12-02
44
436
 
45
- - Initial release
437
+ ### Added
438
+
439
+ - Create a module containing a Ruby struct with some custom features.
440
+ - The module's `.new` method receives the struct arguments as keyword arguments.
441
+ - The module's `.new` can receive a block as a regular `Struct` to add some custom behavior.
442
+ - The module's `to_proc` can instantiate the struct by receiving a hash.
443
+ - The module's struct has its initializer set up as private.
444
+ - Add `to_ary` as an alias of module's struct `to_a`.
445
+
446
+ ```ruby
447
+ Person = Micro::Struct.new(:first_name, :last_name) do
448
+ def name
449
+ "#{first_name} #{last_name}"
450
+ end
451
+ end
452
+
453
+ # == Module's .new ==
454
+
455
+ Person.new
456
+ # ArgumentError (missing keywords: :first_name, :last_name)
457
+
458
+ Person.new(first_name: 'Rodrigo')
459
+ # ArgumentError (missing keyword: :last_name)
460
+
461
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
462
+ # => #<struct Person::Struct first_name="Rodrigo", last_name="Serradura">
463
+
464
+ # == Struct's block - it sets up custom behavior ==
465
+
466
+ person.name # => "Rodrigo Serradura"
467
+
468
+ # == Struct's #to_ary ==
469
+
470
+ first_name, last_name = person
471
+
472
+ p first_name # => "Rodrigo"
473
+ p last_name # => "Serradura"
474
+
475
+ *first_and_last_name = person
476
+
477
+ p first_and_last_name # => ["Rodrigo", "Serradura"]
478
+
479
+ # == Module's .to_proc ==
480
+
481
+ [
482
+ {first_name: 'John', last_name: 'Doe'},
483
+ {first_name: 'Mary', last_name: 'Doe'}
484
+ ].map(&Person)
485
+ # => [
486
+ # #<struct Person::Struct first_name="John", last_name="Doe">,
487
+ # #<struct Person::Struct first_name="Mary", last_name="Doe">
488
+ # ]
489
+
490
+ # == Struct's private initializer ==
491
+
492
+ Person::Struct.new
493
+ # => NoMethodError (private method `new' called for Person::Struct:Class)
494
+ ```
495
+
496
+ [⬆️ &nbsp;Back to Top](#changelog-)
data/README.md CHANGED
@@ -64,13 +64,14 @@ Micro::Struct.new(
64
64
  Micro::Struct.new(:name) {}
65
65
 
66
66
  # Available features (use one, many, or all) to create Structs with a special behavior:
67
- # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
67
+ # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy, :exposed_features)
68
68
 
69
69
  Micro::Struct.with(:to_ary).new(:name)
70
70
  Micro::Struct.with(:to_ary, :to_hash).new(:name)
71
71
  Micro::Struct.with(:to_ary, :to_hash, :to_proc).new(:name)
72
72
  Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly).new(:name)
73
73
  Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy).new(:name)
74
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy, :exposed_features).new(:name)
74
75
 
75
76
  # All of the possible combinations to create a Ruby Struct. ;)
76
77
 
@@ -1,88 +1,107 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Micro::Struct::Factory
4
- module CreateStruct
5
- extend self
3
+ module Micro::Struct
4
+ class Factory
5
+ module CreateStruct
6
+ extend self
6
7
 
7
- def with(members, block, features)
8
- struct = ::Struct.new(*members.required_and_optional)
8
+ def with(members, block, features)
9
+ struct = ::Struct.new(*members.required_and_optional)
9
10
 
10
- ClassScope.def_new(struct, members)
11
- ClassScope.def_to_proc(struct) if features[:to_proc]
12
- ClassScope.def_private_writers(struct) if features[:readonly]
11
+ ClassScope.def_new(struct, members)
12
+
13
+ ClassScope.def_features(struct, features) if features.is_a?(Features::Exposed)
14
+ ClassScope.def_to_proc(struct) if features.option?(:to_proc)
15
+ ClassScope.def_private_writers(struct) if features.option?(:readonly)
13
16
 
14
- InstanceScope.def_with(struct) if features[:instance_copy]
15
- InstanceScope.def_to_ary(struct) if features[:to_ary]
16
- InstanceScope.def_to_hash(struct) if features[:to_hash]
17
+ InstanceScope.def_with(struct) if features.option?(:instance_copy)
18
+ InstanceScope.def_to_ary(struct) if features.option?(:to_ary)
19
+ InstanceScope.def_to_hash(struct) if features.option?(:to_hash)
17
20
 
18
- ClassScope.evaluate(struct, block)
21
+ ClassScope.evaluate(struct, block)
19
22
 
20
- struct
21
- end
22
-
23
- module ClassScope
24
- def self.def_new(struct, members)
25
- # The .new() method will require all required keyword arguments.
26
- # We are doing this because the Struct constructor keyword init option treats everything as optional.
27
- #
28
- struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
29
- class << self
30
- undef_method :new
31
-
32
- def new(#{members.to_eval.keyword_args}) # def new(a:, b:, c: nil) do
33
- instance = allocate # instance = allocate
34
- instance.send(:initialize, #{members.to_eval.positional_args}) # instance.send(:initialize, a, b, c)
35
- instance # instance
36
- end # end
37
-
38
- alias __new__ new
39
- end
40
- RUBY
41
- end
42
-
43
- def self.def_to_proc(struct)
44
- struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
45
- def self.to_proc
46
- ->(hash) { new(**hash) }
47
- end
48
- RUBY
23
+ struct
49
24
  end
50
25
 
51
- def self.def_private_writers(struct)
52
- struct.send(:private, *struct.members.map { |member| "#{member}=" })
26
+ module ClassScope
27
+ def self.def_new(struct, members)
28
+ # The .new() method will require all required keyword arguments.
29
+ # We are doing this because the Struct constructor keyword init option treats everything as optional.
30
+ #
31
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
32
+ class << self
33
+ undef_method :new
34
+
35
+ def new(#{members.to_eval.keyword_args}) # def new(a:, b:, c: nil) do
36
+ instance = allocate # instance = allocate
37
+ instance.send(:initialize, #{members.to_eval.positional_args}) # instance.send(:initialize, a, b, c)
38
+ instance # instance
39
+ end # end
40
+
41
+ alias __new__ new
42
+ end
43
+ RUBY
44
+ end
45
+
46
+ def self.def_features(struct, features)
47
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
48
+ class << self
49
+ attr_accessor :__features__
50
+
51
+ alias features __features__
52
+ end
53
+ RUBY
54
+
55
+ struct.__features__ = features
56
+
57
+ struct.send(:private_class_method, :__features__=)
58
+ end
59
+
60
+ def self.def_to_proc(struct)
61
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
62
+ def self.to_proc
63
+ ->(hash) { new(**hash) }
64
+ end
65
+ RUBY
66
+ end
67
+
68
+ def self.def_private_writers(struct)
69
+ struct.send(:private, :[]=)
70
+ struct.send(:private, *struct.members.map { |member| "#{member}=" })
71
+ end
72
+
73
+ def self.evaluate(struct, block)
74
+ struct.class_eval(&block) if block
75
+ end
53
76
  end
54
77
 
55
- def self.evaluate(struct, block)
56
- struct.class_eval(&block) if block
78
+ module InstanceScope
79
+ def self.def_to_ary(struct)
80
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
81
+ def to_ary
82
+ to_a
83
+ end
84
+ RUBY
85
+ end
86
+
87
+ def self.def_to_hash(struct)
88
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
89
+ def to_hash
90
+ to_h
91
+ end
92
+ RUBY
93
+ end
94
+
95
+ def self.def_with(struct)
96
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
97
+ def with(**members)
98
+ self.class.new(**to_h.merge(members))
99
+ end
100
+ RUBY
101
+ end
57
102
  end
58
103
  end
59
104
 
60
- module InstanceScope
61
- def self.def_to_ary(struct)
62
- struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
63
- def to_ary
64
- to_a
65
- end
66
- RUBY
67
- end
68
-
69
- def self.def_to_hash(struct)
70
- struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
71
- def to_hash
72
- to_h
73
- end
74
- RUBY
75
- end
76
-
77
- def self.def_with(struct)
78
- struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
79
- def with(**members)
80
- self.class.new(**to_h.merge(members))
81
- end
82
- RUBY
83
- end
84
- end
105
+ private_constant :CreateStruct
85
106
  end
86
-
87
- private_constant :CreateStruct
88
107
  end
@@ -6,7 +6,7 @@ module Micro::Struct
6
6
  require_relative 'factory/create_struct'
7
7
 
8
8
  def initialize(features)
9
- @features = Features.require(features)
9
+ @features = Features.config(features)
10
10
  end
11
11
 
12
12
  def new(*required_members, required: nil, optional: nil, &struct_block)
@@ -14,6 +14,10 @@ module Micro::Struct
14
14
 
15
15
  CreateStruct.with(members, struct_block, @features)
16
16
  end
17
+
18
+ def instance(**members, &block)
19
+ new(*members.keys, &block).new(**members)
20
+ end
17
21
  end
18
22
 
19
23
  private_constant :Factory
@@ -2,29 +2,53 @@
2
2
 
3
3
  module Micro::Struct
4
4
  module Features
5
- DISABLED =
6
- { to_ary: false,
7
- to_hash: false,
8
- to_proc: false,
9
- readonly: false,
10
- instance_copy: false }.freeze
11
-
12
- Check = ->(to_ary:, to_hash:, to_proc:, readonly:, instance_copy:) do
13
- { to_ary: to_ary,
14
- to_hash: to_hash,
15
- to_proc: to_proc,
16
- readonly: readonly,
17
- instance_copy: instance_copy }
18
- end
19
-
20
5
  Names = ->(values) do
21
6
  NormalizeNames::AsSymbols.(values, context: 'feature')
22
7
  end
23
8
 
24
- def self.require(values)
25
- to_enable = Names[values].each_with_object({}) { |name, memo| memo[name] = true }
9
+ module Options
10
+ def self.check(to_ary:, to_hash:, to_proc:, readonly:, instance_copy:, exposed_features:)
11
+ { to_ary: to_ary,
12
+ to_hash: to_hash,
13
+ to_proc: to_proc,
14
+ readonly: readonly,
15
+ instance_copy: instance_copy,
16
+ exposed_features: exposed_features }
17
+ end
18
+
19
+ With = ->(bool, names) { names.each_with_object({}) { |name, memo| memo[name] = bool } }
20
+
21
+ DISABLED = With.(false, method(:check).parameters.map(&:last)).freeze
22
+
23
+ def self.from_names(values)
24
+ enabled = With.(true, values)
25
+
26
+ check(**DISABLED.merge(enabled))
27
+ end
28
+ end
29
+
30
+ Config = ::Struct.new(:names, :options) do
31
+ def option?(name)
32
+ options.fetch(name)
33
+ end
34
+
35
+ def options?(*names)
36
+ names.all? { |name| option?(name) }
37
+ end
38
+ end
39
+
40
+ Exposed = Class.new(Config)
41
+
42
+ def self.config(values)
43
+ names = Names[values]
44
+ options = Options.from_names(names)
45
+
46
+ return Config.new(names, options) unless options[:exposed_features]
47
+
48
+ names.delete(:exposed_features)
49
+ options.delete(:exposed_features)
26
50
 
27
- Check.(**DISABLED.merge(to_enable))
51
+ Exposed.new(names.freeze, options.freeze).freeze
28
52
  end
29
53
  end
30
54
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  module Struct
5
- VERSION = '0.11.0'
5
+ VERSION = '0.12.0'
6
6
  end
7
7
  end
data/lib/micro/struct.rb CHANGED
@@ -31,13 +31,14 @@ module Micro
31
31
  # Micro::Struct.new(:name) {}
32
32
  #
33
33
  # Available features (use one, many, or all) to create Structs with a special behavior:
34
- # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
34
+ # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy, :exposed_features)
35
35
  #
36
36
  # Micro::Struct.with(:to_ary).new(:name)
37
37
  # Micro::Struct.with(:to_ary, :to_hash).new(:name)
38
38
  # Micro::Struct.with(:to_ary, :to_hash, :to_proc).new(:name)
39
39
  # Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly).new(:name)
40
40
  # Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy).new(:name)
41
+ # Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy, :exposed_features).new(:name)
41
42
  #
42
43
  # All of the possible combinations to create a Ruby Struct. ;)
43
44
  #
@@ -64,5 +65,9 @@ module Micro
64
65
  def self.with(*features)
65
66
  Factory.new(features)
66
67
  end
68
+
69
+ def self.instance(**members, &block)
70
+ with.instance(**members, &block)
71
+ end
67
72
  end
68
73
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-19 00:00:00.000000000 Z
11
+ date: 2021-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler