simple_input 0.2.1 → 0.2.2

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: 7bc4897e13a98263144b74d7f41c52a5551a5fd82137700fc81f7fef5ca27271
4
- data.tar.gz: 111cec79eef33fe0c657f79d42a29020b8764880513df2839d676fc926bf96bf
3
+ metadata.gz: 067ce86ac266a279491b935536c473fe2b257e9a3cd3556877c7affe2be3311c
4
+ data.tar.gz: e0f078fa1ee462081ed0d2da0d76f44461b6e90d1d595cf1beea1186574937e6
5
5
  SHA512:
6
- metadata.gz: f2a86db2a363fdef0d0b75c71b9c9b962af329a171f10ae97879a52950e6a75374ed569645217ec32356ff637f1b34ba085edf242040681be60eeed56635e335
7
- data.tar.gz: 6a6b0061f158c692db24a9ab149ad79d71ea0d49adb49f43bef20973299cd40a7b7443962e45bf4ed467418ff1dd1537426362f2ad1031d68124b32f4ef6b803
6
+ metadata.gz: c488763914f58752e2f230e78df81e55bc88b53587218ddfeee91ac855c35ffb18a4c8260911615652c058803fd36acf2ee64a53840b704245af23d9a01da736
7
+ data.tar.gz: f165c355abc6a8776b38bdbc47f2b35991cee593c8ccc776304fca1cf60260186a8b361b7b559225cd2a080aebf31f4544c7eaf4e70f8f34146b23e2c8609391
data/README.md CHANGED
@@ -167,6 +167,92 @@ age = Input.new_input
167
167
 
168
168
  All methods (except `#run`) return `self` for chaining.
169
169
 
170
+ ## Complete Example: Area Calculator
171
+
172
+ Here's a practical example that demonstrates validation, conversion, and dependency injection for testing:
173
+
174
+ ```ruby
175
+ class AreaCalculator
176
+ def initialize(reader = $stdin, writer = $stdout)
177
+ @reader = reader
178
+ @writer = writer
179
+ end
180
+
181
+ # Reusable conversion function
182
+ def to_number
183
+ ->(value) do
184
+ if value.to_i.to_s == value
185
+ value.to_i
186
+ elsif value.to_f.to_s == value
187
+ value.to_f
188
+ else
189
+ raise ArgumentError, "Invalid number: #{value}"
190
+ end
191
+ end
192
+ end
193
+
194
+ def input_length
195
+ Input.new_input
196
+ .send(:with_context, @reader, @writer)
197
+ .title('What is the length of the room in feet?')
198
+ .validate { |value| value.to_i > 0 ? nil : 'Length must be a positive number' }
199
+ .convert_func(to_number)
200
+ .run
201
+ end
202
+
203
+ def input_width
204
+ Input.new_input
205
+ .send(:with_context, @reader, @writer)
206
+ .title('What is the width of the room in feet?')
207
+ .validate { |value| value.to_i > 0 ? nil : 'Width must be a positive number' }
208
+ .convert_func(to_number)
209
+ .run
210
+ end
211
+
212
+ def calculate_area
213
+ length = input_length
214
+ width = input_width
215
+ area = length * width
216
+
217
+ @writer.puts "\nYou entered dimensions of #{length} feet by #{width} feet."
218
+ @writer.puts "The area is #{area} square feet"
219
+ end
220
+ end
221
+ ```
222
+
223
+ This example can be tested with RSpec using mocks:
224
+
225
+ ```ruby
226
+ RSpec.describe AreaCalculator do
227
+ let(:writer) { double('writer') }
228
+ let(:reader) { double('reader') }
229
+ subject(:calculator) { AreaCalculator.new(reader, writer) }
230
+
231
+ before do
232
+ allow(writer).to receive(:puts)
233
+ allow(writer).to receive(:print)
234
+ end
235
+
236
+ it 'returns integer for valid integer input' do
237
+ allow(reader).to receive(:gets).and_return("10\n")
238
+
239
+ result = calculator.input_length
240
+
241
+ expect(result).to eq(10)
242
+ end
243
+
244
+ it 'returns float for valid float input' do
245
+ allow(reader).to receive(:gets).and_return("10.5\n")
246
+
247
+ result = calculator.input_length
248
+
249
+ expect(result).to eq(10.5)
250
+ end
251
+ end
252
+ ```
253
+
254
+ See `examples/area_calculator.rb` and `examples/area_calculator_spec.rb` for the complete implementation.
255
+
170
256
  ## Running Examples
