wittyflow 0.1.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +160 -0
- data/Guardfile +30 -0
- data/README.md +463 -11
- data/examples/rails_initializer.rb +56 -0
- data/lib/wittyflow/client.rb +193 -0
- data/lib/wittyflow/configuration.rb +20 -0
- data/lib/wittyflow/sms.rb +32 -0
- data/lib/wittyflow/version.rb +1 -1
- data/lib/wittyflow.rb +27 -67
- data/wittyflow-0.1.0.gem +0 -0
- data/wittyflow.gemspec +34 -11
- metadata +221 -16
- data/.gitignore +0 -8
- data/.rubocop.yml +0 -13
- data/Gemfile +0 -10
- data/Rakefile +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdc8fbce7e5844bca9a9050807303d746084cc12596b8cd755373633b572111b
|
4
|
+
data.tar.gz: 3483c288145246fb8597f7ee91d0c4fc2b9a83ce4897becd32ff3faa4acb5e0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfcd365cf908f23631e3be46fb32f19b7ccad546f2eb5807f4f6eabb2492ef654bba3b26992efb1315ce8c12cf75b4a26eba9f819d7acc357fdc4ea90c29009e
|
7
|
+
data.tar.gz: cc518905f36f4ad53b98b58f08c161bd1ad7b264576c1443d3e18c7899e26432079c6cc3415b2d60f3efbaa0bc9645990abab8a761f9ee1f2402e412719c2fe4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [1.0.0] - 2024-01-XX
|
9
|
+
|
10
|
+
### 🚀 Major Release - Complete Rewrite
|
11
|
+
|
12
|
+
This is a major release with breaking changes. The gem has been completely rewritten with modern Ruby patterns and production-ready features.
|
13
|
+
|
14
|
+
### Added
|
15
|
+
|
16
|
+
- **Modern Ruby Support**: Now requires Ruby 3.0+ for better performance and security
|
17
|
+
- **Zeitwerk Autoloading**: Automatic code loading for better performance
|
18
|
+
- **Comprehensive Error Handling**: Specific error classes for different failure scenarios
|
19
|
+
- `Wittyflow::ConfigurationError` - Invalid configuration
|
20
|
+
- `Wittyflow::ValidationError` - Invalid parameters
|
21
|
+
- `Wittyflow::AuthenticationError` - Invalid credentials
|
22
|
+
- `Wittyflow::NetworkError` - Network timeouts and connectivity issues
|
23
|
+
- `Wittyflow::APIError` - API-specific errors
|
24
|
+
- **Retry Logic**: Automatic retry mechanism for network failures with configurable attempts and delays
|
25
|
+
- **Global Configuration**: Configure the gem globally for easier Rails integration
|
26
|
+
- **Bulk SMS Support**: Send SMS to multiple recipients with `send_bulk_sms` method
|
27
|
+
- **Enhanced Phone Number Formatting**: Intelligent handling of various phone number formats
|
28
|
+
- **Comprehensive Test Suite**: 90%+ test coverage with RSpec, WebMock, and VCR
|
29
|
+
- **Rails Integration Examples**: Detailed examples for Rails applications including:
|
30
|
+
- Service classes
|
31
|
+
- Background jobs with Sidekiq
|
32
|
+
- Model integration
|
33
|
+
- Controller usage
|
34
|
+
- Database logging
|
35
|
+
- **Production Features**:
|
36
|
+
- Request timeouts and retries
|
37
|
+
- Proper logging support
|
38
|
+
- User-Agent headers
|
39
|
+
- HTTP connection pooling via HTTParty
|
40
|
+
- **Development Tools**:
|
41
|
+
- RuboCop with modern rules
|
42
|
+
- SimpleCov for coverage reporting
|
43
|
+
- YARD for documentation
|
44
|
+
- Guard for automated testing
|
45
|
+
|
46
|
+
### Changed
|
47
|
+
|
48
|
+
- **BREAKING**: Minimum Ruby version is now 3.0.0
|
49
|
+
- **BREAKING**: Main API has changed to use keyword arguments:
|
50
|
+
```ruby
|
51
|
+
# Old (still works but deprecated)
|
52
|
+
client.send_sms(sender, phone, message)
|
53
|
+
|
54
|
+
# New
|
55
|
+
client.send_sms(from: sender, to: phone, message: message)
|
56
|
+
```
|
57
|
+
- **BREAKING**: Error handling now uses specific exception classes instead of generic errors
|
58
|
+
- Updated HTTParty to latest version (0.21.x)
|
59
|
+
- Improved phone number validation and formatting
|
60
|
+
- Better response parsing and error messages
|
61
|
+
|
62
|
+
### Deprecated
|
63
|
+
|
64
|
+
- `Wittyflow::Sms` class is now deprecated in favor of `Wittyflow::Client`
|
65
|
+
- Old positional argument methods (still work but show deprecation warnings)
|
66
|
+
|
67
|
+
### Removed
|
68
|
+
|
69
|
+
- Ruby 2.x support (breaking change)
|
70
|
+
- Legacy error handling patterns
|
71
|
+
|
72
|
+
### Fixed
|
73
|
+
|
74
|
+
- Phone number formatting edge cases
|
75
|
+
- Memory leaks in HTTP connections
|
76
|
+
- Race conditions in concurrent usage
|
77
|
+
- Proper handling of network timeouts
|
78
|
+
|
79
|
+
### Security
|
80
|
+
|
81
|
+
- Updated all dependencies to latest secure versions
|
82
|
+
- Added proper input validation and sanitization
|
83
|
+
- Removed potential security vulnerabilities in error messages
|
84
|
+
|
85
|
+
## [0.1.0] - 2023-XX-XX
|
86
|
+
|
87
|
+
### Added
|
88
|
+
- Initial release
|
89
|
+
- Basic SMS sending functionality
|
90
|
+
- Flash SMS support
|
91
|
+
- Account balance checking
|
92
|
+
- Message status tracking
|
93
|
+
- Basic error handling
|
94
|
+
|
95
|
+
### Requirements
|
96
|
+
- Ruby 2.0+
|
97
|
+
- HTTParty 0.17.x
|
98
|
+
|
99
|
+
---
|
100
|
+
|
101
|
+
## Migration Guide
|
102
|
+
|
103
|
+
### From v0.1.x to v1.0.0
|
104
|
+
|
105
|
+
1. **Update Ruby Version**: Ensure you're using Ruby 3.0 or later
|
106
|
+
2. **Update Dependencies**: Run `bundle update`
|
107
|
+
3. **Update API Calls**: Change to keyword arguments:
|
108
|
+
```ruby
|
109
|
+
# Before
|
110
|
+
sms = Wittyflow::Sms.new(app_id, app_secret)
|
111
|
+
sms.send_sms("Sender", "0241234567", "Message")
|
112
|
+
|
113
|
+
# After
|
114
|
+
client = Wittyflow::Client.new(app_id: app_id, app_secret: app_secret)
|
115
|
+
client.send_sms(from: "Sender", to: "0241234567", message: "Message")
|
116
|
+
```
|
117
|
+
4. **Update Error Handling**: Use specific exception classes:
|
118
|
+
```ruby
|
119
|
+
# Before
|
120
|
+
begin
|
121
|
+
sms.send_sms(...)
|
122
|
+
rescue => e
|
123
|
+
puts "Error: #{e.message}"
|
124
|
+
end
|
125
|
+
|
126
|
+
# After
|
127
|
+
begin
|
128
|
+
client.send_sms(...)
|
129
|
+
rescue Wittyflow::AuthenticationError => e
|
130
|
+
# Handle auth errors
|
131
|
+
rescue Wittyflow::NetworkError => e
|
132
|
+
# Handle network errors
|
133
|
+
rescue Wittyflow::ValidationError => e
|
134
|
+
# Handle validation errors
|
135
|
+
end
|
136
|
+
```
|
137
|
+
5. **Add Configuration**: Set up global configuration in Rails initializer:
|
138
|
+
```ruby
|
139
|
+
# config/initializers/wittyflow.rb
|
140
|
+
Wittyflow.configure do |config|
|
141
|
+
config.app_id = ENV['WITTYFLOW_APP_ID']
|
142
|
+
config.app_secret = ENV['WITTYFLOW_APP_SECRET']
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
### Breaking Changes Summary
|
147
|
+
|
148
|
+
- **Ruby 3.0+ Required**: The gem no longer supports Ruby 2.x
|
149
|
+
- **Keyword Arguments**: All methods now use keyword arguments instead of positional arguments
|
150
|
+
- **New Error Classes**: Generic errors have been replaced with specific error classes
|
151
|
+
- **Configuration Changes**: New configuration system with global and instance-level options
|
152
|
+
- **HTTParty Update**: Updated to latest version which may have its own breaking changes
|
153
|
+
|
154
|
+
### New Features You Can Use
|
155
|
+
|
156
|
+
- **Bulk SMS**: Send to multiple recipients at once
|
157
|
+
- **Retry Logic**: Automatic retries for failed requests
|
158
|
+
- **Better Formatting**: Improved phone number handling
|
159
|
+
- **Rails Integration**: Comprehensive examples for Rails apps
|
160
|
+
- **Testing Helpers**: Built-in test helpers for easier testing
|
data/Guardfile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A sample Guardfile
|
4
|
+
# More info at https://github.com/guard/guard#readme
|
5
|
+
|
6
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
7
|
+
require "guard/rspec/dsl"
|
8
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
9
|
+
|
10
|
+
# Feel free to open issues for suggestions and improvements
|
11
|
+
|
12
|
+
# RSpec files
|
13
|
+
rspec = dsl.rspec
|
14
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
15
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
16
|
+
watch(rspec.spec_files)
|
17
|
+
|
18
|
+
# Ruby files
|
19
|
+
ruby = dsl.ruby
|
20
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
21
|
+
|
22
|
+
# Wittyflow specific files
|
23
|
+
watch(%r{^lib/wittyflow/(.+)\.rb$}) { |m| "spec/wittyflow/#{m[1]}_spec.rb" }
|
24
|
+
watch(%r{^lib/wittyflow\.rb$}) { "spec/wittyflow_spec.rb" }
|
25
|
+
end
|
26
|
+
|
27
|
+
guard :rubocop, all_on_start: false, cli: ["--format", "emacs"] do
|
28
|
+
watch(/.+\.rb$/)
|
29
|
+
watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
|
30
|
+
end
|
data/README.md
CHANGED
@@ -1,8 +1,21 @@
|
|
1
|
-
# Wittyflow
|
1
|
+
# Wittyflow Ruby Gem
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/wittyflow)
|
4
|
+
[](https://github.com/charlesagyemang/wittyflow_sms_ruby_gem/actions/workflows/ruby.yml)
|
4
5
|
|
5
|
-
|
6
|
+
A production-ready Ruby gem for integrating with the [Wittyflow SMS API](https://wittyflow.com/). Send SMS messages, check delivery status, and manage your account with a clean, modern Ruby interface.
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
- 🚀 **Modern Ruby**: Requires Ruby 3.0+ with Zeitwerk autoloading
|
11
|
+
- 📱 **Full SMS API**: Send regular SMS, flash SMS, and bulk messaging
|
12
|
+
- 🔄 **Retry Logic**: Built-in retry mechanism for network failures
|
13
|
+
- 🛡️ **Error Handling**: Comprehensive error types with detailed messages
|
14
|
+
- 📊 **Account Management**: Check balance and message delivery status
|
15
|
+
- 🌍 **Phone Formatting**: Intelligent phone number formatting for Ghana
|
16
|
+
- 🧪 **Well Tested**: 90%+ test coverage with RSpec
|
17
|
+
- 📖 **Rails Ready**: Easy integration with Rails applications
|
18
|
+
- 🔧 **Configurable**: Global and per-instance configuration options
|
6
19
|
|
7
20
|
## Installation
|
8
21
|
|
@@ -14,30 +27,469 @@ gem 'wittyflow'
|
|
14
27
|
|
15
28
|
And then execute:
|
16
29
|
|
17
|
-
|
30
|
+
```bash
|
31
|
+
$ bundle install
|
32
|
+
```
|
18
33
|
|
19
34
|
Or install it yourself as:
|
20
35
|
|
21
|
-
|
36
|
+
```bash
|
37
|
+
$ gem install wittyflow
|
38
|
+
```
|
39
|
+
|
40
|
+
## Configuration
|
41
|
+
|
42
|
+
### Global Configuration (Recommended for Rails)
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
# config/initializers/wittyflow.rb
|
46
|
+
Wittyflow.configure do |config|
|
47
|
+
config.app_id = ENV['WITTYFLOW_APP_ID']
|
48
|
+
config.app_secret = ENV['WITTYFLOW_APP_SECRET']
|
49
|
+
config.timeout = 30
|
50
|
+
config.retries = 3
|
51
|
+
config.retry_delay = 1
|
52
|
+
config.default_country_code = "233" # Ghana
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
### Environment Variables
|
57
|
+
|
58
|
+
Create a `.env` file or set environment variables:
|
59
|
+
|
60
|
+
```bash
|
61
|
+
WITTYFLOW_APP_ID=your_app_id_here
|
62
|
+
WITTYFLOW_APP_SECRET=your_app_secret_here
|
63
|
+
```
|
22
64
|
|
23
65
|
## Usage
|
24
66
|
|
25
|
-
|
67
|
+
### Basic SMS Sending
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
# Using global configuration
|
71
|
+
client = Wittyflow::Client.new
|
72
|
+
|
73
|
+
# Send regular SMS
|
74
|
+
response = client.send_sms(
|
75
|
+
from: "YourApp",
|
76
|
+
to: "0241234567",
|
77
|
+
message: "Hello from Wittyflow!"
|
78
|
+
)
|
79
|
+
|
80
|
+
# Send flash SMS (appears directly on screen)
|
81
|
+
response = client.send_flash_sms(
|
82
|
+
from: "YourApp",
|
83
|
+
to: "0241234567",
|
84
|
+
message: "Urgent: Your verification code is 123456"
|
85
|
+
)
|
86
|
+
```
|
87
|
+
|
88
|
+
### Instance Configuration
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# Per-instance configuration
|
92
|
+
client = Wittyflow::Client.new(
|
93
|
+
app_id: "your_app_id",
|
94
|
+
app_secret: "your_app_secret",
|
95
|
+
timeout: 60,
|
96
|
+
retries: 5
|
97
|
+
)
|
98
|
+
|
99
|
+
response = client.send_sms(
|
100
|
+
from: "YourApp",
|
101
|
+
to: "+233241234567",
|
102
|
+
message: "Hello World!"
|
103
|
+
)
|
104
|
+
```
|
105
|
+
|
106
|
+
### Bulk SMS
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
# Send to multiple recipients
|
110
|
+
recipients = ["0241234567", "0241234568", "0241234569"]
|
111
|
+
|
112
|
+
results = client.send_bulk_sms(
|
113
|
+
from: "YourApp",
|
114
|
+
to: recipients,
|
115
|
+
message: "Bulk message to all recipients"
|
116
|
+
)
|
117
|
+
|
118
|
+
# Results is an array with response for each recipient
|
119
|
+
results.each_with_index do |result, index|
|
120
|
+
puts "Message to #{recipients[index]}: #{result['status']}"
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
### Message Status Tracking
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
# Check delivery status
|
128
|
+
status_response = client.message_status("message_id_from_send_response")
|
129
|
+
puts "Message status: #{status_response['data']['status']}"
|
130
|
+
```
|
131
|
+
|
132
|
+
### Account Balance
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
# Check account balance
|
136
|
+
balance_response = client.account_balance
|
137
|
+
puts "Balance: #{balance_response['data']['balance']} #{balance_response['data']['currency']}"
|
138
|
+
```
|
139
|
+
|
140
|
+
## Rails Integration
|
141
|
+
|
142
|
+
### 1. SMS Service Class
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
# app/services/sms_service.rb
|
146
|
+
class SmsService
|
147
|
+
include ActiveModel::Model
|
148
|
+
|
149
|
+
attr_accessor :to, :message, :from
|
150
|
+
|
151
|
+
validates :to, :message, presence: true
|
152
|
+
validates :from, presence: true, length: { maximum: 11 }
|
153
|
+
|
154
|
+
def initialize(attributes = {})
|
155
|
+
super
|
156
|
+
@from ||= Rails.application.credentials.sms_sender_id || "YourApp"
|
157
|
+
end
|
158
|
+
|
159
|
+
def send_sms
|
160
|
+
return false unless valid?
|
161
|
+
|
162
|
+
begin
|
163
|
+
response = wittyflow_client.send_sms(
|
164
|
+
from: from,
|
165
|
+
to: to,
|
166
|
+
message: message
|
167
|
+
)
|
168
|
+
|
169
|
+
# Log the response
|
170
|
+
Rails.logger.info "SMS sent successfully: #{response}"
|
171
|
+
|
172
|
+
# Store in database if needed
|
173
|
+
SmsLog.create!(
|
174
|
+
recipient: to,
|
175
|
+
message: message,
|
176
|
+
sender: from,
|
177
|
+
external_id: response.dig('data', 'message_id'),
|
178
|
+
status: 'sent',
|
179
|
+
response: response
|
180
|
+
)
|
181
|
+
|
182
|
+
true
|
183
|
+
rescue Wittyflow::Error => e
|
184
|
+
Rails.logger.error "SMS sending failed: #{e.message}"
|
185
|
+
errors.add(:base, "Failed to send SMS: #{e.message}")
|
186
|
+
false
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def wittyflow_client
|
193
|
+
@wittyflow_client ||= Wittyflow::Client.new
|
194
|
+
end
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
198
|
+
### 2. Controller Integration
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
# app/controllers/notifications_controller.rb
|
202
|
+
class NotificationsController < ApplicationController
|
203
|
+
before_action :authenticate_user!
|
204
|
+
|
205
|
+
def send_sms
|
206
|
+
@sms_service = SmsService.new(sms_params)
|
207
|
+
|
208
|
+
if @sms_service.send_sms
|
209
|
+
flash[:notice] = 'SMS sent successfully!'
|
210
|
+
redirect_to notifications_path
|
211
|
+
else
|
212
|
+
flash[:alert] = @sms_service.errors.full_messages.join(', ')
|
213
|
+
render :new
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
def sms_params
|
220
|
+
params.require(:sms_service).permit(:to, :message, :from)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
### 3. Background Jobs with Sidekiq
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
# app/jobs/send_sms_job.rb
|
229
|
+
class SendSmsJob < ApplicationJob
|
230
|
+
queue_as :default
|
231
|
+
|
232
|
+
retry_on Wittyflow::NetworkError, wait: :exponentially_longer, attempts: 3
|
233
|
+
retry_on Wittyflow::APIError, wait: 30.seconds, attempts: 2
|
234
|
+
|
235
|
+
def perform(recipient, message, sender = nil)
|
236
|
+
client = Wittyflow::Client.new
|
237
|
+
|
238
|
+
response = client.send_sms(
|
239
|
+
from: sender || Rails.application.credentials.sms_sender_id,
|
240
|
+
to: recipient,
|
241
|
+
message: message
|
242
|
+
)
|
243
|
+
|
244
|
+
# Update database record if needed
|
245
|
+
SmsLog.find_by(recipient: recipient, message: message)
|
246
|
+
&.update!(external_id: response.dig('data', 'message_id'))
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Usage in controllers or models
|
251
|
+
SendSmsJob.perform_later(user.phone, "Welcome to our service!", "YourApp")
|
252
|
+
```
|
253
|
+
|
254
|
+
### 4. Model Integration
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
# app/models/user.rb
|
258
|
+
class User < ApplicationRecord
|
259
|
+
after_create :send_welcome_sms
|
260
|
+
|
261
|
+
def send_sms(message, sender = nil)
|
262
|
+
return false unless phone.present?
|
263
|
+
|
264
|
+
SmsService.new(
|
265
|
+
to: phone,
|
266
|
+
message: message,
|
267
|
+
from: sender
|
268
|
+
).send_sms
|
269
|
+
end
|
270
|
+
|
271
|
+
def send_verification_code
|
272
|
+
code = generate_verification_code
|
273
|
+
update(verification_code: code)
|
274
|
+
|
275
|
+
send_sms("Your verification code is: #{code}")
|
276
|
+
end
|
277
|
+
|
278
|
+
private
|
279
|
+
|
280
|
+
def send_welcome_sms
|
281
|
+
SendSmsJob.perform_later(
|
282
|
+
phone,
|
283
|
+
"Welcome to #{Rails.application.class.module_parent_name}! Thanks for joining us.",
|
284
|
+
"YourApp"
|
285
|
+
)
|
286
|
+
end
|
287
|
+
|
288
|
+
def generate_verification_code
|
289
|
+
SecureRandom.random_number(999999).to_s.rjust(6, '0')
|
290
|
+
end
|
291
|
+
end
|
292
|
+
```
|
293
|
+
|
294
|
+
### 5. Database Logging
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
# db/migrate/xxx_create_sms_logs.rb
|
298
|
+
class CreateSmsLogs < ActiveRecord::Migration[7.0]
|
299
|
+
def change
|
300
|
+
create_table :sms_logs do |t|
|
301
|
+
t.string :recipient, null: false
|
302
|
+
t.text :message, null: false
|
303
|
+
t.string :sender
|
304
|
+
t.string :external_id
|
305
|
+
t.string :status, default: 'pending'
|
306
|
+
t.json :response
|
307
|
+
t.timestamps
|
308
|
+
end
|
309
|
+
|
310
|
+
add_index :sms_logs, :recipient
|
311
|
+
add_index :sms_logs, :external_id
|
312
|
+
add_index :sms_logs, :status
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# app/models/sms_log.rb
|
317
|
+
class SmsLog < ApplicationRecord
|
318
|
+
validates :recipient, :message, presence: true
|
319
|
+
|
320
|
+
enum status: {
|
321
|
+
pending: 'pending',
|
322
|
+
sent: 'sent',
|
323
|
+
delivered: 'delivered',
|
324
|
+
failed: 'failed'
|
325
|
+
}
|
326
|
+
|
327
|
+
scope :recent, -> { order(created_at: :desc) }
|
328
|
+
scope :for_recipient, ->(phone) { where(recipient: phone) }
|
329
|
+
end
|
330
|
+
```
|
331
|
+
|
332
|
+
## Phone Number Formats
|
333
|
+
|
334
|
+
The gem automatically handles various phone number formats for Ghana:
|
335
|
+
|
336
|
+
```ruby
|
337
|
+
# All of these formats work:
|
338
|
+
client.send_sms(from: "App", to: "0241234567", message: "Hello") # Local with 0
|
339
|
+
client.send_sms(from: "App", to: "241234567", message: "Hello") # Local without 0
|
340
|
+
client.send_sms(from: "App", to: "+233241234567", message: "Hello") # International
|
341
|
+
client.send_sms(from: "App", to: "233241234567", message: "Hello") # Country code only
|
342
|
+
|
343
|
+
# They all get formatted to: "233241234567"
|
344
|
+
```
|
345
|
+
|
346
|
+
## Error Handling
|
347
|
+
|
348
|
+
The gem provides specific error types for different scenarios:
|
349
|
+
|
350
|
+
```ruby
|
351
|
+
begin
|
352
|
+
client.send_sms(from: "App", to: "0241234567", message: "Hello")
|
353
|
+
rescue Wittyflow::AuthenticationError => e
|
354
|
+
# Invalid app_id or app_secret
|
355
|
+
puts "Authentication failed: #{e.message}"
|
356
|
+
rescue Wittyflow::ValidationError => e
|
357
|
+
# Invalid parameters (missing required fields, etc.)
|
358
|
+
puts "Validation error: #{e.message}"
|
359
|
+
rescue Wittyflow::NetworkError => e
|
360
|
+
# Network timeout or connection issues
|
361
|
+
puts "Network error: #{e.message}"
|
362
|
+
rescue Wittyflow::APIError => e
|
363
|
+
# API-specific errors (rate limiting, server errors)
|
364
|
+
puts "API error: #{e.message}"
|
365
|
+
rescue Wittyflow::Error => e
|
366
|
+
# Any other Wittyflow-related error
|
367
|
+
puts "Wittyflow error: #{e.message}"
|
368
|
+
end
|
369
|
+
```
|
370
|
+
|
371
|
+
## Testing
|
372
|
+
|
373
|
+
### With RSpec
|
374
|
+
|
375
|
+
```ruby
|
376
|
+
# spec/support/wittyflow_helpers.rb
|
377
|
+
module WittyflowHelpers
|
378
|
+
def stub_wittyflow_success(response_data = {})
|
379
|
+
allow_any_instance_of(Wittyflow::Client)
|
380
|
+
.to receive(:send_sms)
|
381
|
+
.and_return({
|
382
|
+
'status' => 'success',
|
383
|
+
'data' => { 'message_id' => 'msg_123' }.merge(response_data)
|
384
|
+
})
|
385
|
+
end
|
386
|
+
|
387
|
+
def stub_wittyflow_error(error_class = Wittyflow::APIError, message = "API Error")
|
388
|
+
allow_any_instance_of(Wittyflow::Client)
|
389
|
+
.to receive(:send_sms)
|
390
|
+
.and_raise(error_class, message)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
# In your specs
|
395
|
+
RSpec.configure do |config|
|
396
|
+
config.include WittyflowHelpers
|
397
|
+
end
|
398
|
+
|
399
|
+
# spec/services/sms_service_spec.rb
|
400
|
+
RSpec.describe SmsService do
|
401
|
+
describe '#send_sms' do
|
402
|
+
let(:service) { described_class.new(to: '0241234567', message: 'Test') }
|
403
|
+
|
404
|
+
context 'when API call succeeds' do
|
405
|
+
before { stub_wittyflow_success }
|
406
|
+
|
407
|
+
it 'returns true' do
|
408
|
+
expect(service.send_sms).to be true
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
context 'when API call fails' do
|
413
|
+
before { stub_wittyflow_error(Wittyflow::NetworkError, 'Network timeout') }
|
414
|
+
|
415
|
+
it 'returns false and adds error' do
|
416
|
+
expect(service.send_sms).to be false
|
417
|
+
expect(service.errors[:base]).to include('Failed to send SMS: Network timeout')
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
```
|
26
423
|
|
27
424
|
## Development
|
28
425
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt
|
426
|
+
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.
|
427
|
+
|
428
|
+
### Available Rake Tasks
|
429
|
+
|
430
|
+
```bash
|
431
|
+
rake spec # Run all tests
|
432
|
+
rake rubocop # Run RuboCop linter
|
433
|
+
rake quality # Run all quality checks (tests + linting)
|
434
|
+
rake coverage # Generate test coverage report
|
435
|
+
rake doc # Generate YARD documentation
|
436
|
+
```
|
437
|
+
|
438
|
+
### Contributing
|
30
439
|
|
31
|
-
|
440
|
+
1. Fork the repository
|
441
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
442
|
+
3. Write tests for your changes
|
443
|
+
4. Ensure all tests pass (`rake quality`)
|
444
|
+
5. Commit your changes (`git commit -am 'Add some feature'`)
|
445
|
+
6. Push to the branch (`git push origin my-new-feature`)
|
446
|
+
7. Create a Pull Request
|
32
447
|
|
33
|
-
##
|
448
|
+
## Changelog
|
34
449
|
|
35
|
-
|
450
|
+
### v1.0.0 (Latest)
|
451
|
+
|
452
|
+
- **Breaking Changes**: Requires Ruby 3.0+
|
453
|
+
- Complete rewrite with modern Ruby patterns
|
454
|
+
- Added comprehensive error handling with specific error types
|
455
|
+
- Added retry logic for network failures
|
456
|
+
- Added bulk SMS support
|
457
|
+
- Added extensive test suite (90%+ coverage)
|
458
|
+
- Added Rails integration examples
|
459
|
+
- Added global configuration support
|
460
|
+
- Improved phone number formatting
|
461
|
+
- Added Zeitwerk autoloading
|
462
|
+
|
463
|
+
### v0.1.0 (Legacy)
|
464
|
+
|
465
|
+
- Basic SMS sending functionality
|
466
|
+
- Minimal error handling
|
467
|
+
- Ruby 2.0+ support
|
468
|
+
|
469
|
+
## Migration from v0.x
|
470
|
+
|
471
|
+
If you're upgrading from v0.x, here are the key changes:
|
472
|
+
|
473
|
+
```ruby
|
474
|
+
# Old way (still works but deprecated)
|
475
|
+
sms = Wittyflow::Sms.new(app_id, app_secret)
|
476
|
+
sms.send_sms(sender, phone, message)
|
477
|
+
|
478
|
+
# New way
|
479
|
+
client = Wittyflow::Client.new(app_id: app_id, app_secret: app_secret)
|
480
|
+
client.send_sms(from: sender, to: phone, message: message)
|
481
|
+
```
|
36
482
|
|
37
483
|
## License
|
38
484
|
|
39
485
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
40
486
|
|
487
|
+
## Support
|
488
|
+
|
489
|
+
- 📧 Email: micnkru@gmail.com
|
490
|
+
- 🐛 [Report Issues](https://github.com/charlesagyemang/wittyflow_sms_ruby_gem/issues)
|
491
|
+
- 📖 [Documentation](https://github.com/charlesagyemang/wittyflow_sms_ruby_gem)
|
492
|
+
|
41
493
|
## Code of Conduct
|
42
494
|
|
43
|
-
Everyone interacting in the Wittyflow project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
495
|
+
Everyone interacting in the Wittyflow project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/charlesagyemang/wittyflow_sms_ruby_gem/blob/main/CODE_OF_CONDUCT.md).
|