u-struct 0.5.0 → 0.6.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: 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