trinkets 0.3.1 → 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 +6 -0
- data/doc/class/init.md +72 -12
- data/lib/explicit/trinkets/extend/class/init.rb +152 -54
- data/lib/trinkets/version.rb +1 -1
- metadata +1 -2
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
@@ -1,4 +1,10 @@
|
|
1
1
|
|
2
|
+
### [0.5.0](https://github.com/SilverPhoenix99/trinkets/tree/v0.5.0)
|
3
|
+
* `Class::init`: Added `:super` option.
|
4
|
+
|
5
|
+
### [0.4.0](https://github.com/SilverPhoenix99/trinkets/tree/v0.4.0)
|
6
|
+
* `Class::init`: Allow default values for keyword arguments.
|
7
|
+
|
2
8
|
### [0.3.1](https://github.com/SilverPhoenix99/trinkets/tree/v0.3.1)
|
3
9
|
* Fixed repeated arguments in `Class::init`.
|
4
10
|
|
data/doc/class/init.md
CHANGED
@@ -9,7 +9,14 @@ To use it, define a class and call `::init` like you would call `::attr` methods
|
|
9
9
|
* can be `:accessor`, `:reader`, `:writer` or `:none`
|
10
10
|
* defaults to `:accessor`
|
11
11
|
* `kw` : if arguments are to be set as keyword arguments
|
12
|
+
* when `false`, it's a mandatory positional argument
|
13
|
+
* when `true`, it becomes a mandatory keyword argument, like `(a:)`
|
14
|
+
* when it's a hash, like `{ default: <VALUE> }`, it's an optional keyword argument
|
15
|
+
* an empty hash `{}` is equivalent to `{ default: nil }`
|
12
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`
|
13
20
|
|
14
21
|
The same options can be used per individual argument.
|
15
22
|
|
@@ -40,8 +47,9 @@ end
|
|
40
47
|
class Test
|
41
48
|
attr_accessor :a, :b
|
42
49
|
def initialize(a, b)
|
43
|
-
|
44
|
-
@
|
50
|
+
super()
|
51
|
+
@a = a unless instance_variable_defined?(:@a)
|
52
|
+
@b = b unless instance_variable_defined?(:@b)
|
45
53
|
end
|
46
54
|
end
|
47
55
|
|
@@ -67,8 +75,9 @@ end
|
|
67
75
|
class TestAttr
|
68
76
|
attr_reader :a, :b
|
69
77
|
def initialize(a, b)
|
70
|
-
|
71
|
-
@
|
78
|
+
super()
|
79
|
+
@a = a unless instance_variable_defined?(:@a)
|
80
|
+
@b = b unless instance_variable_defined?(:@b)
|
72
81
|
end
|
73
82
|
end
|
74
83
|
|
@@ -94,8 +103,9 @@ end
|
|
94
103
|
class TestKW
|
95
104
|
attr_accessor :a, :b
|
96
105
|
def initialize(a:, b:)
|
97
|
-
|
98
|
-
@
|
106
|
+
super()
|
107
|
+
@a = a unless instance_variable_defined?(:@a)
|
108
|
+
@b = b unless instance_variable_defined?(:@b)
|
99
109
|
end
|
100
110
|
end
|
101
111
|
|
@@ -122,10 +132,11 @@ class TestAttrOptions
|
|
122
132
|
attr_reader :a
|
123
133
|
attr_accessor :b, :c
|
124
134
|
def initialize(b, d, a:, c:)
|
125
|
-
|
126
|
-
@
|
127
|
-
@
|
128
|
-
@
|
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)
|
129
140
|
end
|
130
141
|
end
|
131
142
|
|
@@ -147,6 +158,54 @@ test.a = 5
|
|
147
158
|
# => raises NoMethodError
|
148
159
|
```
|
149
160
|
|
161
|
+
## Default values for keyword arguments
|
162
|
+
```ruby
|
163
|
+
class TestDefaultKw
|
164
|
+
init [:a, kw: true],
|
165
|
+
:b,
|
166
|
+
kw: {default: 3}
|
167
|
+
end
|
168
|
+
|
169
|
+
# would be the same as
|
170
|
+
class TestDefaultKw
|
171
|
+
attr_accessor :a, :b
|
172
|
+
def initialize(a:, b: 3)
|
173
|
+
super()
|
174
|
+
@a = a unless instance_variable_defined?(:@a)
|
175
|
+
@b = b unless instance_variable_defined?(:@b)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
test = TestDefaultKw.new(a: 2)
|
180
|
+
|
181
|
+
test.a
|
182
|
+
# 2
|
183
|
+
|
184
|
+
test.b
|
185
|
+
# 3
|
186
|
+
```
|
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
|
+
|
150
209
|
## Mixed together
|
151
210
|
```ruby
|
152
211
|
class TestMixed
|
@@ -160,8 +219,9 @@ class TestMixed
|
|
160
219
|
attr_reader :a
|
161
220
|
attr_accessor :b
|
162
221
|
def initialize(a:, b:)
|
163
|
-
|
164
|
-
@
|
222
|
+
super()
|
223
|
+
@a = a unless instance_variable_defined?(:@a)
|
224
|
+
@b = b unless instance_variable_defined?(:@b)
|
165
225
|
end
|
166
226
|
end
|
167
227
|
|
@@ -5,93 +5,191 @@ module Trinkets
|
|
5
5
|
module Init
|
6
6
|
ATTR = %i[accessor reader writer none].freeze
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
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]
|
15
|
+
end
|
16
|
+
end
|
11
17
|
|
12
|
-
|
18
|
+
class BoundParameter < Parameter
|
13
19
|
|
14
|
-
|
20
|
+
attr_reader :value
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
22
|
+
def initialize(name:, attr:, kw:, super:, value:)
|
23
|
+
super(name:, attr:, kw:, super:)
|
24
|
+
@value = value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
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)
|
35
|
+
|
36
|
+
#@return [Parameters]
|
37
|
+
def self.build(params, **default_options)
|
38
|
+
|
39
|
+
raise ArgumentError, 'At least 1 attribute is required.' if params.empty?
|
40
|
+
|
41
|
+
unless ::Trinkets::Class::Init::ATTR.include?(default_options[:attr])
|
42
|
+
attr = default_options[:attr].inspect
|
43
|
+
raise ArgumentError, "wrong `attr` type (given #{attr}, expected :accessor (default), :reader, :writer or :none)"
|
19
44
|
end
|
20
45
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
46
|
+
# @type [Array[Parameter]]
|
47
|
+
params = params.map do |name, opts|
|
48
|
+
name = name.to_s.sub(/^@/, '').to_sym
|
49
|
+
|
50
|
+
opts ||= {}
|
51
|
+
opts.reject! { |_, v| v.nil? }
|
52
|
+
opts = default_options.merge(opts)
|
53
|
+
|
54
|
+
Parameter.new(name:, **opts)
|
25
55
|
end
|
26
|
-
attr_methods[attr].call(name) unless attr == :none
|
27
|
-
end
|
28
56
|
|
29
|
-
|
57
|
+
repeated_params = params.map(&:name)
|
58
|
+
.tally
|
59
|
+
.select { |_, count| count > 1 }
|
60
|
+
.keys
|
30
61
|
|
31
|
-
|
32
|
-
kw_attrs, attrs = attrs
|
33
|
-
.map { |name, opts| [name, opts[:kw]] }
|
34
|
-
.partition { |_, kw_opt| kw_opt }
|
35
|
-
.map(&:to_h)
|
62
|
+
raise ArgumentError, "duplicated argument names: #{repeated_params.join(', ')}" if repeated_params.any?
|
36
63
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
+
)
|
41
77
|
|
42
|
-
class << self
|
43
|
-
private def sanitize_attrs(attrs, default_attr_options)
|
44
|
-
# Normalize attrs into an array: [[:name, **options], ...]
|
45
|
-
# @type [Array[Array[Symbol, Hash]]]
|
46
|
-
attrs = attrs.map do |a|
|
47
|
-
name, opts = [*a]
|
48
|
-
name = name.to_s.sub(/^@/, '').to_sym
|
49
|
-
opts = default_attr_options.merge(opts || {})
|
50
|
-
[name, opts]
|
51
78
|
end
|
52
79
|
|
53
|
-
|
54
|
-
|
55
|
-
.select { |_, count| count > 1 }
|
56
|
-
.keys
|
80
|
+
#@return [Parameters]
|
81
|
+
def bind(values, kw_values)
|
57
82
|
|
58
|
-
|
83
|
+
validate values, kw_values
|
59
84
|
|
60
|
-
|
61
|
-
|
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
|
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
|
104
|
+
|
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
|
115
|
+
|
116
|
+
Parameters.new(req:, key_req:, key_opt:)
|
62
117
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
118
|
+
end
|
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)
|
67
125
|
|
68
|
-
unless
|
69
|
-
raise ArgumentError, "wrong number of arguments (given #{values.size}, expected #{
|
126
|
+
unless req.size == values.size
|
127
|
+
raise ArgumentError, "wrong number of arguments (given #{values.size}, expected #{req.size})"
|
70
128
|
end
|
71
129
|
|
72
|
-
missing_keys =
|
130
|
+
missing_keys = key_req.map(&:name) - kw_values.keys
|
73
131
|
unless missing_keys.empty?
|
74
|
-
missing_keys = missing_keys.
|
132
|
+
missing_keys = missing_keys.map(&:inspect).join(', ')
|
75
133
|
raise ArgumentError, "missing keywords: #{missing_keys}"
|
76
134
|
end
|
77
135
|
|
78
|
-
unknown_keywords = kw_values.except(*
|
136
|
+
unknown_keywords = kw_values.except(*all_key_params.map(&:name))
|
79
137
|
unless unknown_keywords.empty?
|
80
138
|
unknown_keywords = unknown_keywords.keys.map(&:to_sym).map(&:inspect).join(', ')
|
81
139
|
raise ArgumentError, "unknown keywords: #{unknown_keywords}"
|
82
140
|
end
|
83
141
|
|
84
|
-
|
85
|
-
|
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}")
|
86
153
|
end
|
87
154
|
|
88
|
-
|
89
|
-
|
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)"
|
90
158
|
end
|
159
|
+
attr_methods[attr].call(name) unless attr == :none
|
160
|
+
end
|
91
161
|
|
162
|
+
params.all_params.each do |param|
|
163
|
+
attr_init.call param.name, attr: param.attr
|
92
164
|
end
|
165
|
+
|
166
|
+
init_method = Init.send(:define_initialize, params)
|
167
|
+
define_method :initialize, init_method
|
93
168
|
end
|
94
|
-
end
|
95
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
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
96
194
|
end
|
97
195
|
end
|
data/lib/trinkets/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trinkets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SilverPhoenix99
|
@@ -65,7 +65,6 @@ homepage: https://github.com/SilverPhoenix99/trinkets
|
|
65
65
|
licenses:
|
66
66
|
- MIT
|
67
67
|
metadata:
|
68
|
-
homepage_uri: https://github.com/SilverPhoenix99/trinkets
|
69
68
|
source_code_uri: https://github.com/SilverPhoenix99/trinkets
|
70
69
|
changelog_uri: https://github.com/SilverPhoenix99/trinkets/blob/master/CHANGELOG.md
|
71
70
|
bug_tracker_uri: https://github.com/SilverPhoenix99/trinkets/issues
|