u-struct 0.5.0 → 0.9.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: 393ba5215b21bf91ef0229de920ca0980c44168fcc0e65d539a0f6b4f6a7caa2
4
- data.tar.gz: e1ad5cfbd6215982aa8f6e5708d4a6f8cea0a046da9ad82568d0ed8136d58629
3
+ metadata.gz: '0765309623bfa9eb720102c66cd30f5ba785bdada60af711f19a03a51a7027bd'
4
+ data.tar.gz: 216dd4920427d682c9e797e5ec0a8631486b5d8c0613391440bd0f94f2778824
5
5
  SHA512:
6
- metadata.gz: 2d86bb3a75737927e5f74d56e16c7760b8c7b9c3dce7642ebdef058122212dd21c38aec04573722842ea081995b5e03c6a094a1548bf63f1ed709762c6e5ad2c
7
- data.tar.gz: 6dee9c1ab9cd6ba5675cf7c1e6cedc128e70fe45aabf52a2b1beb77fb63e9b01c1d70b0775b4393167132371923b5d56a2efed099048e0e3e594b5c199d95ab3
6
+ metadata.gz: ed911103da03769ac8014a618d713b313e20a19a65074ac1a737f2d4cd1717dbdf521d0f7693665d840bdbff145e518afe3e7ef875b1eef55d2555e198bf8d62
7
+ data.tar.gz: 683f94804b10f3ff03ed3f6ecbf4ff40c889dacf8dfd964496e8807974f96cbb611399fa21d95d73881a9c20d4dd0296975528cafc59dfbb20128471149c398a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.9.0] - 2021-12-14
4
+
5
+ - To-do
6
+
7
+ ## [0.8.0] - 2021-12-05
8
+
9
+ - To-do
10
+
11
+ ## [0.7.0] - 2021-12-04
12
+
13
+ - To-do
14
+
15
+ ## [0.6.0] - 2021-12-03
16
+
17
+ - To-do
18
+
3
19
  ## [0.5.0] - 2021-12-03
4
20
 
5
21
  - To-do
data/Gemfile CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in micro-struct.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 13.0"
8
+ gem 'rake', '~> 13.0'
9
9
 
