trinkets 0.3.0 → 0.4.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: '018b1a7eeedc27c57853bd408ac7b698f1573f7b2646c7fdb8f36e2dce2b0add'
4
- data.tar.gz: 50d9ec8927e0f127c17a410148c923e382b2749afe5ba87f13ef6d518349ecd4
3
+ metadata.gz: 1b95bff0eaa948f0edc0375709fd3ec786ad10e970f5b5abcd501247dceb1e74
4
+ data.tar.gz: 3d4d1bce01e034167209cad41d9bf4ad356d79ddf100cfc1968d16b9686a06f9
5
5
  SHA512:
6
- metadata.gz: 73b50ad1d15f661512a2c1b230b993b6d4a9a56d5a8a72ebc9a7cd9cfe03989bd32897131f1991a071daaac50fdad2eaf8fb0cbfd4dd60430ea6de74c3cddbcc
7
- data.tar.gz: 8643d7e712ef9798b459cb9dc28f35e72f4dc80215e2b860c7a23a06a4c029dac83d7271324f2605d6812d30c40ad96032d348fca2c1eeb62812c7923666e81f
6
+ metadata.gz: 5b3a7cc743fae16ee12cfab9d9efc4236b31d69d8a570fe1bbf009cc3558be08d3cef3a38aa110403fb40dea019a3236adbf4a00c983c5f1f4e148a909bfb884
7
+ data.tar.gz: e94881a3dc54ca93be7ea759b0b6aff8607b9d91d7d1247d3ec2f59d265ecb2ea4c7f014754ca9a4a351ee6994eabb2fcae0e93fe0d14d27a46b01985370c3f6
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
1
 
2
+ ### [0.4.0](https://github.com/SilverPhoenix99/trinkets/tree/v0.4.0)
3
+ * `Class::init`: Allow default values for keyword arguments.
4
+
5
+ ### [0.3.1](https://github.com/SilverPhoenix99/trinkets/tree/v0.3.1)
6
+ * Fixed repeated arguments in `Class::init`.
7
+
2
8
  ### [0.3.0](https://github.com/SilverPhoenix99/trinkets/tree/v0.3.0)
3
9
  * Restructured folders to default to `/refine/`.
4
10
  * Added `Enumerable#each_with_hash`/`Enumerator#with_hash`.
data/README.md CHANGED
@@ -4,10 +4,16 @@ It's the bootleg [facets](https://github.com/rubyworks/facets?tab=readme-ov-file
4
4
 
5
5
  ## Installation
6
6
 
7
+ ### RubyGems
7
8
  ```
8
9
  gem install trinkets
9
10
  ```
10
11
 
12
+ ### Bundler
13
+ ```ruby
14
+ gem 'trinkets', require: false
15
+ ```
16
+
11
17
  ## Usage
12
18
 
13
19
  The trinkets are loaded with the following structure:
@@ -34,7 +40,7 @@ The `refine` subdirectory is the default, and it can be omitted from `require`.
34
40
  require 'trinkets/refine/class/init'
35
41
  ```
36
42
 
37
- ### Extend
43
+ ### Extend / Include
38
44
 
39
45
  ```ruby
40
46
  require 'trinkets/extend/class/init'
@@ -44,6 +50,15 @@ class Test
44
50
  end
45
51
  ```
46
52
 
53
+ ```ruby
54
+ require 'trinkets/include/enumerable/each_with_hash'
55
+
56
+ class Test
57
+ include Enumerable
58
+ include ::Trinkets::Enumerable::WithHash
59
+ end
60
+ ```
61
+
47
62
  ### Mokey Patching
48
63
 
49
64
  ```ruby
data/doc/class/init.md CHANGED
@@ -9,6 +9,10 @@ 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`
13
17
 
14
18
  The same options can be used per individual argument.
@@ -147,6 +151,33 @@ test.a = 5
147
151
  # => raises NoMethodError
148
152
  ```
149
153
 