171
257
 
172
258
  ```bash
@@ -184,6 +270,9 @@ ruby examples/validation_patterns.rb
184
270
 
185
271
  # Error handling and conversion safety
186
272
  ruby examples/error_handling.rb
273
+
274
+ # Area calculator with numeric conversion (practical example)
275
+ ruby examples/area_calculator.rb
187
276
  ```
188
277
 
189
278
  ## Testing
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/simple_prompt'
5
+
6
+ # Example: Area Calculator with Input Validation and Conversion
7
+ # This example demonstrates using Input with:
8
+ # - Custom validation for positive numbers
9
+ # - Value conversion from string to integer/float
10
+ # - Reusable conversion function
11
+
12
+ class AreaCalculator
13
+ def initialize(reader = $stdin, writer = $stdout)
14
+ @reader = reader
15
+ @writer = writer
16
+ end
17
+
18
+ # Conversion function that converts string to appropriate numeric type
19
+ def to_number
20
+ ->(value) do
21
+ if value.to_i.to_s == value
22
+ value.to_i
23
+ elsif value.to_f.to_s == value
24
+ value.to_f
25
+ else
26
+ raise ArgumentError, "Invalid number: #{value}"
27
+ end
28
+ end
29
+ end
30
+
31
+ def input_length
32
+ Input.new_input
33
+ .send(:with_context, @reader, @writer)
34
+ .title('What is the length of the room in feet?')
35
+ .validate { |value| value.to_i > 0 ? nil : 'Length must be a positive number' }
36
+ .convert_func(to_number)
37
+ .run
38
+ end
39
+
40
+ def input_width
41
+ Input.new_input
42
+ .send(:with_context, @reader, @writer)
43
+ .title('What is the width of the room in feet?')
44
+ .validate { |value| value.to_i > 0 ? nil : 'Width must be a positive number' }
45
+ .convert_func(to_number)
46
+ .run
47
+ end
48
+
49
+ def calculate_area
50
+ length = input_length
51
+ width = input_width
52
+ area = length * width
53
+
54
+ @writer.puts "\nYou entered dimensions of #{length} feet by #{width} feet."
55
+ @writer.puts "The area is"
56
+ @writer.puts "#{area} square feet"
57
+ @writer.puts "#{(area * 0.09290304).round(3)} square meters"
58
+ end
59
+ end
60
+
61
+ # Run the calculator if this file is executed directly
62
+ if __FILE__ == $PROGRAM_NAME
63
+ calculator = AreaCalculator.new
64
+ calculator.calculate_area
65
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/simple_prompt'
4
+ require_relative 'area_calculator'
5
+
6
+ # Example RSpec tests for area_calculator.rb
7
+ # This demonstrates how to test Input with mocks
8
+
9
+ RSpec.describe AreaCalculator do
10
+ let(:writer) { double('writer') }
11
+ let(:reader) { double('reader') }
12
+ subject(:calculator) { AreaCalculator.new(reader, writer) }
13
+
14
+ before do
15
+ allow(writer).to receive(:puts)
16
+ allow(writer).to receive(:print)
17
+ end
18
+
19
+ describe '#input_length' do
20
+ it 'returns integer for valid integer input' do
21
+ allow(reader).to receive(:gets).and_return("10\n")
22
+
23
+ result = calculator.input_length
24
+
25
+ expect(result).to eq(10)
26
+ end
27
+
28
+ it 'returns float for valid float input' do
29
+ allow(reader).to receive(:gets).and_return("10.5\n")
30
+
31
+ result = calculator.input_length
32
+
33
+ expect(result).to eq(10.5)
34
+ end
35
+ end
36
+
37
+ describe '#input_width' do
38
+ it 'returns integer for valid integer input' do
39
+ allow(reader).to receive(:gets).and_return("20\n")
40
+
41
+ result = calculator.input_width
42
+
43
+ expect(result).to eq(20)
44
+ end
45
+
46
+ it 'returns float for valid float input' do
47
+ allow(reader).to receive(:gets).and_return("20.3\n")
48
+
49
+ result = calculator.input_width
50
+
51
+ expect(result).to eq(20.3)
52
+ end
53
+ end
54
+ end
@@ -10,6 +10,7 @@ class Input
10
10
 
