u-struct 0.5.0 → 0.6.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: 52450d53b7accc5aa4c83682dfc6557d3f5fa4d7879648c189701198548e0ace
4
+ data.tar.gz: 4a25f913523f7aa9e79e96e49cafd33cff2ec748dac79d0bca611cd67aa304e3
5
5
  SHA512:
6
- metadata.gz: 2d86bb3a75737927e5f74d56e16c7760b8c7b9c3dce7642ebdef058122212dd21c38aec04573722842ea081995b5e03c6a094a1548bf63f1ed709762c6e5ad2c
7
- data.tar.gz: 6dee9c1ab9cd6ba5675cf7c1e6cedc128e70fe45aabf52a2b1beb77fb63e9b01c1d70b0775b4393167132371923b5d56a2efed099048e0e3e594b5c199d95ab3
6
+ metadata.gz: 1bd5940255d259aeebbe5207a0a397bfade550bbc0e4bf719233ef18c2ba8f3fb483b96fb932fd775f7b98c35af184292b5f7392b32ebf0456888379b13c857c
7
+ data.tar.gz: 0ca2437cf6a48c090cc176324477b579c394b11c8c451a14a11c62a9509e5b3f6f722f1318d777e4db610cf4ab004b7f820dba169544fca0c31fb6033d03cf76
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.0] - 2021-12-03
4
+
5
+ - To-do
6
+
3
7
  ## [0.5.0] - 2021-12-03
4
8
 
5
9
  - To-do
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- u-struct (0.5.0)
4
+ u-struct (0.6.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -22,19 +22,45 @@ 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 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
+
27
37
  # You can also pass a block to define custom methods.
38
+
28
39
  Micro::Struct.new(:name) {}
29
40
 
30
- # Available features (use one, many or all):
31
- # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
41
+ # Available features (use one, many, or all) to create Structs with a special behavior:
42
+ # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
32
43
 
33
44
  Micro::Struct.with(:to_ary).new(:name)
34
45
  Micro::Struct.with(:to_ary, :to_hash).new(:name)
35
46
  Micro::Struct.with(:to_ary, :to_hash, :to_proc).new(:name)
47
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly).new(:name)
48
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy).new(:name)
49
+
50
+ # All of the possible combinations to create a Ruby Struct. ;)
51
+
52
+ Micro::Struct.new(*required)
53
+ Micro::Struct.new(*required) {}
54
+
55
+ Micro::Struct.new(_optional: *)
56
+ Micro::Struct.new(_optional: *) {}
57
+
58
+ Micro::Struct.new(*required, _optional: *)
59
+ Micro::Struct.new(*required, _optional: *) {}
60
+
61
+ # Any options above can be used by the `.new()` method of the struct creator returned by the `.with()` method.
36
62
 