154
+ ## Default values for keyword arguments
155
+
156
+ ```ruby
157
+ class TestDefaultKw
158
+ init [:a, kw: true],
159
+ :b,
160
+ kw: {default: 3}
161
+ end
162
+
163
+ # would be the same as
164
+ class TestDefaultKw
165
+ attr_accessor :a, :b
166
+ def initialize(a: , b: 3)
167
+ @a = a
168
+ @b = b
169
+ end
170
+ end
171
+
172
+ test = TestDefaultKw.new(a: 2)
173
+
174
+ test.a
175
+ # 2
176
+
177
+ test.b
178
+ # 3
179
+ ```
180
+
150
181
  ## Mixed together
151
182
  ```ruby
152
183
  class TestMixed
@@ -6,22 +6,9 @@ module Trinkets
6
6
  ATTR = %i[accessor reader writer none].freeze
7
7
 
8
8
  def init(*attrs, attr: ATTR.first, kw: false)
9
- raise ArgumentError, 'At least 1 attribute is required.' if attrs.empty?
10
- raise ArgumentError, '`attr` must be one of :accessor (default), :reader, :writer or :none' unless ATTR.include?(attr)
11
-
12
- default_attr_options = { attr: attr, kw: kw }
13
-
14
- # Normalize attrs into a hash: { :name => **options }
15
- # @type [Hash[Symbol, Hash]]
16
- attrs = attrs.map { |a| [*a] }
17
- .map { |a| a.size == 1 ? a << {} : a }
18
- .each_with_object({}) do |(name, opts), h|
19
- # name, opts = a
20
- name = name.to_s.sub(/^@/, '').to_sym
21
- opts = default_attr_options.merge(opts)
22
- h[name] = opts
23
- end
9
+ attrs = Init.send(:sanitize_attrs, attrs, attr: attr, kw: kw)
24
10
 
11
+ # @type [Hash[Symbol, Method]]
25
12
  attr_methods = (ATTR - [:none])
26
13
  .each_with_object({}) do |name, h|
27
14
  h[name] = method("attr_#{name}")
@@ -30,57 +17,98 @@ module Trinkets
30
17
  # even though options like `kw` aren't used, they serve here to validate the `attrs` options
31
18
  attr_init = ->(name, attr: ATTR.first, kw: false) do
32
19
  unless ATTR.include?(attr)
33
- raise ArgumentError, "attr `#{name}`, option attr` must be one of :accessor (default), :reader, :writer or :none"
20
+ raise ArgumentError, "wrong `attr` type for `#{name.inspect}` (given #{attr.inspect}, expected :accessor (default), :reader, :writer or :none)"
34
21
  end
35
22
  attr_methods[attr].call(name) unless attr == :none
36
23
  end
37
24
 
38
25
  attrs.each { |name, opts| attr_init.call(name, **opts) }
39
26
 
40
- # 2 hashes: { :name => bool }
41
- kw_attrs, attrs = attrs
42
- .map { |name, opts| [name, opts[:kw]] }
43
- .partition { |_, kw_opt| kw_opt }
44
- .map(&:to_h)
45
-
46
- init_method = ::Trinkets::Class.send :define_initialize, attrs, kw_attrs
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 }
35
+
36
+ pos_params = [*grouped_params[FalseClass]].map(&:first)
37
+ kw_params = [*grouped_params[TrueClass]].map(&:first)
38
+ opt_kw_params = [*grouped_params[Hash]].to_h
39
+ .transform_values! { _1[:default] }
40
+
41
+ init_method = Init.send(:define_initialize, pos_params, kw_params, opt_kw_params)
47
42
  define_method :initialize, init_method
48
43
  end
49
- end
50
44
 
51
- class << self
52
- # @param [Hash[Symbol Boolean]] attrs
53
- # @param [Hash[Symbol Boolean]] kw_attrs
54
- private def define_initialize(attrs, kw_attrs)
55
- ->(*values, **kw_values) do
45
+ class << self
46
+ private def sanitize_attrs(attrs, **default_options)
56
47
 
57
- unless attrs.size == values.size
58
- raise ArgumentError, "wrong number of arguments (given #{values.size}, expected #{attrs.size})"
59
- end
48
+ raise ArgumentError, 'At least 1 attribute is required.' if attrs.empty?
60
49
 
