u-struct 0.4.0 → 0.8.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: 1ebbd60464f2e2d2efd428b0091d68820ef2b5d1a4bc73bed1a474d22f2f0fac
4
- data.tar.gz: 1965a2b7bd8da50c6c5c31acff5c950590146c060ced14e732d3a5b646dafd8f
3
+ metadata.gz: bebaeb99b56b26288a87f8907532e194a4388563a11ed486c90d203c34d9e951
4
+ data.tar.gz: 66d1f615dcaa99c713d24b7ea375f00293396fe91d2314be223a487cfa90caa8
5
5
  SHA512:
6
- metadata.gz: b7274b8401d134cc21408447b87a54472f5f5cdea5263c3ca24e1a4feab6c0104deea8ff1985edb49a3e58d6e91e27f52b908d6fa2974b26ed6472916ebc4cb9
7
- data.tar.gz: 62238f702e13aaa61d13bcb6213819d79b67e373b3f768cf0675526baa7bf73aa7d1e6c8130d84072f8a6a124923de28318b34b2a44ae17b0de573513c557acf
6
+ metadata.gz: 4131a340a7a5981a60a177a2a1c59846f12693053eb80cd77e289e83a3602e10df31b694c8bb8a0fbb9a8d60886ce4b0e27424cce27a1ae2e513570d981c0c67
7
+ data.tar.gz: 2ed2f7a1a80265ace687639e575f5395f3cb1c663ac0c4b5dc044a52a2ba321540942523a6a7f0b9377df6d2a4569e029b35a8f1345e861ab23cd0f5cd0d885b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.8.0] - 2021-12-05
4
+
5
+ - To-do
6
+
7
+ ## [0.7.0] - 2021-12-04
8
+
9
+ - To-do
10
+
11
+ ## [0.6.0] - 2021-12-03
12
+
13
+ - To-do
14
+
15
+ ## [0.5.0] - 2021-12-03
16
+
17
+ - To-do
18
+
19
+ ## [0.4.0] - 2021-12-03
20
+
21
+ - To-do
22
+
23
+ ## [0.3.1] - 2021-12-02
24
+
25
+ - To-do
26
+
27
+ ## [0.3.0] - 2021-12-02
28
+
29
+ - To-do
30
+
31
+ ## [0.2.0] - 2021-12-02
32
+
33
+ - To-do
34
+
3
35
  ## [0.1.0] - 2021-12-02
4
36
 
5
37
  - Initial release
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,22 +1,31 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- u-struct (0.4.0)
4
+ u-struct (0.8.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
22
- 2.2.26
31
+ 2.2.32
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Micro::Struct
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/micro/struct`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Create powered Ruby structs.
6
4
 
7
5
  ## Installation
8
6
 
@@ -22,7 +20,61 @@ Or install it yourself as:
22
20
 
23
21
  ## Usage
24
22
 
25
- TODO: Write usage instructions here
23
+ ```ruby
24
+ # Like in a regular Struct, you can define one or many attributes.
25
+ # But all of them will be required by default.
26
+
27
+ Micro::Struct.new(:first_name, :last_name, ...)
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
+
44
+ # You can also pass a block to define custom methods.
45
+
46
+ Micro::Struct.new(:name) {}
47
+
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)
50
+
51
+ Micro::Struct.with(:to_ary).new(:name)
52
+ Micro::Struct.with(:to_ary, :to_hash).new(:name)
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.
75
+
76
+ Micro::Struct.with(*features).new(...) {}
77
+ ```
26
78
 
27
79
  ## Development
28
80
 
@@ -0,0 +1,65 @@
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, features)
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
+ RUBY
35
+ end
36
+
37
+ def def_triple_eq(container)
38
+ container.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
39
+ def self.===(other)
40
+ Struct === other
41
+ end
42
+ RUBY
43
+ end
44
+
45
+ def def_members(container)
46
+ container.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
47
+ def self.members
48
+ Struct.members
49
+ end
50
+ RUBY
51
+ end
52
+
53
+ def def_to_proc(container, features)
54
+ return unless features[:to_proc]
55
+
56
+ container.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
57
+ def self.to_proc
58
+ ->(hash) { new(**hash) }
59
+ end
60
+ RUBY
61
+ end
62
+ end
63
+
64
+ private_constant :CreateModule
65
+ 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
+ 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.4.0'
5
+ VERSION = '0.8.0'
6
6
  end
7
7
  end
data/lib/micro/struct.rb CHANGED
@@ -1,112 +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
- # Micro::Struct.new(:first_name, :last_name, ...)
9
+ # Like in a regular Struct, you can define one or many attributes.
10
+ # But all of them will be required by default.
7
11
  #
8
- # Micro::Struct.with(:to_ary).new(:name) # or .with(:to_hash), .with(:to_proc)
12
+ # Micro::Struct.new(:first_name, :last_name, ...)
9
13
  #
10
- # Micro::Struct.with(:to_ary, :to_hash).new(:name)
14
+ # Use the `optional:` arg if you want some optional attributes.
11
15
  #
12
- # Micro::Struct.with(:to_ary, :to_hash, :to_proc).new(:name)
16
+ # Micro::Struct.new(:first_name, :last_name, optional: :gender)
13
17
  #
14
- # Micro::Struct.new(:name) {}
18
+ # Using `optional:` to define all attributes are optional.
15
19
  #
16
- # Micro::Struct.with(...).new(:name) {}
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
+ #
29
+ # You can also pass a block to define custom methods.
30
+ #
31
+ # Micro::Struct.new(:name) {}
32
+ #
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)
35
+ #
36
+ # Micro::Struct.with(:to_ary).new(:name)
37
+ # Micro::Struct.with(:to_ary, :to_hash).new(:name)
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: *) {}
46
+ #
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(...) {}
17
59
  module Struct
18
- class Creator
19
- module Features
20
- DISABLED =
21
- { to_ary: false,
22
- to_hash: false,
23
- to_proc: false }.freeze
24
-
25
- Expose = ->(to_ary:, to_hash:, to_proc:) do
26
- { to_ary: to_ary,
27
- to_hash: to_hash,
28
- to_proc: to_proc }
29
- end
30
-
31
- def self.check(names)
32
- features_to_enable =
33
- Array(names).each_with_object({}) { |name, memo| memo[name] = true }
34
-
35
- Expose.(**DISABLED.merge(features_to_enable))
36
- end
37
- end
38
-
39
- def initialize(features)
40
- @features = Features.check(features)
41
- end
42
-
43
- def new(*members, &block)
44
- def_module do |mod|
45
- def_struct(mod, members, block) do |struct|
46
- def_initialize(mod, struct)
47
- def_to_ary(struct)
48
- def_to_hash(struct)
49
- def_to_proc(mod)
50
- end
51
- end
52
- end
53
-
54
- private
55
-
56
- def def_module(&block)
57
- Module.new.tap(&block)
58
- end
59
-
60
- def def_struct(mod, members, block)
61
- struct = ::Struct.new(*members, &block)
62
- struct.send(:private_class_method, :new)
63
-
64
- mod.const_set(:Struct, struct)
65
-
66
- yield struct
67
- end
68
-
69
- def def_initialize(mod, struct)
70
- # The .new() method will require all of the Struct's keyword arguments.
71
- # We are doing this because Struct's keyword_init option doesn't do that.
72
- mod.module_eval(<<~RUBY, __FILE__, __LINE__ + 1) #
73
- def self.new(#{struct.members.join(':, ')}:) # def self.new(a:, b:) do
74
- Struct.send(:new, #{struct.members.join(', ')}) # Struct.send(:new, a, b)
75
- end # end
76
-
77
- def self.members
78
- Struct.members
79
- end
80
- RUBY
81
- end
82
-
83
- def def_to_ary(struct)
84
- struct.send(:alias_method, :to_ary, :to_a) if @features[:to_ary]
85
- end
86
-
87
- def def_to_hash(struct)
88
- struct.send(:alias_method, :to_hash, :to_h) if @features[:to_hash]
89
- end
90
-
91
- def def_to_proc(mod)
92
- return unless @features[:to_proc]
93
-
94
- mod.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
95
- def self.to_proc
96
- ->(hash) { new(**hash) }
97
- end
98
- RUBY
99
- end
100
- end
101
-
102
- def self.new(*members, &block)
103
- with.new(*members, &block)
60
+ def self.new(*members, required: nil, optional: nil, &block)
61
+ with.new(*members, required: required, optional: optional, &block)
104
62
  end
105
63
 
106
64
  def self.with(*features)
107
65
  Creator.new(features)
108
66
  end
109
-
110
- private_constant :Creator
111
67
  end
112
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.4.0
4
+ version: 0.8.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-05 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/normalize_names.rb
59
64
  - lib/micro/struct/version.rb
60
65
  - lib/u-struct.rb
61
66
  - u-struct.gemspec
@@ -81,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
86
  - !ruby/object:Gem::Version
82
87
  version: '0'
83
88
  requirements: []
84
- rubygems_version: 3.2.21
89
+ rubygems_version: 3.2.17
85
90
  signing_key:
86
91
  specification_version: 4
87
92
  summary: Create powered Ruby structs.