37
- Micro::Struct.with(...).new(...) {}
63
+ Micro::Struct.with(*features).new(...) {}
38
64
  ```
39
65
 
40
66
  ## Development
@@ -0,0 +1,50 @@
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_and_members(container, required_members, optional_members)
11
+
12
+ def_to_proc(container) if features[:to_proc]
13
+
14
+ container
15
+ end
16
+
17
+ private
18
+
19
+ def def_initialize_and_members(container, required_members, optional_members)
20
+ required = "#{required_members.join(':, ')}#{':' unless required_members.empty?}"
21
+ optional = "#{optional_members.join(': nil, ')}#{': nil' unless optional_members.empty?}"
22
+
23
+ method_arguments = [required, optional].reject(&:empty?).join(', ')
24
+ struct_arguments = (required_members + optional_members).join(', ')
25
+
26
+ container.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
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
+ def self.new(#{method_arguments}) # def self.new(a:, b:) do
31
+ Struct.send(:new, #{struct_arguments}) # Struct.send(:new, a, b)
32
+ end # end
33
+
34
+ def self.members
35
+ Struct.members
36
+ end
37
+ RUBY
38
+ end
39
+
40
+ def def_to_proc(container)
41
+ container.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
42
+ def self.to_proc
43
+ ->(hash) { new(**hash) }
44
+ end
45
+ RUBY
46
+ end
47
+ end
48
+
49
+ private_constant :CreateModule
50
+ end
@@ -0,0 +1,43 @@
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[:readonly] || features[:instance_copy]
15
+
16
+ struct
17
+ end
18
+
19
+ private
20
+
21
+ def def_to_ary(struct)
22
+ struct.send(:alias_method, :to_ary, :to_a)
23
+ end
24
+
25
+ def def_to_hash(struct)
26
+ struct.send(:alias_method, :to_hash, :to_h)
27
+ end
28
+
29
+ def def_readonly(struct)
30
+ struct.send(:private, *struct.members.map { |member| "#{member}=" })
31
+ end
32
+
33
+ def def_instance_copy(struct)
34
+ struct.class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
35
+ def with(**members)
36
+ self.class.const_get(:Container, false).new(**to_h.merge(members))
37
+ end
38
+ RUBY
39
+ end
40
+ end
41
+
42
+ private_constant :CreateStruct
43
+ 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
+ ValidateMemberNames = ->(values) do
13
+ Validate::Names.(values, label: 'member')
14
+ end
15
+
16
+ def new(*members, _optional: nil, &block)
17
+ required_members = ValidateMemberNames[members]
18
+ optional_members = ValidateMemberNames[_optional]
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,34 @@
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
11
+ }.freeze
12
+
13
+ Check = ->(to_ary:, to_hash:, to_proc:, readonly:, instance_copy:) do
14
+ { to_ary: to_ary,
15
+ to_hash: to_hash,
16
+ to_proc: to_proc,
17
+ readonly: readonly,
18
+ instance_copy: instance_copy }
19
+ end
20
+
21
+ ValidateFeatureNames = ->(values) do
22
+ Validate::Names.(values, label: 'feature')
23
+ end
24
+
25
+ def self.require(names)
26
+ features_to_enable =
27
+ ValidateFeatureNames[names].each_with_object({}) { |name, memo| memo[name] = true }
28
+
29
+ Check.(**DISABLED.merge(features_to_enable))
30
+ end
31
+ end
32
+
33
+ private_constant :Features
34
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Struct
4
+ module Validate
5
+ module Names
6
+ REGEXP = /\A[_A-Za-z]\w*\z/.freeze
7
+ Invalid = ->(label, val) { raise NameError.new("invalid #{label} name: #{val}") }
8
+ AsSymbol = ->(label, val) { REGEXP =~ val ? val.to_sym : Invalid[label, val] }.curry
9
+
10
+ def self.call(values, label:)
11
+ Array(values).map(&Names::AsSymbol[label])
12
+ end
13
+ end
14
+ end
15
+
16
+ private_constant :Validate
17
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  module Struct
5
- VERSION = '0.5.0'
5
+ VERSION = '0.6.0'
6
6
  end
7
7
  end
data/lib/micro/struct.rb CHANGED
@@ -1,141 +1,58 @@
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/validate'
4
7
 
5
8
  module Micro
6
9
  # Like in a regular Struct, you can define one or many attributes.
10
+ # But all of 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
+ #
9
22
  # You can also pass a block to define custom methods.
23
+ #
10
24
  # Micro::Struct.new(:name) {}
11
25
  #
12
- # Available features (use one, many or all):
26
+ # Available features (use one, many, or all) to create Structs with a special behavior:
13
27
  # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
14
28
  #
15
29
  # Micro::Struct.with(:to_ary).new(:name)
16
30
  # Micro::Struct.with(:to_ary, :to_hash).new(:name)
17
31
  # Micro::Struct.with(:to_ary, :to_hash, :to_proc).new(:name)
32
+ # Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly).new(:name)
33
+ # Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy).new(:name)
34
+ #
35
+ # All of the possible combinations to create a Ruby Struct. ;)
36
+ #
37
+ # Micro::Struct.new(*required)
38
+ # Micro::Struct.new(*required) {}
39
+ #
40
+ # Micro::Struct.new(_optional: *)
41
+ # Micro::Struct.new(_optional: *) {}
18
42
  #
19
- # Micro::Struct.with(...).new(...) {}
43
+ # Micro::Struct.new(*required, _optional: *)
44
+ # Micro::Struct.new(*required, _optional: *) {}
45
+ #
46
+ # Any options above can be used by the `.new()` method of the struct creator returned by the `.with()` method.
47
+ #
48
+ # Micro::Struct.with(*features).new(...) {}
20
49
  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)
50
+ def self.new(*members, _optional: nil, &block)
51
+ with.new(*members, _optional: _optional, &block)
133
52
  end
134
53
 
135
54
  def self.with(*features)
136
55
  Creator.new(features)
137
56
  end
138
-
139
- private_constant :Creator
140
57
  end
141
58
  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.6.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-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -56,6 +56,11 @@ files:
56
56
  - bin/console
57
57
  - bin/setup
58
58
  - lib/micro/struct.rb
59
+ - lib/micro/struct/creator.rb
60
+ - lib/micro/struct/creator/create_module.rb
61
+ - lib/micro/struct/creator/create_struct.rb
62
+ - lib/micro/struct/features.rb
63
+ - lib/micro/struct/validate.rb
59
64
  - lib/micro/struct/version.rb
60
65
  - lib/u-struct.rb
61
66
  - u-struct.gemspec