61
- missing_keys = kw_attrs.except(*kw_values.keys)
62
- unless missing_keys.empty?
63
- missing_keys = missing_keys.keys.map(&:inspect).join(', ')
64
- raise ArgumentError, "missing keywords: #{missing_keys}"
50
+ unless ::Trinkets::Class::Init::ATTR.include?(default_options[:attr])
51
+ attr = default_options[:attr].inspect
52
+ raise ArgumentError, "wrong `attr` type (given #{attr}, expected :accessor (default), :reader, :writer or :none)"
65
53
  end
66
54
 
67
- unknown_keywords = kw_values.except(*kw_attrs.keys)
68
- unless unknown_keywords.empty?
69
- unknown_keywords = unknown_keywords.keys.map(&:to_sym).map(&:inspect).join(', ')
70
- raise ArgumentError, "unknown keywords: #{unknown_keywords}"
55
+ # Normalize attrs into an array: [[:name, **options], ...]
56
+ # @type [Array[Array[Symbol, Hash]]]
57
+ attrs = attrs.map do |a|
58
+ name, opts = [*a]
59
+ name = name.to_s.sub(/^@/, '').to_sym
60
+ opts = default_options.merge(opts || {})
61
+ [name, opts]
71
62
  end
72
63
 
73
- attrs.keys.zip(values).each do |name, value|
74
- instance_variable_set "@#{name}", value
75
- end
64
+ repeated_attrs = attrs.map(&:first)
65
+ .tally
66
+ .select { |_, count| count > 1 }
67
+ .keys
76
68
 
77
- kw_values.each do |name, value|
78
- instance_variable_set "@#{name}", value
79
- end
69
+ raise ArgumentError, "duplicated argument names: #{repeated_attrs.join(', ')}" if repeated_attrs.any?
80
70
 
71
+ attrs.to_h
72
+ end
73
+
74
+ # @param [Array[Symbol]] pos_params
75
+ # @param [Array[Symbol]] kw_params
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
79
+
80
+ unless pos_params.size == values.size
81
+ raise ArgumentError, "wrong number of arguments (given #{values.size}, expected #{pos_params.size})"
82
+ end
83
+
84
+ missing_keys = kw_params - kw_values.keys
85
+ unless missing_keys.empty?
86
+ missing_keys = missing_keys.map(&:inspect).join(', ')
87
+ raise ArgumentError, "missing keywords: #{missing_keys}"
88
+ end
89
+
90
+ unknown_keywords = kw_values.except(*kw_params, *opt_kw_params.keys)
91
+ unless unknown_keywords.empty?
92
+ unknown_keywords = unknown_keywords.keys.map(&:to_sym).map(&:inspect).join(', ')
93
+ raise ArgumentError, "unknown keywords: #{unknown_keywords}"
94
+ end
95
+
96
+ pos_params.zip(values).each do |name, value|
97
+ instance_variable_set "@#{name}", value
98
+ end
99
+
100
+ kw_params.each do |name|
101
+ instance_variable_set "@#{name}", kw_values[name]
102
+ end
103
+
104
+ opt_kw_params.each do |name, default_value|
105
+ value = kw_values.include?(name) ? kw_values[name] : default_value
106
+ instance_variable_set "@#{name}", value
107
+ end
108
+
109
+ end
81
110
  end
82
111
  end
83
112
  end
84
-
85
113
  end
86
114
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Trinkets
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SilverPhoenix99
@@ -9,8 +9,36 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-02-03 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2024-02-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '3.0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '3.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: simplecov
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.22.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.22.0
14
42
  description: Truly outrageous bootleg facets in your trinkets box.
15
43
  email:
16
44
  - antoniopedrosilvapinto@gmail.com
@@ -37,7 +65,10 @@ homepage: https://github.com/SilverPhoenix99/trinkets
37
65
  licenses:
38
66
  - MIT
39
67
  metadata:
40
- homepage_uri: https://github.com/SilverPhoenix99/trinkets
68
+ source_code_uri: https://github.com/SilverPhoenix99/trinkets
69
+ changelog_uri: https://github.com/SilverPhoenix99/trinkets/blob/master/CHANGELOG.md
70
+ bug_tracker_uri: https://github.com/SilverPhoenix99/trinkets/issues
71
+ documentation_uri: https://github.com/SilverPhoenix99/trinkets/blob/master/README.md
41
72
  post_install_message:
42
73
  rdoc_options: []
43
74
  require_paths: