tabscanner 0.1.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 +7 -0
- data/.rspec +3 -0
- data/README.md +582 -0
- data/Rakefile +8 -0
- data/docs/architecture.md +124 -0
- data/docs/prd.md +124 -0
- data/docs/stories/1.1.story.md +229 -0
- data/docs/stories/1.2.story.md +255 -0
- data/docs/stories/1.3.story.md +246 -0
- data/docs/stories/1.4.story.md +152 -0
- data/docs/stories/1.5.story.md +149 -0
- data/docs/stories/1.6.story.md +166 -0
- data/docs/stories/2.1.story.md +216 -0
- data/examples/README.md +85 -0
- data/examples/batch_process.rb +56 -0
- data/examples/check_credits.rb +75 -0
- data/examples/process_receipt.rb +12 -0
- data/examples/quick_test.rb +80 -0
- data/lib/tabscanner/client.rb +50 -0
- data/lib/tabscanner/config.rb +101 -0
- data/lib/tabscanner/credits.rb +63 -0
- data/lib/tabscanner/errors/base_error.rb +55 -0
- data/lib/tabscanner/errors/configuration_error.rb +6 -0
- data/lib/tabscanner/errors/server_error.rb +6 -0
- data/lib/tabscanner/errors/unauthorized_error.rb +6 -0
- data/lib/tabscanner/errors/validation_error.rb +6 -0
- data/lib/tabscanner/http_client.rb +108 -0
- data/lib/tabscanner/request.rb +227 -0
- data/lib/tabscanner/result.rb +192 -0
- data/lib/tabscanner/version.rb +5 -0
- data/lib/tabscanner.rb +43 -0
- data/sig/tabscanner.rbs +4 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 94b707ecbc035054b09ece9560c43ab50cc807742dc0c3b822934d1b8a5e8c00
|
4
|
+
data.tar.gz: 88adeaa46a2717c71cd4843466b6addb01da8c766c09a30013450c9f45c0fe34
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e03197b23de85a72d1d2417e66875fa047e92f106e8d343c3326158ca240bc8aebab15c389dd71257607f7b9a7c9656b55c63f8e53e27d41b69aa0e8fea88996
|
7
|
+
data.tar.gz: 186b1b74b5f5881422b575e12f78bc62c4cc1edd41e56fe4ea82691469fccf41cef103f19bbeb15e899cc58e03e3723d905ca4d75e2894a4a63ec13528b84be6
|
data/.rspec
ADDED
data/README.md
ADDED
@@ -0,0 +1,582 @@
|
|
1
|
+
# Tabscanner
|
2
|
+
|
3
|
+
A Ruby gem for processing receipt images using the Tabscanner API. Extract structured data from receipt images with just a few lines of code.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- 📸 **Image Processing**: Submit receipt images via file path or IO stream
|
8
|
+
- 🔄 **Automatic Polling**: Built-in polling for processing results with timeout handling
|
9
|
+
- 💳 **Credit Monitoring**: Check remaining API credits to stay within plan limits
|
10
|
+
- 🛡️ **Error Handling**: Comprehensive error handling with detailed debug information
|
11
|
+
- 🔧 **Configurable**: Environment variables and programmatic configuration
|
12
|
+
- 🐛 **Debug Support**: Optional debug logging for troubleshooting
|
13
|
+
- ⚡ **Simple API**: Complete workflow in under 10 lines of code
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
### Using Bundler
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'tabscanner'
|
23
|
+
```
|
24
|
+
|
25
|
+
Then execute:
|
26
|
+
|
27
|
+
```bash
|
28
|
+
bundle install
|
29
|
+
```
|
30
|
+
|
31
|
+
### Manual Installation
|
32
|
+
|
33
|
+
Install the gem directly:
|
34
|
+
|
35
|
+
```bash
|
36
|
+
gem install tabscanner
|
37
|
+
```
|
38
|
+
|
39
|
+
## Quick Start
|
40
|
+
|
41
|
+
Here's a complete example that processes a receipt in under 10 lines:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
require 'tabscanner'
|
45
|
+
|
46
|
+
# Configure
|
47
|
+
Tabscanner.configure do |config|
|
48
|
+
config.api_key = 'your_api_key_here'
|
49
|
+
end
|
50
|
+
|
51
|
+
# Process receipt
|
52
|
+
token = Tabscanner.submit_receipt('receipt.jpg')
|
53
|
+
result = Tabscanner.get_result(token)
|
54
|
+
|
55
|
+
puts "Merchant: #{result['merchant']}"
|
56
|
+
puts "Total: $#{result['total']}"
|
57
|
+
```
|
58
|
+
|
59
|
+
## Configuration
|
60
|
+
|
61
|
+
### Environment Variables
|
62
|
+
|
63
|
+
The simplest way to configure the gem is using environment variables:
|
64
|
+
|
65
|
+
```bash
|
66
|
+
export TABSCANNER_API_KEY=your_api_key_here
|
67
|
+
export TABSCANNER_REGION=us # Optional, defaults to 'us'
|
68
|
+
export TABSCANNER_BASE_URL=https://custom.api.url # Optional, for staging/testing
|
69
|
+
export TABSCANNER_DEBUG=true # Optional, enables debug logging
|
70
|
+
```
|
71
|
+
|
72
|
+
### Programmatic Configuration
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
Tabscanner.configure do |config|
|
76
|
+
config.api_key = 'your_api_key_here'
|
77
|
+
config.region = 'us' # Optional
|
78
|
+
config.base_url = 'https://api.tabscanner.com' # Optional
|
79
|
+
config.debug = false # Optional, enables debug logging
|
80
|
+
config.logger = Logger.new(STDOUT) # Optional, custom logger
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
### Configuration Examples
|
85
|
+
|
86
|
+
#### Production Configuration
|
87
|
+
```ruby
|
88
|
+
Tabscanner.configure do |config|
|
89
|
+
config.api_key = ENV['TABSCANNER_API_KEY']
|
90
|
+
config.region = 'us'
|
91
|
+
config.debug = false
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
#### Development Configuration
|
96
|
+
```ruby
|
97
|
+
Tabscanner.configure do |config|
|
98
|
+
config.api_key = ENV['TABSCANNER_API_KEY']
|
99
|
+
config.region = 'us'
|
100
|
+
config.debug = true
|
101
|
+
config.logger = Logger.new(STDOUT)
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
#### Staging Configuration
|
106
|
+
```ruby
|
107
|
+
Tabscanner.configure do |config|
|
108
|
+
config.api_key = ENV['TABSCANNER_STAGING_API_KEY']
|
109
|
+
config.region = 'us'
|
110
|
+
config.base_url = 'https://staging.tabscanner.com'
|
111
|
+
config.debug = true
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
## Usage
|
116
|
+
|
117
|
+
### Basic Usage
|
118
|
+
|
119
|
+
#### Submit a Receipt Image
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
# From file path
|
123
|
+
token = Tabscanner.submit_receipt('/path/to/receipt.jpg')
|
124
|
+
|
125
|
+
# From IO stream
|
126
|
+
File.open('/path/to/receipt.jpg', 'rb') do |file|
|
127
|
+
token = Tabscanner.submit_receipt(file)
|
128
|
+
end
|
129
|
+
|
130
|
+
# From uploaded file (Rails example)
|
131
|
+
token = Tabscanner.submit_receipt(params[:receipt_file])
|
132
|
+
```
|
133
|
+
|
134
|
+
#### Get Processing Results
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
# With default timeout (15 seconds)
|
138
|
+
result = Tabscanner.get_result(token)
|
139
|
+
|
140
|
+
# With custom timeout
|
141
|
+
result = Tabscanner.get_result(token, timeout: 30)
|
142
|
+
```
|
143
|
+
|
144
|
+
### Complete Workflow Example
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
require 'tabscanner'
|
148
|
+
|
149
|
+
# Configure once
|
150
|
+
Tabscanner.configure do |config|
|
151
|
+
config.api_key = ENV['TABSCANNER_API_KEY']
|
152
|
+
config.debug = Rails.env.development?
|
153
|
+
end
|
154
|
+
|
155
|
+
# Process multiple receipts
|
156
|
+
receipt_files = Dir['receipts/*.jpg']
|
157
|
+
|
158
|
+
receipt_files.each do |file_path|
|
159
|
+
begin
|
160
|
+
# Submit for processing
|
161
|
+
puts "Processing #{file_path}..."
|
162
|
+
token = Tabscanner.submit_receipt(file_path)
|
163
|
+
|
164
|
+
# Get results
|
165
|
+
result = Tabscanner.get_result(token, timeout: 30)
|
166
|
+
|
167
|
+
# Use the data
|
168
|
+
puts "✅ #{File.basename(file_path)}"
|
169
|
+
puts " Merchant: #{result['merchant']}"
|
170
|
+
puts " Total: $#{result['total']}"
|
171
|
+
puts " Date: #{result['date']}"
|
172
|
+
puts " Items: #{result['items']&.count || 0}"
|
173
|
+
puts
|
174
|
+
|
175
|
+
rescue => e
|
176
|
+
puts "❌ Error processing #{file_path}: #{e.message}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
### Credit Monitoring
|
182
|
+
|
183
|
+
Check your remaining API credits to stay within your plan limits:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
# Check remaining credits
|
187
|
+
credits = Tabscanner.get_credits
|
188
|
+
puts "Remaining credits: #{credits}"
|
189
|
+
|
190
|
+
# Basic usage monitoring
|
191
|
+
if credits < 10
|
192
|
+
puts "Warning: Low credit balance!"
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
#### Credits with Error Handling
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
begin
|
200
|
+
credits = Tabscanner.get_credits
|
201
|
+
|
202
|
+
case credits
|
203
|
+
when 0
|
204
|
+
puts "⚠️ No credits remaining - please upgrade your plan"
|
205
|
+
when 1..9
|
206
|
+
puts "⚠️ Low credits (#{credits}) - consider upgrading soon"
|
207
|
+
when 10..49
|
208
|
+
puts "ℹ️ Credits getting low (#{credits})"
|
209
|
+
else
|
210
|
+
puts "✅ #{credits} credits available"
|
211
|
+
end
|
212
|
+
|
213
|
+
rescue Tabscanner::UnauthorizedError => e
|
214
|
+
puts "❌ Invalid API key: #{e.message}"
|
215
|
+
rescue Tabscanner::ServerError => e
|
216
|
+
puts "❌ Service error: #{e.message}"
|
217
|
+
rescue Tabscanner::Error => e
|
218
|
+
puts "❌ Error checking credits: #{e.message}"
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
222
|
+
### Ruby on Rails Integration
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
class ReceiptsController < ApplicationController
|
226
|
+
def create
|
227
|
+
receipt_file = params[:receipt_file]
|
228
|
+
|
229
|
+
begin
|
230
|
+
# Submit receipt for processing
|
231
|
+
token = Tabscanner.submit_receipt(receipt_file)
|
232
|
+
|
233
|
+
# Store token for later retrieval
|
234
|
+
receipt = Receipt.create!(
|
235
|
+
user: current_user,
|
236
|
+
token: token,
|
237
|
+
status: 'processing',
|
238
|
+
filename: receipt_file.original_filename
|
239
|
+
)
|
240
|
+
|
241
|
+
# Start background job to poll for results
|
242
|
+
ProcessReceiptJob.perform_later(receipt.id)
|
243
|
+
|
244
|
+
render json: { status: 'submitted', receipt_id: receipt.id }
|
245
|
+
|
246
|
+
rescue Tabscanner::ValidationError => e
|
247
|
+
render json: { error: "Invalid receipt: #{e.message}" }, status: 422
|
248
|
+
rescue Tabscanner::UnauthorizedError => e
|
249
|
+
render json: { error: "API authentication failed" }, status: 401
|
250
|
+
rescue => e
|
251
|
+
render json: { error: "Processing failed: #{e.message}" }, status: 500
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Background job
|
257
|
+
class ProcessReceiptJob < ApplicationJob
|
258
|
+
def perform(receipt_id)
|
259
|
+
receipt = Receipt.find(receipt_id)
|
260
|
+
|
261
|
+
begin
|
262
|
+
result = Tabscanner.get_result(receipt.token, timeout: 60)
|
263
|
+
|
264
|
+
receipt.update!(
|
265
|
+
status: 'completed',
|
266
|
+
merchant: result['merchant'],
|
267
|
+
total: result['total'],
|
268
|
+
date: result['date'],
|
269
|
+
raw_data: result
|
270
|
+
)
|
271
|
+
|
272
|
+
rescue Tabscanner::Error => e
|
273
|
+
receipt.update!(status: 'failed', error_message: e.message)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
```
|
278
|
+
|
279
|
+
## Error Handling
|
280
|
+
|
281
|
+
The gem provides specific error classes for different types of failures:
|
282
|
+
|
283
|
+
### Error Types
|
284
|
+
|
285
|
+
- `Tabscanner::ConfigurationError` - Invalid or missing configuration
|
286
|
+
- `Tabscanner::UnauthorizedError` - Authentication failures (401)
|
287
|
+
- `Tabscanner::ValidationError` - Request validation failures (422)
|
288
|
+
- `Tabscanner::ServerError` - Server errors (500+)
|
289
|
+
- `Tabscanner::Error` - Base error class for other failures
|
290
|
+
|
291
|
+
### Error Handling Examples
|
292
|
+
|
293
|
+
#### Basic Error Handling
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
begin
|
297
|
+
token = Tabscanner.submit_receipt('receipt.jpg')
|
298
|
+
result = Tabscanner.get_result(token)
|
299
|
+
rescue Tabscanner::ConfigurationError => e
|
300
|
+
puts "Configuration problem: #{e.message}"
|
301
|
+
rescue Tabscanner::UnauthorizedError => e
|
302
|
+
puts "Authentication failed: #{e.message}"
|
303
|
+
rescue Tabscanner::ValidationError => e
|
304
|
+
puts "Invalid request: #{e.message}"
|
305
|
+
rescue Tabscanner::ServerError => e
|
306
|
+
puts "Server error: #{e.message}"
|
307
|
+
rescue Tabscanner::Error => e
|
308
|
+
puts "General error: #{e.message}"
|
309
|
+
end
|
310
|
+
```
|
311
|
+
|
312
|
+
#### Specific Error Scenarios
|
313
|
+
|
314
|
+
```ruby
|
315
|
+
# Handle configuration errors
|
316
|
+
begin
|
317
|
+
Tabscanner.submit_receipt('receipt.jpg')
|
318
|
+
rescue Tabscanner::ConfigurationError => e
|
319
|
+
# API key not set or invalid
|
320
|
+
puts "Please set TABSCANNER_API_KEY environment variable"
|
321
|
+
return
|
322
|
+
end
|
323
|
+
|
324
|
+
# Handle authentication errors
|
325
|
+
begin
|
326
|
+
token = Tabscanner.submit_receipt('receipt.jpg')
|
327
|
+
rescue Tabscanner::UnauthorizedError => e
|
328
|
+
# Invalid API key or expired
|
329
|
+
puts "Please check your API key: #{e.message}"
|
330
|
+
return
|
331
|
+
end
|
332
|
+
|
333
|
+
# Handle validation errors
|
334
|
+
begin
|
335
|
+
token = Tabscanner.submit_receipt('invalid-file.txt')
|
336
|
+
rescue Tabscanner::ValidationError => e
|
337
|
+
# Invalid file format or corrupted image
|
338
|
+
puts "Invalid image file: #{e.message}"
|
339
|
+
return
|
340
|
+
end
|
341
|
+
|
342
|
+
# Handle timeout errors
|
343
|
+
begin
|
344
|
+
result = Tabscanner.get_result(token, timeout: 5)
|
345
|
+
rescue Tabscanner::Error => e
|
346
|
+
if e.message.include?('Timeout')
|
347
|
+
puts "Processing is taking longer than expected, try again later"
|
348
|
+
else
|
349
|
+
puts "Unexpected error: #{e.message}"
|
350
|
+
end
|
351
|
+
end
|
352
|
+
```
|
353
|
+
|
354
|
+
#### Advanced Error Handling with Debug Information
|
355
|
+
|
356
|
+
When debug mode is enabled, errors include additional debugging information:
|
357
|
+
|
358
|
+
```ruby
|
359
|
+
Tabscanner.configure do |config|
|
360
|
+
config.api_key = 'your_key'
|
361
|
+
config.debug = true # Enable debug mode
|
362
|
+
end
|
363
|
+
|
364
|
+
begin
|
365
|
+
result = Tabscanner.get_result('invalid_token')
|
366
|
+
rescue Tabscanner::ValidationError => e
|
367
|
+
puts "Error: #{e.message}"
|
368
|
+
|
369
|
+
# Access raw response data for debugging
|
370
|
+
if e.raw_response
|
371
|
+
puts "HTTP Status: #{e.raw_response[:status]}"
|
372
|
+
puts "Response Body: #{e.raw_response[:body]}"
|
373
|
+
puts "Response Headers: #{e.raw_response[:headers]}"
|
374
|
+
end
|
375
|
+
end
|
376
|
+
```
|
377
|
+
|
378
|
+
#### Retry Logic Example
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
def process_receipt_with_retry(file_path, max_retries: 3)
|
382
|
+
retries = 0
|
383
|
+
|
384
|
+
begin
|
385
|
+
token = Tabscanner.submit_receipt(file_path)
|
386
|
+
result = Tabscanner.get_result(token)
|
387
|
+
return result
|
388
|
+
|
389
|
+
rescue Tabscanner::ServerError => e
|
390
|
+
retries += 1
|
391
|
+
if retries <= max_retries
|
392
|
+
puts "Server error, retrying (#{retries}/#{max_retries})..."
|
393
|
+
sleep(2 ** retries) # Exponential backoff
|
394
|
+
retry
|
395
|
+
else
|
396
|
+
raise e
|
397
|
+
end
|
398
|
+
|
399
|
+
rescue Tabscanner::UnauthorizedError, Tabscanner::ValidationError => e
|
400
|
+
# Don't retry these errors
|
401
|
+
raise e
|
402
|
+
end
|
403
|
+
end
|
404
|
+
```
|
405
|
+
|
406
|
+
## Debug Mode
|
407
|
+
|
408
|
+
Enable debug mode to get detailed logging of HTTP requests and responses:
|
409
|
+
|
410
|
+
### Environment Variable
|
411
|
+
|
412
|
+
```bash
|
413
|
+
export TABSCANNER_DEBUG=true
|
414
|
+
```
|
415
|
+
|
416
|
+
### Programmatic Configuration
|
417
|
+
|
418
|
+
```ruby
|
419
|
+
Tabscanner.configure do |config|
|
420
|
+
config.api_key = 'your_key'
|
421
|
+
config.debug = true
|
422
|
+
config.logger = Logger.new(STDOUT) # Optional: custom logger
|
423
|
+
end
|
424
|
+
```
|
425
|
+
|
426
|
+
### Debug Output Example
|
427
|
+
|
428
|
+
When debug mode is enabled, you'll see detailed logs:
|
429
|
+
|
430
|
+
```
|
431
|
+
[2023-07-28 10:30:15] DEBUG -- Tabscanner: Starting result polling for token: abc123 (timeout: 15s)
|
432
|
+
[2023-07-28 10:30:15] DEBUG -- Tabscanner: HTTP Request: GET result/abc123
|
433
|
+
[2023-07-28 10:30:15] DEBUG -- Tabscanner: Request Headers: Authorization=Bearer [REDACTED], User-Agent=Tabscanner Ruby Gem 1.0.0
|
434
|
+
[2023-07-28 10:30:16] DEBUG -- Tabscanner: HTTP Response: 200
|
435
|
+
[2023-07-28 10:30:16] DEBUG -- Tabscanner: Response Headers: {"content-type"=>["application/json"]}
|
436
|
+
[2023-07-28 10:30:16] DEBUG -- Tabscanner: Response Body: {"status":"processing"}
|
437
|
+
[2023-07-28 10:30:16] DEBUG -- Tabscanner: Result still processing for token: abc123, waiting 1s...
|
438
|
+
```
|
439
|
+
|
440
|
+
## API Reference
|
441
|
+
|
442
|
+
### Configuration Methods
|
443
|
+
|
444
|
+
```ruby
|
445
|
+
# Configure the gem
|
446
|
+
Tabscanner.configure do |config|
|
447
|
+
config.api_key = 'string' # Required: Your API key
|
448
|
+
config.region = 'string' # Optional: API region (default: 'us')
|
449
|
+
config.base_url = 'string' # Optional: Custom API base URL
|
450
|
+
config.debug = boolean # Optional: Enable debug logging (default: false)
|
451
|
+
config.logger = Logger # Optional: Custom logger instance
|
452
|
+
end
|
453
|
+
|
454
|
+
# Access current configuration
|
455
|
+
Tabscanner.config
|
456
|
+
|
457
|
+
# Validate configuration
|
458
|
+
Tabscanner.config.validate!
|
459
|
+
```
|
460
|
+
|
461
|
+
### Core Methods
|
462
|
+
|
463
|
+
```ruby
|
464
|
+
# Submit a receipt for processing
|
465
|
+
# @param file_path_or_io [String, IO] File path or IO stream
|
466
|
+
# @return [String] Token for result retrieval
|
467
|
+
token = Tabscanner.submit_receipt(file_path_or_io)
|
468
|
+
|
469
|
+
# Get processing results
|
470
|
+
# @param token [String] Token from submit_receipt
|
471
|
+
# @param timeout [Integer] Maximum wait time in seconds (default: 15)
|
472
|
+
# @return [Hash] Parsed receipt data
|
473
|
+
result = Tabscanner.get_result(token, timeout: 30)
|
474
|
+
```
|
475
|
+
|
476
|
+
### Response Format
|
477
|
+
|
478
|
+
The `get_result` method returns a hash with the parsed receipt data:
|
479
|
+
|
480
|
+
```ruby
|
481
|
+
{
|
482
|
+
"merchant" => "Coffee Shop",
|
483
|
+
"total" => 15.99,
|
484
|
+
"subtotal" => 14.50,
|
485
|
+
"tax" => 1.49,
|
486
|
+
"date" => "2023-07-28",
|
487
|
+
"time" => "14:30:00",
|
488
|
+
"items" => [
|
489
|
+
{
|
490
|
+
"name" => "Latte",
|
491
|
+
"price" => 4.50,
|
492
|
+
"quantity" => 1
|
493
|
+
},
|
494
|
+
{
|
495
|
+
"name" => "Sandwich",
|
496
|
+
"price" => 10.00,
|
497
|
+
"quantity" => 1
|
498
|
+
}
|
499
|
+
],
|
500
|
+
"payment_method" => "Credit Card",
|
501
|
+
"currency" => "USD"
|
502
|
+
}
|
503
|
+
```
|
504
|
+
|
505
|
+
## Examples
|
506
|
+
|
507
|
+
### Simple Script Example (Under 10 Lines)
|
508
|
+
|
509
|
+
Create a file `process_receipt.rb`:
|
510
|
+
|
511
|
+
```ruby
|
512
|
+
#!/usr/bin/env ruby
|
513
|
+
require 'tabscanner'
|
514
|
+
|
515
|
+
Tabscanner.configure { |c| c.api_key = ENV['TABSCANNER_API_KEY'] }
|
516
|
+
|
517
|
+
token = Tabscanner.submit_receipt(ARGV[0])
|
518
|
+
result = Tabscanner.get_result(token)
|
519
|
+
|
520
|
+
puts "Merchant: #{result['merchant']}"
|
521
|
+
puts "Total: $#{result['total']}"
|
522
|
+
puts "Items: #{result['items']&.count || 0}"
|
523
|
+
```
|
524
|
+
|
525
|
+
Usage:
|
526
|
+
```bash
|
527
|
+
ruby process_receipt.rb receipt.jpg
|
528
|
+
```
|
529
|
+
|
530
|
+
### Batch Processing Example
|
531
|
+
|
532
|
+
```ruby
|
533
|
+
require 'tabscanner'
|
534
|
+
|
535
|
+
Tabscanner.configure do |config|
|
536
|
+
config.api_key = ENV['TABSCANNER_API_KEY']
|
537
|
+
config.debug = true
|
538
|
+
end
|
539
|
+
|
540
|
+
receipts_dir = 'receipts'
|
541
|
+
results = []
|
542
|
+
|
543
|
+
Dir[File.join(receipts_dir, '*.{jpg,jpeg,png}')].each do |file_path|
|
544
|
+
puts "Processing #{File.basename(file_path)}..."
|
545
|
+
|
546
|
+
begin
|
547
|
+
token = Tabscanner.submit_receipt(file_path)
|
548
|
+
result = Tabscanner.get_result(token, timeout: 30)
|
549
|
+
|
550
|
+
results << {
|
551
|
+
file: File.basename(file_path),
|
552
|
+
merchant: result['merchant'],
|
553
|
+
total: result['total'],
|
554
|
+
date: result['date']
|
555
|
+
}
|
556
|
+
|
557
|
+
puts "✅ Success: #{result['merchant']} - $#{result['total']}"
|
558
|
+
|
559
|
+
rescue => e
|
560
|
+
puts "❌ Error: #{e.message}"
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
# Save results to CSV
|
565
|
+
require 'csv'
|
566
|
+
CSV.open('receipt_results.csv', 'w') do |csv|
|
567
|
+
csv << ['File', 'Merchant', 'Total', 'Date']
|
568
|
+
results.each { |r| csv << [r[:file], r[:merchant], r[:total], r[:date]] }
|
569
|
+
end
|
570
|
+
|
571
|
+
puts "\nProcessed #{results.count} receipts successfully"
|
572
|
+
```
|
573
|
+
|
574
|
+
## Development
|
575
|
+
|
576
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
577
|
+
|
578
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
579
|
+
|
580
|
+
## Contributing
|
581
|
+
|
582
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/fkchang/tabscanner.
|