trinkets 0.3.0 → 0.4.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: '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: