u-struct 0.11.0 → 0.12.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: 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