11
11
  ValidatorProc = T.type_alias { T.proc.params(arg0: String).returns(T.nilable(String)) }
12
12
  ValidatorType = T.type_alias { T.any(Symbol, ValidatorProc) }
13
+ ConvertProc = T.type_alias { T.proc.params(arg0: String).returns(T.untyped) }
13
14
 
14
15
  sig { returns(Input) }
15
16
  def self.new_input; new; end
@@ -18,12 +19,13 @@ class Input
18
19
 
19
20
  sig { void }
20
21
  def initialize
21
- @reader = T.let($stdin, T.any(IO, StringIO))
22
- @writer = T.let($stdout, T.any(IO, StringIO))
22
+ @reader = T.let($stdin, T.untyped)
23
+ @writer = T.let($stdout, T.untyped)
23
24
  @title = T.let('', String)
24
25
  @prompt = T.let('> ', String)
25
26
  @validators = T.let([], T::Array[ValidatorProc])
26
27
  @validator_provider = T.let(DefaultValidators, T.untyped)
28
+ @convert_func = T.let(nil, T.nilable(ConvertProc))
27
29
  end
28
30
 
29
31
  sig { params(text: String).returns(T.self_type) }
@@ -35,6 +37,12 @@ class Input
35
37
  sig { params(provider: T.untyped).returns(T.self_type) }
36
38
  def with_validators(provider); @validator_provider = provider; self; end
37
39
 
40
+ sig { params(converter: T.nilable(ConvertProc), block: T.nilable(ConvertProc)).returns(T.self_type) }
41
+ def convert_func(converter = nil, &block)
42
+ @convert_func = block || converter
43
+ self
44
+ end
45
+
38
46
  sig { params(validator: T.nilable(ValidatorType), block: T.nilable(ValidatorProc)).returns(T.self_type) }
39
47
  def validate(validator = nil, &block)
40
48
  if block
@@ -51,7 +59,7 @@ class Input
51
59
  self
52
60
  end
53
61
 
54
- sig { returns(String) }
62
+ sig { returns(T.untyped) }
55
63
  def run
56
64
  loop do
57
65
  @writer.puts "\e[1m#{@title}\e[0m" unless @title.empty?
@@ -64,7 +72,13 @@ class Input
64
72
  error = T.let(nil, T.nilable(String))
65
73
  @validators.each { |v| break if (error = v.call(val)) }
66
74
 
67
- return val unless error
75
+ unless error
76
+ begin
77
+ return @convert_func ? @convert_func.call(val) : val
78
+ rescue StandardError => e
79
+ error = "Conversion error: #{e.message} (please add validation)"
80
+ end
81
+ end
68
82
 
69
83
  # 視認性のための余白
70
84
  @writer.puts ''
@@ -78,6 +92,6 @@ class Input
78
92
 
79
93
  private
80
94
 
81
- sig { params(r: T.any(IO, StringIO), w: T.any(IO, StringIO)).returns(T.self_type) }
82
- def with_context(r, w); @reader = r; @writer = w; self; end
95
+ sig { params(reader: T.untyped, writer: T.untyped).returns(T.self_type) }
96
+ def with_context(reader, writer); @reader = reader; @writer = writer; self; end
83
97
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimplePrompt
4
- VERSION = '0.2.1'
4
+ VERSION = '0.2.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_input
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroaki Satou
@@ -104,6 +104,8 @@ extra_rdoc_files: []
104
104
  files:
105
105
  - LICENSE
106
106
  - README.md
107
+ - examples/area_calculator.rb
108
+ - examples/area_calculator_spec.rb
107
109
  - examples/basic.rb
108
110
  - examples/convert.rb
109
111
  - examples/custom_validators.rb