marameters 0.1.0 → 0.2.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: 0e9a67405793129c6913e84a4ead90cbc6eda2abbec139f00764a677aa6d30b1
4
- data.tar.gz: fe3d699a7717e37ae79d07773870cf1137ad0b4b14a761c3eaaa56ed9a4f9fdd
3
+ metadata.gz: a3daee40e52b7df55680618be8c5a37a6a62c3ca7f62b88128fbc724f4222c50
4
+ data.tar.gz: 3b4d527a40fc8ed6500faea3e6676b45be484a2fb9288b5e79ee38684ac0bec0
5
5
  SHA512:
6
- metadata.gz: ded0a8de22a2f67bdb2f308bbfc02db520fb98028b1595825d9e4ab82396b6bdb062a5a602c1df52ced38862963c5b0eb3285cdee586a6934f5f888a6777d313
7
- data.tar.gz: bc840a326f4116973cda051c23b411f92e74b53e2f704e69adb9536159ef178a16b094c29f52b038dfb17af65be2f26fbe10e7d062da2b5e222bb704895a7793
6
+ metadata.gz: 459d04fcc654dcf3ef58d23dfaaff01f5df52e0ffbb8d670cb0d1a9f59f10f91d0e9ff424e5871c9e892a1ba7f2b4cbdb236a287e3b3ffbdf32a1e56de9b5f97
7
+ data.tar.gz: 92df818d39aeacd231721b69299e0e23705b0f889db54360fefd0f269355fcbf40499b62c59bfc30a22b99dac39874b8a168c381e825ae4b2891a18b6fd9f53f
checksums.yaml.gz.sig CHANGED
Binary file
data/README.adoc CHANGED
@@ -14,7 +14,8 @@ difference between a method's parameters and arguments is:
14
14
  Example: `demo 1, two: 2`.
15
15
 
16
16
  This gem will help you debug methods or -- more importantly -- aid your workflow when
17
- metaprogramming and architecting more sophisticated applications.
17
+ metaprogramming, as used in the link:https://www.alchemists.io/projects/auto_injector[Auto Injector]
18
+ gem, when architecting more sophisticated applications.
18
19
 
19
20
  toc::[]
20
21
 
@@ -45,8 +46,16 @@ gem "marameters"
45
46
 
46
47
  == Usage
47
48
 
48
- The primary object you'll want to interact with is the `Marameters::Analyzer` class. To understand
49
- how to _analyze_ a method's parameters, consider the following demonstration class:
49
+ There are two main objects you'll want to interact with:
50
+
51
+ * *Analyzer*: Allows you to analyze a method's parameters.
52
+ * *Signature*: Allows you to dynamically build a method signature.
53
+
54
+ Both of these objects are meant to serve as building blocks to more complex architectures.
55
+
56
+ === Analyzer
57
+
58
+ To understand how to _analyze_ a method's parameters, consider the following demonstration class:
50
59
 
51
60
  [source,ruby]
52
61
  ----
@@ -55,21 +64,23 @@ class Demo
55
64
  @logger = logger
56
65
  end
57
66
 
58
- def example one, two = nil, *three, four:, five: nil, **six, &seven
67
+ def all one, two = nil, *three, four:, five: nil, **six, &seven
59
68
  logger.debug [one, two, three, four, five, six, seven]
60
69
  end
61
70
 
71
+ def none = logger.debug "Nothing to see here."
72
+
62
73
  private
63
74
 
64
75
  attr_reader :logger
65
76
  end
66
77
  ----
67
78
 
68
- We can then analyze the above `#example` method's parameters as follows:
79
+ You can then analyze the `#all` method's parameters as follows:
69
80
 
70
81
  [source,ruby]
71
82
  ----
72
- analyzer = Marameters::Analyzer.new Demo.instance_method(:example).parameters
83
+ analyzer = Marameters::Analyzer.new Demo.instance_method(:all).parameters
73
84
 
74
85
  analyzer.block # :seven
75
86
  analyzer.block? # true
@@ -91,6 +102,119 @@ analyzer.to_a # [[:req, :one], [:opt, :two], [:rest, :three], [:
91
102
  analyzer.to_h # {:req=>:one, :opt=>:two, :rest=>:three, :keyreq=>:four, :key=>:five, :keyrest=>:six, :block=>:seven}
