trinkets 0.4.0 → 0.5.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 +4 -4
- data/CHANGELOG.md +3 -0
- data/doc/class/init.md +45 -16
- data/lib/explicit/trinkets/extend/class/init.rb +152 -71
- data/lib/trinkets/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e815777ee25a04feffd792fc975e68256e346eb3ff77126bd3610b949deadb2
|
4
|
+
data.tar.gz: 77a0c78f5dc7699e2d90e7db69333b246023145c66e408901ae328d29d377385
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e57c30e56c22137542e58d2128d9a8ddb31e78acca1756c3337fa54a906046790997fecb8737b2c442528920756cfc8ccab465ac47489bb44257c4cecb9c9d03
|
7
|
+
data.tar.gz: 49ed92fdb96d888727ebc972cd71520f08fb603cad2345d9832cf9f8704d99e979cf02cd10c268dea5c12e8681f4bccdd49b8c9eca7a68a2025db002b349c74b
|
data/CHANGELOG.md
CHANGED
data/doc/class/init.md
CHANGED
@@ -14,6 +14,9 @@ To use it, define a class and call `::init` like you would call `::attr` methods
|
|
14
14
|
* when it's a hash, like `{ default: <VALUE> }`, it's an optional keyword argument
|
15
15
|
* an empty hash `{}` is equivalent to `{ default: nil }`
|
16
16
|
* defaults to `false`
|
17
|
+
* `super` : if the argument should be passed to `super()`
|
18
|
+
* the super class can have a `initialize` or call `init`
|
19
|
+
* default: `false`
|
17
20
|
|
18
21
|
The same options can be used per individual argument.
|
19
22
|
|
@@ -44,8 +47,9 @@ end
|
|
44
47
|
class Test
|
45
48
|
attr_accessor :a, :b
|
46
49
|
def initialize(a, b)
|
47
|
-
|
48
|
-
@
|
50
|
+
super()
|
51
|
+
@a = a unless instance_variable_defined?(:@a)
|
52
|
+
@b = b unless instance_variable_defined?(:@b)
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
@@ -71,8 +75,9 @@ end
|
|
71
75
|
class TestAttr
|
72
76
|
attr_reader :a, :b
|
73
77
|
def initialize(a, b)
|
74
|
-
|
75
|
-
@
|
78
|
+
super()
|
79
|
+
@a = a unless instance_variable_defined?(:@a)
|
80
|
+
@b = b unless instance_variable_defined?(:@b)
|
76
81
|
end
|
77
82
|
end
|
78
83
|
|
@@ -98,8 +103,9 @@ end
|
|
98
103
|
class TestKW
|
99
104
|
attr_accessor :a, :b
|
100
105
|
def initialize(a:, b:)
|
101
|
-
|
102
|
-
@
|
106
|
+
super()
|
107
|
+
@a = a unless instance_variable_defined?(:@a)
|
108
|
+
@b = b unless instance_variable_defined?(:@b)
|
103
109
|
end
|
104
110
|
end
|
105
111
|
|
@@ -126,10 +132,11 @@ class TestAttrOptions
|
|
126
132
|
attr_reader :a
|
127
133
|
attr_accessor :b, :c
|
128
134
|
def initialize(b, d, a:, c:)
|
129
|
-
|
130
|
-
@
|
131
|
-
@
|
132
|
-
@
|
135
|
+
super()
|
136
|
+
@a = a unless instance_variable_defined?(:@a)
|
137
|
+
@b = b unless instance_variable_defined?(:@b)
|
138
|
+
@c = c unless instance_variable_defined?(:@c)
|
139
|
+
@d = d unless instance_variable_defined?(:@d)
|
133
140
|
end
|
134
141
|
end
|
135
142
|
|
@@ -152,7 +159,6 @@ test.a = 5
|
|
152
159
|
```
|
153
160
|
|
154
161
|
## Default values for keyword arguments
|
155
|
-
|
156
162
|
```ruby
|
157
163
|
class TestDefaultKw
|
158
164
|
init [:a, kw: true],
|
@@ -163,9 +169,10 @@ end
|
|
163
169
|
# would be the same as
|
164
170
|
class TestDefaultKw
|
165
171
|
attr_accessor :a, :b
|
166
|
-
def initialize(a
|
167
|
-
|
168
|
-
@
|
172
|
+
def initialize(a:, b: 3)
|
173
|
+
super()
|
174
|
+
@a = a unless instance_variable_defined?(:@a)
|
175
|
+
@b = b unless instance_variable_defined?(:@b)
|
169
176
|
end
|
170
177
|
end
|
171
178
|
|
@@ -178,6 +185,27 @@ test.b
|
|
178
185
|
# 3
|
179
186
|
```
|
180
187
|
|
188
|
+
## Super
|
189
|
+
```ruby
|
190
|
+
class TestParent
|
191
|
+
init :a # also works with a plain initialize()
|
192
|
+
end
|
193
|
+
|
194
|
+
class TestChild
|
195
|
+
init [:a, super: true], :b
|
196
|
+
end
|
197
|
+
|
198
|
+
# would be the same as
|
199
|
+
class TestChild
|
200
|
+
attr_accessor :a, :b
|
201
|
+
def initialize(a, b)
|
202
|
+
super(a)
|
203
|
+
@a = a unless instance_variable_defined?(:@a)
|
204
|
+
@b = b unless instance_variable_defined?(:@b)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
181
209
|
## Mixed together
|
182
210
|
```ruby
|
183
211
|
class TestMixed
|
@@ -191,8 +219,9 @@ class TestMixed
|
|
191
219
|
attr_reader :a
|
192
220
|
attr_accessor :b
|
193
221
|
def initialize(a:, b:)
|
194
|
-
|
195
|
-
@
|
222
|
+
super()
|
223
|
+
@a = a unless instance_variable_defined?(:@a)
|
224
|
+
@b = b unless instance_variable_defined?(:@b)
|
196
225
|
end
|
197
226
|
end
|
198
227
|
|
@@ -5,107 +5,188 @@ module Trinkets
|
|
5
5
|
module Init
|
6
6
|
ATTR = %i[accessor reader writer none].freeze
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
# even though options like `kw` aren't used, they serve here to validate the `attrs` options
|
18
|
-
attr_init = ->(name, attr: ATTR.first, kw: false) do
|
19
|
-
unless ATTR.include?(attr)
|
20
|
-
raise ArgumentError, "wrong `attr` type for `#{name.inspect}` (given #{attr.inspect}, expected :accessor (default), :reader, :writer or :none)"
|
21
|
-
end
|
22
|
-
attr_methods[attr].call(name) unless attr == :none
|
8
|
+
class Parameter
|
9
|
+
attr_reader :name, :attr, :kw, :super
|
10
|
+
def initialize(name:, attr:, kw:, super:)
|
11
|
+
@name = name
|
12
|
+
@attr = attr
|
13
|
+
@kw = kw
|
14
|
+
@super = [super:].first[:super]
|
23
15
|
end
|
16
|
+
end
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
# hash with 3 keys: {
|
28
|
-
# FalseClass => [] # positional args
|
29
|
-
# TrueClass => [] # mandatory kw args
|
30
|
-
# Hash => [] # optional kw args with default value
|
31
|
-
# }
|
32
|
-
grouped_params = attrs
|
33
|
-
.map { |name, opts| [name, opts[:kw] || false] }
|
34
|
-
.group_by { _1.last.class }
|
18
|
+
class BoundParameter < Parameter
|
35
19
|
|
36
|
-
|
37
|
-
kw_params = [*grouped_params[TrueClass]].map(&:first)
|
38
|
-
opt_kw_params = [*grouped_params[Hash]].to_h
|
39
|
-
.transform_values! { _1[:default] }
|
20
|
+
attr_reader :value
|
40
21
|
|
41
|
-
|
42
|
-
|
22
|
+
def initialize(name:, attr:, kw:, super:, value:)
|
23
|
+
super(name:, attr:, kw:, super:)
|
24
|
+
@value = value
|
25
|
+
end
|
43
26
|
end
|
44
27
|
|
45
|
-
|
46
|
-
|
28
|
+
# @!attribute r req
|
29
|
+
# @return [Array[Parameter]]
|
30
|
+
# @!attribute r key_req
|
31
|
+
# @return [Array[Parameter]]
|
32
|
+
# @!attribute r key_opt
|
33
|
+
# @return [Array[Parameter]]
|
34
|
+
class Parameters < Struct.new(:req, :key_req, :key_opt, keyword_init: true)
|
47
35
|
|
48
|
-
|
36
|
+
#@return [Parameters]
|
37
|
+
def self.build(params, **default_options)
|
38
|
+
|
39
|
+
raise ArgumentError, 'At least 1 attribute is required.' if params.empty?
|
49
40
|
|
50
41
|
unless ::Trinkets::Class::Init::ATTR.include?(default_options[:attr])
|
51
42
|
attr = default_options[:attr].inspect
|
52
43
|
raise ArgumentError, "wrong `attr` type (given #{attr}, expected :accessor (default), :reader, :writer or :none)"
|
53
44
|
end
|
54
45
|
|
55
|
-
#
|
56
|
-
|
57
|
-
attrs = attrs.map do |a|
|
58
|
-
name, opts = [*a]
|
46
|
+
# @type [Array[Parameter]]
|
47
|
+
params = params.map do |name, opts|
|
59
48
|
name = name.to_s.sub(/^@/, '').to_sym
|
60
|
-
|
61
|
-
|
49
|
+
|
50
|
+
opts ||= {}
|
51
|
+
opts.reject! { |_, v| v.nil? }
|
52
|
+
opts = default_options.merge(opts)
|
53
|
+
|
54
|
+
Parameter.new(name:, **opts)
|
62
55
|
end
|
63
56
|
|
64
|
-
|
57
|
+
repeated_params = params.map(&:name)
|
65
58
|
.tally
|
66
59
|
.select { |_, count| count > 1 }
|
67
60
|
.keys
|
68
61
|
|
69
|
-
raise ArgumentError, "duplicated argument names: #{
|
62
|
+
raise ArgumentError, "duplicated argument names: #{repeated_params.join(', ')}" if repeated_params.any?
|
63
|
+
|
64
|
+
# hash with 3 keys: {
|
65
|
+
# FalseClass => { :name => Parameter } # positional args
|
66
|
+
# TrueClass => { :name => Parameter } # mandatory kw args
|
67
|
+
# Hash => { :name => Parameter } # optional kw args with default value
|
68
|
+
# }
|
69
|
+
#@type [Hash[Class, Array[Parameter]]]
|
70
|
+
params = params.group_by { |param| param.kw.class }
|
71
|
+
|
72
|
+
Parameters.new(
|
73
|
+
req: [*params[FalseClass]],
|
74
|
+
key_req: [*params[TrueClass]],
|
75
|
+
key_opt: [*params[Hash]]
|
76
|
+
)
|
70
77
|
|
71
|
-
attrs.to_h
|
72
78
|
end
|
73
79
|
|
74
|
-
|
75
|
-
|
76
|
-
# @param [Hash[Symbol, Object]] opt_kw_params
|
77
|
-
private def define_initialize(pos_params, kw_params, opt_kw_params)
|
78
|
-
->(*values, **kw_values) do
|
80
|
+
#@return [Parameters]
|
81
|
+
def bind(values, kw_values)
|
79
82
|
|
80
|
-
|
81
|
-
raise ArgumentError, "wrong number of arguments (given #{values.size}, expected #{pos_params.size})"
|
82
|
-
end
|
83
|
+
validate values, kw_values
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
85
|
+
req = self.req.zip(values).map do |(param, value)|
|
86
|
+
BoundParameter.new(
|
87
|
+
name: param.name,
|
88
|
+
attr: param.attr,
|
89
|
+
kw: param.kw,
|
90
|
+
super: param.super,
|
91
|
+
value:
|
92
|
+
)
|
93
|
+
end
|
89
94
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
+
key_req = self.key_req.map do |param|
|
96
|
+
BoundParameter.new(
|
97
|
+
name: param.name,
|
98
|
+
attr: param.attr,
|
99
|
+
kw: param.kw,
|
100
|
+
super: param.super,
|
101
|
+
value: kw_values[param.name]
|
102
|
+
)
|
103
|
+
end
|
95
104
|
|
96
|
-
|
97
|
-
|
98
|
-
|
105
|
+
key_opt = self.key_opt.map do |param|
|
106
|
+
key, value = kw_values.assoc(param.name)
|
107
|
+
BoundParameter.new(
|
108
|
+
name: param.name,
|
109
|
+
attr: param.attr,
|
110
|
+
kw: param.kw,
|
111
|
+
super: param.super,
|
112
|
+
value: key ? value : param.kw[:default]
|
113
|
+
)
|
114
|
+
end
|
99
115
|
|
100
|
-
|
101
|
-
instance_variable_set "@#{name}", kw_values[name]
|
102
|
-
end
|
116
|
+
Parameters.new(req:, key_req:, key_opt:)
|
103
117
|
|
104
|
-
|
105
|
-
value = kw_values.include?(name) ? kw_values[name] : default_value
|
106
|
-
instance_variable_set "@#{name}", value
|
107
|
-
end
|
118
|
+
end
|
108
119
|
|
120
|
+
def all_params = req + all_key_params
|
121
|
+
|
122
|
+
def all_key_params = key_req + key_opt
|
123
|
+
|
124
|
+
private def validate(values, kw_values)
|
125
|
+
|
126
|
+
unless req.size == values.size
|
127
|
+
raise ArgumentError, "wrong number of arguments (given #{values.size}, expected #{req.size})"
|
128
|
+
end
|
129
|
+
|
130
|
+
missing_keys = key_req.map(&:name) - kw_values.keys
|
131
|
+
unless missing_keys.empty?
|
132
|
+
missing_keys = missing_keys.map(&:inspect).join(', ')
|
133
|
+
raise ArgumentError, "missing keywords: #{missing_keys}"
|
134
|
+
end
|
135
|
+
|
136
|
+
unknown_keywords = kw_values.except(*all_key_params.map(&:name))
|
137
|
+
unless unknown_keywords.empty?
|
138
|
+
unknown_keywords = unknown_keywords.keys.map(&:to_sym).map(&:inspect).join(', ')
|
139
|
+
raise ArgumentError, "unknown keywords: #{unknown_keywords}"
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def init(*params, attr: ATTR.first, kw: false, super: false)
|
146
|
+
|
147
|
+
params = Parameters.build(params, attr:, kw:, super:)
|
148
|
+
|
149
|
+
# @type [Hash[Symbol, Method]]
|
150
|
+
attr_methods = (ATTR - [:none])
|
151
|
+
.each_with_object({}) do |name, h|
|
152
|
+
h[name] = method("attr_#{name}")
|
153
|
+
end
|
154
|
+
|
155
|
+
attr_init = ->(name, attr: ATTR.first) do
|
156
|
+
unless ATTR.include?(attr)
|
157
|
+
raise ArgumentError, "wrong `attr` type for `#{name.inspect}` (given #{attr.inspect}, expected :accessor (default), :reader, :writer or :none)"
|
158
|
+
end
|
159
|
+
attr_methods[attr].call(name) unless attr == :none
|
160
|
+
end
|
161
|
+
|
162
|
+
params.all_params.each do |param|
|
163
|
+
attr_init.call param.name, attr: param.attr
|
164
|
+
end
|
165
|
+
|
166
|
+
init_method = Init.send(:define_initialize, params)
|
167
|
+
define_method :initialize, init_method
|
168
|
+
end
|
169
|
+
|
170
|
+
class << self
|
171
|
+
|
172
|
+
# @param [Parameters] params
|
173
|
+
private def define_initialize(params)
|
174
|
+
->(*values, **kw_values) do
|
175
|
+
|
176
|
+
params = params.bind(values, kw_values)
|
177
|
+
|
178
|
+
super_req = params.req.select(&:super).map(&:value)
|
179
|
+
super_kws = params.all_key_params.select(&:super)
|
180
|
+
.each_with_object({}) do |param, h|
|
181
|
+
h[param.name] = param.value
|
182
|
+
end
|
183
|
+
|
184
|
+
super *super_req, **super_kws
|
185
|
+
|
186
|
+
params.all_params.each do |param|
|
187
|
+
name = "@#{param.name}"
|
188
|
+
instance_variable_set(name, param.value) unless instance_variable_defined?(name)
|
189
|
+
end
|
109
190
|
end
|
110
191
|
end
|
111
192
|
end
|
data/lib/trinkets/version.rb
CHANGED