10
- gem "minitest", "~> 5.0"
10
+ gem 'minitest', '~> 5.0'
11
+ gem 'simplecov', '~> 0.21.2'
data/Gemfile.lock CHANGED
@@ -1,21 +1,30 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- u-struct (0.5.0)
4
+ u-struct (0.9.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ docile (1.4.0)
9
10
  minitest (5.14.4)
10
11
  rake (13.0.6)
12
+ simplecov (0.21.2)
13
+ docile (~> 1.1)
14
+ simplecov-html (~> 0.11)
15
+ simplecov_json_formatter (~> 0.1)
16
+ simplecov-html (0.12.3)
17
+ simplecov_json_formatter (0.1.3)
11
18
 
12
19
  PLATFORMS
20
+ ruby
13
21
  x86_64-darwin-19
14
22
 
15
23
  DEPENDENCIES
16
24
  bundler
17
25
  minitest (~> 5.0)
18
26
  rake (~> 13.0)
27
+ simplecov (~> 0.21.2)
19
28
  u-struct!
20
29
 
21
30
  BUNDLED WITH
data/README.md CHANGED
@@ -22,19 +22,58 @@ Or install it yourself as:
22
22
 
23
23
  ```ruby
24
24
  # Like in a regular Struct, you can define one or many attributes.
25
+ # But all of them will be required by default.
26
+
25
27
  Micro::Struct.new(:first_name, :last_name, ...)
26
28
 
29
+ # Use the `optional:` arg if you want some optional attributes.
30
+
31
+ Micro::Struct.new(:first_name, :last_name, optional: :gender)
32
+
33
+ # Using `optional:` to define all attributes are optional.
34
+
35
+ Micro::Struct.new(optional: [:first_name, :last_name])
36
+
37
+ # Use the `required:` arg to define required attributes.
38
+
39
+ Micro::Struct.new(
40
+ required: [:first_name, :last_name],
41
+ optional: [:gender, :age]
42
+ )
43
+
27
44
  # You can also pass a block to define custom methods.
45
+
28
46
  Micro::Struct.new(:name) {}
29
47
 
30
- # Available features (use one, many or all):
31
- # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
48
+ # Available features (use one, many, or all) to create Structs with a special behavior:
49
+ # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
32
50
 
33
51
  Micro::Struct.with(:to_ary).new(:name)
34
52
  Micro::Struct.with(:to_ary, :to_hash).new(:name)
35
53
  Micro::Struct.with(:to_ary, :to_hash, :to_proc).new(:name)
54
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly).new(:name)
55
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy).new(:name)
56
+
57
+ # All of the possible combinations to create a Ruby Struct. ;)
58
+
59
+ Micro::Struct.new(*required)
60
+ Micro::Struct.new(*required) {}
61
+
62
+ Micro::Struct.new(optional: *)
63
+ Micro::Struct.new(optional: *) {}
64
+
65
+ Micro::Struct.new(required: *)
66
+ Micro::Struct.new(required: *) {}
67
+
68
+ Micro::Struct.new(*required, optional: *)
69
+ Micro::Struct.new(*required, optional: *) {}
70
+
71
+ Micro::Struct.new(required: *, optional: *)
72
+ Micro::Struct.new(required: *, optional: *) {}
73
+
74
+ # Any options above can be used by the `.new()` method of the struct creator returned by the `.with()` method.
36
75
 
37
- Micro::Struct.with(...).new(...) {}
76
+ Micro::Struct.with(*features).new(...) {}
38
77
  ```
39
78
 
40
79
  ## Development
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+
8
+ gem 'u-struct', path: '..'
9
+ end
10
+
11
+ Person = Micro::Struct.new(:first_name, :last_name, optional: :age) do
12
+ def name
13
+ "#{first_name} #{last_name}"
14
+ end
15
+ end
16
+
17
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
18
+
19
+ puts format('first_name: %p', person.first_name)
20
+ puts format('last_name: %p', person.last_name)
21
+ puts format('name: %p', person.name)
22
+ puts format('age: %p', person.age)
23
+ puts
24
+
25
+ person.age = rand(18..100)
26
+
27
+ puts format('age: %p', person.age)
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+
8
+ gem 'u-struct', path: '..'
9
+ end
10
+
11
+ Person = Micro::Struct.with(:readonly, :instance_copy).new(
12
+ required: [:first_name, :last_name],
13
+ optional: [:age]
14
+ ) do
15
+ def name
16
+ "#{first_name} #{last_name}"
17
+ end
18
+ end
19
+
20
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
21
+
22
+ puts format('first_name: %p', person.first_name)
23
+ puts format('last_name: %p', person.last_name)
24
+ puts format('name: %p', person.name)
25
+ puts format('age: %p', person.age)
26
+ puts
27
+
28
+ rand_age = rand(18..100)
29
+
30
+ new_person = person.with(age: rand_age)
31
+
32
+ puts new_person.equal?(person)
33
+
34
+ puts format('age: %p', person.age)
35
+ puts format('age: %p', new_person.age)
36
+
37
+ person.age = rand_age
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RGB
4
+ Color = Micro::Struct.with(:readonly, :to_ary).new(:red, :green, :blue) do
5
+ def to_a
6
+ super.map(&:value)
7
+ end
8
+
9
+ def to_hex
10
+ "##{red}#{green}#{blue}"
11
+ end
12
+ end
13
+
14
+ module Color
15
+ def self.new(r:, g:, b:)
16
+ __new__(
17
+ red: Number.new(r, label: 'r'),
18
+ green: Number.new(g, label: 'g'),
19
+ blue: Number.new(b, label: 'b')
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RGB
4
+ Number = Micro::Struct.with(:readonly).new(:value) do
5
+ def to_s
6
+ value.to_s(16)
7
+ end
8
+ end
9
+
10
+ module Number
11
+ Input = Kind.object(name: 'Integer(>= 0 and <= 255)') do |value|
12
+ value.is_a?(::Integer) && value >= 0 && value <= 255
13
+ end
14
+
15
+ def self.new(value, label:)
16
+ __new__(value: Input[value, label: label])
17
+ end
18
+ end
19
+ end
data/examples/rgb_1.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+
8
+ gem 'u-struct', path: '..'
9
+ gem 'kind'
10
+ end
11
+
12
+ RGBColor = Micro::Struct.with(:readonly, :to_ary).new(:red, :green, :blue) do
13
+ def to_hex
14
+ "##{red.to_s(16)}#{green.to_s(16)}#{blue.to_s(16)}"
15
+ end
16
+ end
17
+
18
+ module RGBColor
19
+ ColorNumber = Kind.object(name: 'Integer(>= 0 and <= 255)') do |value|
20
+ value.is_a?(::Integer) && value >= 0 && value <= 255
21
+ end
22
+
23
+ def self.new(r:, g:, b:)
24
+ __new__(
25
+ red: ColorNumber[r, label: 'r'],
26
+ green: ColorNumber[g, label: 'g'],
27
+ blue: ColorNumber[b, label: 'b']
28
+ )
29
+ end
30
+ end
31
+
32
+ rgb_color = RGBColor.new(r: 1, g: 1, b: 255)
33
+
34
+ p rgb_color
35
+
36
+ puts
37
+ puts format('to_hex: %p', rgb_color.to_hex)
38
+ puts format('to_a: %p', rgb_color.to_a)
39
+ puts
40
+
41
+ r, g, b = rgb_color
42
+
43
+ puts format('red: %p', r)
44
+ puts format('green: %p', g)
45
+ puts format('blue: %p', b)
46
+
47
+ RGBColor.new(r: 1, g: -1, b: 255) # Kind::Error (g: -1 expected to be a kind of Integer(>= 0 and <= 255))
data/examples/rgb_2.rb ADDED
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+
8
+ gem 'u-struct', path: '..'
9
+ gem 'kind'
10
+ end
11
+
12
+ RGBNumber = Micro::Struct.with(:readonly).new(:value) do
13
+ def to_s
14
+ value.to_s(16)
15
+ end
16
+ end
17
+
18
+ module RGBNumber
19
+ Input = Kind.object(name: 'Integer(>= 0 and <= 255)') do |value|
20
+ value.is_a?(::Integer) && value >= 0 && value <= 255
21
+ end
22
+
23
+ def self.new(value, label:)
24
+ __new__(value: Input[value, label: label])
25
+ end
26
+ end
27
+
28
+ RGBColor = Micro::Struct.with(:readonly, :to_ary).new(:red, :green, :blue) do
29
+ def to_a
30
+ [red.value, green.value, blue.value]
31
+ end
32
+
33
+ def to_hex
34
+ "##{red}#{green}#{blue}"
35
+ end
36
+ end
37
+
38
+ module RGBColor
39
+ def self.new(r:, g:, b:)
40
+ __new__(
41
+ red: RGBNumber.new(r, label: 'r'),
42
+ green: RGBNumber.new(g, label: 'g'),
43
+ blue: RGBNumber.new(b, label: 'b')
44
+ )
45
+ end
46
+ end
47
+
48
+ rgb_color = RGBColor.new(r: 1, g: 1, b: 255)
49
+
50
+ p rgb_color
51
+
52
+ puts
53
+ puts format('to_hex: %p', rgb_color.to_hex)
54
+ puts format('to_a: %p', rgb_color.to_a)
55
+ puts
56
+
57
+ r, g, b = rgb_color
58
+
59
+ puts format('red: %p', r)
60
+ puts format('green: %p', g)
61
+ puts format('blue: %p', b)
62
+
63
+ RGB::Color.new(r: 1, g: -1, b: 255) # Kind::Error (g: -1 expected to be a kind of Integer(>= 0 and <= 255))
data/examples/rgb_3.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+
8
+ gem 'u-struct', path: '..'
9
+ gem 'kind'
10
+ end
11
+
12
+ require_relative 'rgb/number'
13
+ require_relative 'rgb/color'
14
+
15
+ rgb_color = RGB::Color.new(r: 1, g: 1, b: 255)
16
+
17
+ p rgb_color
18
+
19
+ puts
20
+ puts format('to_hex: %p', rgb_color.to_hex)
21
+ puts format('to_a: %p', rgb_color.to_a)
22
+ puts
23
+
24
+ r, g, b = rgb_color
25
+
26
+ puts format('red: %p', r)
27
+ puts format('green: %p', g)
28
+ puts format('blue: %p', b)
29
+
30
+ RGB::Color.new(r: 1, g: -1, b: 255) # Kind::Error (g: -1 expected to be a kind of Integer(>= 0 and <= 255))
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Micro::Struct::Creator
4
+ module CreateModule
5
+ extend self
6
+
7
+ def with(required_members, optional_members, features)
8
+ container = Module.new
9
+
10
+ def_initialize(container, required_members, optional_members)
11
+ def_triple_eq(container)
12
+ def_members(container)
13
+ def_to_proc(container) if features[:to_proc]
14
+
15
+ container
16
+ end
17
+
18
+ private
19
+
20
+ def def_initialize(container, required_members, optional_members)
21
+ required = "#{required_members.join(':, ')}#{':' unless required_members.empty?}"
22
+ optional = "#{optional_members.join(': nil, ')}#{': nil' unless optional_members.empty?}"
23
+
24
+ method_arguments = [required, optional].reject(&:empty?).join(', ')
25
+ struct_arguments = (required_members + optional_members).join(', ')
26
+
27
+ # The .new() method will require all required keyword arguments.
28
+ # We are doing this because the Struct constructor keyword init option treats everything as optional.
29
+ #
30
+ container.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
31
+ def self.__new__(#{method_arguments}) # def self.__new__(a:, b:) do
32
+ Struct.send(:new, #{struct_arguments}) # Struct.send(:new, a, b)
33
+ end # end
34
+
35
+ class << self
36
+ alias new __new__
37
+ end
38
+ RUBY
39
+ end
40
+
41
+ def def_triple_eq(container)
42
+ container.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
43
+ def self.===(other)
44
+ Struct === other
45
+ end
46
+ RUBY
47
+ end
48
+
49
+ def def_members(container)
50
+ container.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
51
+ def self.members
52
+ Struct.members
53
+ end
54
+ RUBY
55
+ end
56
+
57
+ def def_to_proc(container)
58
+ container.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
59
+ def self.to_proc
60
+ ->(hash) { new(**hash) }
61
+ end
62
+ RUBY
63
+ end
64
+ end
65
+
66
+ private_constant :CreateModule
67
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Micro::Struct::Creator
4
+ module CreateStruct
5
+ extend self
6
+
7
+ def with(required_members, optional_members, features, &block)
8
+ struct = ::Struct.new(*(required_members + optional_members), &block)
9
+ struct.send(:private_class_method, :new)
10
+
11
+ def_to_ary(struct) if features[:to_ary]
12
+ def_to_hash(struct) if features[:to_hash]
13
+ def_readonly(struct) if features[:readonly]
14
+ def_instance_copy(struct) if features[:instance_copy]
15
+
16
+ struct
17
+ end
18
+
19
+ private
20
+
21
+ def def_to_ary(struct)
22
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
23
+ def to_ary
24
+ to_a
25
+ end
26
+ RUBY
27
+ end
28
+
29
+ def def_to_hash(struct)
30
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
31
+ def to_hash
32
+ to_h
33
+ end
34
+ RUBY
35
+ end
36
+
37
+ def def_readonly(struct)
38
+ struct.send(:private, *struct.members.map { |member| "#{member}=" })
39
+ end
40
+
41
+ def def_instance_copy(struct)
42
+ struct.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
43
+ def with(**members)
44
+ self.class.const_get(:Container, false).new(**to_h.merge(members))
45
+ end
46
+ RUBY
47
+ end
48
+ end
49
+
50
+ private_constant :CreateStruct
51
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Struct
4
+ class Creator
5
+ require_relative 'creator/create_module'
6
+ require_relative 'creator/create_struct'
7
+
8
+ def initialize(features)
9
+ @features = Features.require(features)
10
+ end
11
+
12
+ NormalizeMemberNames = ->(values) do
13
+ NormalizeNames::AsSymbols.(values, context: 'member')
14
+ end
15
+
16
+ def new(*members, required: nil, optional: nil, &block)
17
+ optional_members = NormalizeMemberNames[optional]
18
+ required_members = NormalizeMemberNames[members] + NormalizeMemberNames[required]
19
+
20
+ container = CreateModule.with(required_members, optional_members, @features)
21
+ struct = CreateStruct.with(required_members, optional_members, @features, &block)
22
+
23
+ container.const_set(:Struct, struct)
24
+ struct.const_set(:Container, container)
25
+ end
26
+ end
27
+
28
+ private_constant :Creator
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Struct
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
+ NormalizeFeatureNames = ->(values) do
21
+ NormalizeNames::AsSymbols.(values, context: 'feature')
22
+ end
23
+
24
+ def self.require(names)
25
+ to_enable =
26
+ NormalizeFeatureNames[names].each_with_object({}) { |name, memo| memo[name] = true }
27
+
28
+ Check.(**DISABLED.merge(to_enable))
29
+ end
30
+ end
31
+
32
+ private_constant :Features
33
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Struct
4
+ module NormalizeNames
5
+ module AsSymbols
6
+ REGEXP = /\A[_A-Za-z]\w*\z/.freeze
7
+ Invalid = ->(context, val) { raise NameError.new("invalid #{context} name: #{val}") }
8
+ AsSymbol = ->(context, val) { REGEXP =~ val ? val.to_sym : Invalid[context, val] }.curry
9
+
10
+ def self.call(values, context:)
11
+ Array(values).map(&AsSymbol[context])
12
+ end
13
+ end
14
+ end
15
+
16
+ private_constant :NormalizeNames
17
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  module Struct
5
- VERSION = '0.5.0'
5
+ VERSION = '0.9.0'
6
6
  end
7
7
  end
data/lib/micro/struct.rb CHANGED
@@ -1,141 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'struct/version'
4
+ require_relative 'struct/features'
5
+ require_relative 'struct/creator'
6
+ require_relative 'struct/normalize_names'
4
7
 
5
8
  module Micro
6
9
  # Like in a regular Struct, you can define one or many attributes.
10
+ # But all of them will be required by default.
11
+ #
7
12
  # Micro::Struct.new(:first_name, :last_name, ...)
8
13
  #
14
+ # Use the `optional:` arg if you want some optional attributes.
15
+ #
16
+ # Micro::Struct.new(:first_name, :last_name, optional: :gender)
17
+ #
18
+ # Using `optional:` to define all attributes are optional.
19
+ #
20
+ # Micro::Struct.new(optional: [:first_name, :last_name])
21
+ #
22
+ # Use the `required:` arg to define required attributes.
23
+ #
24
+ # Micro::Struct.new(
25
+ # required: [:first_name, :last_name],
26
+ # optional: [:gender, :age]
27
+ # )
28
+ #
9
29
  # You can also pass a block to define custom methods.
30
+ #
10
31
  # Micro::Struct.new(:name) {}
11
32
  #
12
- # Available features (use one, many or all):
33
+ # Available features (use one, many, or all) to create Structs with a special behavior:
13
34
  # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
14
35
  #
15
36
  # Micro::Struct.with(:to_ary).new(:name)
16
37
  # Micro::Struct.with(:to_ary, :to_hash).new(:name)
17
38
  # Micro::Struct.with(:to_ary, :to_hash, :to_proc).new(:name)
39
+ # Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly).new(:name)
40
+ # Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy).new(:name)
41
+ #
42
+ # All of the possible combinations to create a Ruby Struct. ;)
43
+ #
44
+ # Micro::Struct.new(optional: *)
45
+ # Micro::Struct.new(optional: *) {}
18
46
  #
19
- # Micro::Struct.with(...).new(...) {}
47
+ # Micro::Struct.new(required: *)
48
+ # Micro::Struct.new(required: *) {}
49
+ #
50
+ # Micro::Struct.new(*required, optional: *)
51
+ # Micro::Struct.new(*required, optional: *) {}
52
+ #
53
+ # Micro::Struct.new(required: *, optional: *)
54
+ # Micro::Struct.new(required: *, optional: *) {}
55
+ #
56
+ # Any options above can be used by the `.new()` method of the struct creator returned by the `.with()` method.
57
+ #
58
+ # Micro::Struct.with(*features).new(...) {}
20
59
  module Struct
21
- class Creator
22
- module Features
23
- DISABLED =
24
- { to_ary: false,
25
- to_hash: false,
26
- to_proc: false,
27
- readonly: false,
28
- instance_copy: false
29
- }.freeze
30
-
31
- Expose = ->(to_ary:, to_hash:, to_proc:, readonly:, instance_copy:) do
32
- { to_ary: to_ary,
33
- to_hash: to_hash,
34
- to_proc: to_proc,
35
- readonly: readonly,
36
- instance_copy: instance_copy }
37
- end
38
-
39
- def self.check(names)
40
- features_to_enable =
41
- Array(names).each_with_object({}) { |name, memo| memo[name] = true }
42
-
43
- Expose.(**DISABLED.merge(features_to_enable))
44
- end
45
- end
46
-
47
- def initialize(features)
48
- @features = Features.check(features)
49
- end
50
-
51
- def new(*members, &block)
52
- def_container do |container|
53
- def_struct(container, members, block) do |struct|
54
- def_initialize(container, struct)
55
-
56
- def_to_ary(struct)
57
- def_to_hash(struct)
58
- def_readonly(struct)
59
- def_instance_copy(struct)
60
- end
61
-
62
- def_to_proc(container)
63
- end
64
- end
65
-
66
- private
67
-
68
- def def_container(&block)
69
- Module.new.tap(&block)
70
- end
71
-
72
- def def_struct(container, members, block)
73
- struct = ::Struct.new(*members, &block)
74
- struct.const_set(:Container, container)
75
- struct.send(:private_class_method, :new)
76
-
77
- container.const_set(:Struct, struct)
78
-
79
- yield struct
80
- end
81
-
82
- def def_initialize(container, struct)
83
- # The .new() method will require all of the Struct's keyword arguments.
84
- # We are doing this because Struct's keyword_init option doesn't do that.
85
- container.module_eval(<<~RUBY, __FILE__, __LINE__ + 1) #
86
- def self.new(#{struct.members.join(':, ')}:) # def self.new(a:, b:) do
87
- Struct.send(:new, #{struct.members.join(', ')}) # Struct.send(:new, a, b)
88
- end # end
89
-
90
- def self.members
91
- Struct.members
92
- end
93
- RUBY
94
- end
95
-
96
- def def_to_ary(struct)
97
- struct.send(:alias_method, :to_ary, :to_a) if @features[:to_ary]
98
- end
99
-
100
- def def_to_hash(struct)
101
- struct.send(:alias_method, :to_hash, :to_h) if @features[:to_hash]
102
- end
103
-
104
- def def_readonly(struct)
105
- return unless @features[:readonly]
106
-
107
- struct.send(:private, *struct.members.map { |member| "#{member}=" })
108
- end
109
-
110
- def def_instance_copy(struct)
111
- return unless (@features[:readonly] || @features[:instance_copy])
112
-
113
- struct.class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
114
- def with(**members)
115
- self.class.const_get(:Container, false).new(**to_h.merge(members))
116
- end
117
- RUBY
118
- end
119
-
120
- def def_to_proc(container)
121
- return unless @features[:to_proc]
122
-
123
- container.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
124
- def self.to_proc
125
- ->(hash) { new(**hash) }
126
- end
127
- RUBY
128
- end
129
- end
130
-
131
- def self.new(*members, &block)
132
- with.new(*members, &block)
60
+ def self.new(*members, required: nil, optional: nil, &block)
61
+ with.new(*members, required: required, optional: optional, &block)
133
62
  end
134
63
 
135
64
  def self.with(*features)
136
65
  Creator.new(features)
137
66
  end
138
-
139
- private_constant :Creator
140
67
  end
141
68
  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.5.0
4
+ version: 0.9.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-03 00:00:00.000000000 Z
11
+ date: 2021-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -55,7 +55,19 @@ files:
55
55
  - Rakefile
56
56
  - bin/console
57
57
  - bin/setup
58
+ - examples/person_1.rb
59
+ - examples/person_2.rb
60
+ - examples/rgb/color.rb
61
+ - examples/rgb/number.rb
62
+ - examples/rgb_1.rb
63
+ - examples/rgb_2.rb
64
+ - examples/rgb_3.rb
58
65
  - lib/micro/struct.rb
66
+ - lib/micro/struct/creator.rb
67
+ - lib/micro/struct/creator/create_module.rb
68
+ - lib/micro/struct/creator/create_struct.rb
69
+ - lib/micro/struct/features.rb
70
+ - lib/micro/struct/normalize_names.rb
59
71
  - lib/micro/struct/version.rb
60
72
  - lib/u-struct.rb
61
73
  - u-struct.gemspec
@@ -81,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
93
  - !ruby/object:Gem::Version
82
94
  version: '0'
83
95
  requirements: []
84
- rubygems_version: 3.2.21
96
+ rubygems_version: 3.2.17
85
97
  signing_key:
86
98
  specification_version: 4
87
99
  summary: Create powered Ruby structs.