92
103
  ----
93
104
 
105
+ In contrast the above, we can also analyze the `#none` method which has no parameters for a
106
+ completely different result:
107
+
108
+ [source,ruby]
109
+ ----
110
+ analyzer = Marameters::Analyzer.new Demo.instance_method(:none).parameters
111
+
112
+ analyzer.block # nil
113
+ analyzer.block? # false
114
+ analyzer.empty? # true
115
+ analyzer.keywords # []
116
+ analyzer.keywords? # false
117
+ analyzer.kind?(:req) # true
118
+ analyzer.kinds # []
119
+ analyzer.name?(:three) # false
120
+ analyzer.names # []
121
+ analyzer.only_bare_splats? # false
122
+ analyzer.only_double_splats? # false
123
+ analyzer.only_single_splats? # false
124
+ analyzer.positionals # []
125
+ analyzer.positionals? # false
126
+ analyzer.splats # []
127
+ analyzer.splats? # false
128
+ analyzer.to_a # []
129
+ analyzer.to_h # {}
130
+ ----
131
+
132
+ === Signature
133
+
134
+ The signature class is the opposite of the analyzer in that you want to feed it parameters for
135
+ turning into a method signature. This is useful when dynamically building method signatures or using
136
+ the same signature for multiple methods when metaprogramming.
137
+
138
+ The following demonstrates how you might construct a method signature with all possible parameters:
139
+
140
+ [source,ruby]
141
+ ----
142
+ signature = Marameters::Signature.new(
143
+ {
144
+ req: :one,
145
+ opt: [:two, 2],
146
+ rest: :three,
147
+ keyreq: :four,
148
+ key: [:five, 5],
149
+ keyrest: :six,
150
+ block: :seven
151
+ }
152
+ )
153
+
154
+ puts signature
155
+ # one, two = 2, *three, four:, five: 5, **six, &seven
156
+ ----
157
+
158
+ You'll notice that the parameters is a hash _and_ some values can be tuples. The reason is that it's
159
+ easier to write a hash than a double nested array as normally produced by the analyzer or directly
160
+ from `Method#parameters`. The optional positional and keyword parameters use tuples because you
161
+ might want to supply a default value and this provides a way for you to do that with minimal syntax.
162
+ This can be demonstrated further by using optional keywords (same applies for optional positionals):
163
+
164
+ [source,ruby]
165
+ ----
166
+ # With no default
167
+ puts Marameters::Signature.new({key: :demo})
168
+ # demo: nil
169
+
170
+ # With explicit nil as default
171
+ puts Marameters::Signature.new({key: [:demo, nil]})
172
+ # demo: nil
173
+
174
+ # With string as default
175
+ puts Marameters::Signature.new({key: [:demo, "test"]})
176
+ # demo: "test"
177
+
178
+ # With symbol as default
179
+ puts Marameters::Signature.new({key: [:demo, :test]})
180
+ # demo: :test
181
+
182
+ # With object(dependency) as default
183
+ puts Marameters::Signature.new({key: [:demo, "*Object.new"]})
184
+ # demo: Object.new
185
+ ----
186
+
187
+ In the case of object dependencies you need to wrap these in a string _and_ prefix them with a star
188
+ (`*`) so the signature builder won't confuse these as a normal string. There are two reasons why
189
+ this is important:
190
+
191
+ * The star (`*`) signifies that you want the object to be passed through without any further
192
+ processing while also not being confused normal strings.
193
+ * Objects wrapped as strings allows your dependency to be lazy loaded. Otherwise, if `Object.new`
194
+ was pass directly, you'd be passing the evaluated instance (i.e. `#<Object:0x0000000107df4028>`)
195
+ which is not what you want until much later when your method is defined.
196
+
197
+ When you put all of this together, you can dynamically build a method as follows:
198
+
199
+ [source,ruby]
200
+ ----
201
+ signature = Marameters::Signature.new({opt: [:text, "This is a test."]})
202
+
203
+ Example = Module.new do
204
+ module_eval <<~DEFINITION, __FILE__, __LINE__ + 1
205
+ def self.say #{signature}
206
+ text
207
+ end
208
+ DEFINITION
209
+ end
210
+
211
+ puts Example.say
212
+ # This is a test.
213
+
214
+ puts Example.say "Hello"
215
+ # Hello
216
+ ----
217
+
94
218
  == Development
95
219
 
96
220
  You can also use the IRB console for direct access to all objects:
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Marameters
4
+ # Builds a single parameter of a method's parameter signature.
5
+ class Builder
6
+ def initialize defaulter: Defaulter
7
+ @defaulter = defaulter
8
+ end
9
+
10
+ # :reek:DuplicateMethodCall
11
+ def call kind, name, default: nil
12
+ case kind
13
+ when :req then name
14
+ when :opt then "#{name} = #{defaulter.call default}"
15
+ when :rest then "*#{name}"
16
+ when :keyreq then "#{name}:"
17
+ when :key then "#{name}: #{defaulter.call default}"
18
+ when :keyrest then "**#{name}"
19
+ when :block then "&#{name}"
20
+ else fail ArgumentError, "Wrong kind (#{kind}), name (#{name}), or default (#{default})."
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :defaulter
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Marameters
4
+ # Calculates the default for a given value when used within a method's parameter.
5
+ class Defaulter
6
+ PASSTHROUGH = "*"
7
+
8
+ def self.call(...) = new(...).call
9
+
10
+ def initialize value, passthrough: PASSTHROUGH
11
+ @value = value
12
+ @passthrough = passthrough
13
+ end
14
+
15
+ def call
16
+ case value
17
+ when nil then "nil"
18
+ when /\A#{Regexp.escape passthrough}/ then value.delete_prefix passthrough
19
+ when String then value.dump
20
+ when Symbol then value.inspect
21
+ else value
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :value, :passthrough
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Marameters
4
+ # Produces a method signature for given parameters.
5
+ class Signature
6
+ def initialize parameters, builder: Builder.new
7
+ @parameters = parameters
8
+ @builder = builder
9
+ end
10
+
11
+ def to_s = build.join ", "
12
+
13
+ private
14
+
15
+ attr_reader :parameters, :builder
16
+
17
+ def build
18
+ parameters.reduce [] do |signature, (kind, (name, default))|
19
+ signature << builder.call(kind, name, default:)
20
+ end
21
+ end
22
+ end
23
+ end
data/marameters.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "marameters"
5
- spec.version = "0.1.0"
5
+ spec.version = "0.2.0"
6
6
  spec.authors = ["Brooke Kuhlmann"]
7
7
  spec.email = ["brooke@alchemists.io"]
8
8
  spec.homepage = "https://www.alchemists.io/projects/marameters"
9
- spec.summary = "Provides method parameter introspection which is useful when metaprogramming."
9
+ spec.summary = "Provides dynamic method parameter construction and deconstruction."
10
10
  spec.license = "Hippocratic-2.1"
11
11
 
12
12
  spec.metadata = {
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marameters
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -28,7 +28,7 @@ cert_chain:
28
28
  lkHilIrX69jq8wMPpBhlaw2mRmeSL50Wv5u6xVBvOHhXFSP1crXM95vfLhLyRYod
29
29
  W2A=
30
30
  -----END CERTIFICATE-----
31
- date: 2022-03-11 00:00:00.000000000 Z
31
+ date: 2022-03-12 00:00:00.000000000 Z
32
32
  dependencies:
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: refinements
@@ -71,6 +71,9 @@ files:
71
71
  - README.adoc
72
72
  - lib/marameters.rb
73
73
  - lib/marameters/analyzer.rb
74
+ - lib/marameters/builder.rb
75
+ - lib/marameters/defaulter.rb
76
+ - lib/marameters/signature.rb
74
77
  - marameters.gemspec
75
78
  homepage: https://www.alchemists.io/projects/marameters
76
79
  licenses:
@@ -97,8 +100,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
100
  - !ruby/object:Gem::Version
98
101
  version: '0'
99
102
  requirements: []
100
- rubygems_version: 3.3.8
103
+ rubygems_version: 3.3.9
101
104
  signing_key:
102
105
  specification_version: 4
103
- summary: Provides method parameter introspection which is useful when metaprogramming.
106
+ summary: Provides dynamic method parameter construction and deconstruction.
104
107
  test_files: []
metadata.gz.sig CHANGED
Binary file