mote_sms 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.
- data/.gitignore +18 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +59 -0
- data/Rakefile +6 -0
- data/lib/mote_sms/message.rb +102 -0
- data/lib/mote_sms/number.rb +53 -0
- data/lib/mote_sms/number_list.rb +84 -0
- data/lib/mote_sms/transports/.gitkeep +0 -0
- data/lib/mote_sms/transports/mobile_technics_transport.rb +185 -0
- data/lib/mote_sms/transports/test_transport.rb +40 -0
- data/lib/mote_sms/transports.rb +7 -0
- data/lib/mote_sms/version.rb +3 -0
- data/lib/mote_sms.rb +45 -0
- data/mote_sms.gemspec +27 -0
- data/spec/mote_sms/message_spec.rb +60 -0
- data/spec/mote_sms/number_list_spec.rb +33 -0
- data/spec/mote_sms/number_spec.rb +69 -0
- data/spec/mote_sms/transports/mobile_technics_transport_spec.rb +124 -0
- data/spec/mote_sms/transports/test_transport_spec.rb +17 -0
- data/spec/mote_sms_spec.rb +37 -0
- data/spec/spec_helper.rb +5 -0
- metadata +144 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 at-point ag, Lukas Westermann, http://at-point.ch/
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# MobileTechnics SMS API Client
|
2
|
+
|
3
|
+
Unofficial ruby adapter for MobileTechnics HTTP Bulk SMS API. Tries to mimick
|
4
|
+
mail API, so users can switch e.g. ActionMailer with this SMS provider. Requires
|
5
|
+
Ruby 1.9.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'mote_sms'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install mote_sms
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# Quick and dirty
|
25
|
+
MoteSMS.transport = MoteSMS::MobileTechnicsTransport.new 'https://endpoint.com:1234', 'username', 'password'
|
26
|
+
MoteSMS.deliver do
|
27
|
+
body 'Quick hello world'
|
28
|
+
to '+41 79 111 22 33'
|
29
|
+
from 'ARUBYGEM'
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# Using global transport
|
35
|
+
MoteSMS.transport = MoteSMS::MobileTechnicsTransport.new 'https://endpoint.com:1234', 'username', 'password'
|
36
|
+
sms = MoteSMS.Message.new do
|
37
|
+
to '+41 79 111 22 33'
|
38
|
+
from 'ARUBYGEM'
|
39
|
+
body 'Hello world, you know.'
|
40
|
+
end
|
41
|
+
sms.deliver
|
42
|
+
```
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
# Using client instance
|
46
|
+
transport = MoteSMS::MobileTechnicsTransport.new 'https://endpoint.com:1234', 'username', 'password'
|
47
|
+
sms = MoteSMS.Message.new do
|
48
|
+
# create message
|
49
|
+
end
|
50
|
+
sms.deliver(transport: transport)
|
51
|
+
```
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'mote_sms/number'
|
2
|
+
require 'mote_sms/number_list'
|
3
|
+
|
4
|
+
module MoteSMS
|
5
|
+
|
6
|
+
# Represents an SMS message, currently only provides the
|
7
|
+
# tools to build new messages, not parse incoming messages or
|
8
|
+
# similar stuff.
|
9
|
+
#
|
10
|
+
class Message
|
11
|
+
|
12
|
+
# The transport instance to use, if not defined
|
13
|
+
# falls back to use global MoteSMS.transport instance.
|
14
|
+
attr_accessor :transport
|
15
|
+
|
16
|
+
# Public: Create a new SMS message instance.
|
17
|
+
#
|
18
|
+
# Examples:
|
19
|
+
#
|
20
|
+
# sms = MoteSMS::Message.new do
|
21
|
+
# from '41791112233'
|
22
|
+
# to '41797776655'
|
23
|
+
# body 'Hi there.'
|
24
|
+
# end
|
25
|
+
# sms.from # => '41791112233'
|
26
|
+
# sms.to # => ['41797776655']
|
27
|
+
# sms.body # => 'Hi there.'
|
28
|
+
#
|
29
|
+
# Returns a new instance.
|
30
|
+
def initialize(transport = nil, &block)
|
31
|
+
@transport = transport
|
32
|
+
@to = MoteSMS::NumberList.new
|
33
|
+
instance_eval(&block) if block_given?
|
34
|
+
end
|
35
|
+
|
36
|
+
# Public: Returns current SMS message body, which should
|
37
|
+
# be something stringish.
|
38
|
+
#
|
39
|
+
# Returns value of body.
|
40
|
+
attr_writer :body
|
41
|
+
def body(val = nil)
|
42
|
+
@body = val if val
|
43
|
+
@body
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: Returns string of sender, the sender should
|
47
|
+
# either be 11 alphanumeric characters or 20 numbers.
|
48
|
+
#
|
49
|
+
# Examples:
|
50
|
+
#
|
51
|
+
# sms.from = '41791231234'
|
52
|
+
# sms.from # => '41791231234'
|
53
|
+
#
|
54
|
+
# Returns value of sender.
|
55
|
+
def from(val = nil)
|
56
|
+
self.from = val if val
|
57
|
+
@from
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Asign an instance of Number instead of the direct
|
61
|
+
# string, so only vanity numbers are allowed.
|
62
|
+
def from=(val)
|
63
|
+
@from = val ? Number.new(val, :vanity => true) : nil
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public: Set to multiple arguments or array, or whatever.
|
67
|
+
#
|
68
|
+
# Examples:
|
69
|
+
#
|
70
|
+
# sms.to = '41791231212'
|
71
|
+
# sms.to # => ['41791231212']
|
72
|
+
#
|
73
|
+
# sms.to = ['41791231212', '41791231212']
|
74
|
+
# sms.to # => ['41791231212', '41791231212']
|
75
|
+
#
|
76
|
+
# Returns nothing.
|
77
|
+
def to=(*args)
|
78
|
+
@to = MoteSMS::NumberList.new.push(*args)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Returns NumberList for this message.
|
82
|
+
#
|
83
|
+
# Returns NumberList instance.
|
84
|
+
def to(*numbers)
|
85
|
+
@to.push(*numbers) unless numbers.empty?
|
86
|
+
@to
|
87
|
+
end
|
88
|
+
|
89
|
+
# Public: Deliver message using defined transport, to select the correct
|
90
|
+
# transport method uses any of these values:
|
91
|
+
#
|
92
|
+
# 1. if options[:transport] is defined
|
93
|
+
# 2. falls back to self.transport
|
94
|
+
# 3. falls back to use MoteSMS.transport (global transport)
|
95
|
+
#
|
96
|
+
# Returns result of transport#deliver.
|
97
|
+
def deliver(options = {})
|
98
|
+
transport = options.delete(:transport) || self.transport || MoteSMS.transport
|
99
|
+
transport.deliver(self, options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'phony'
|
2
|
+
|
3
|
+
module MoteSMS
|
4
|
+
|
5
|
+
# MoteSMS::Number handles all the number parsing and formatting
|
6
|
+
# issues, also a number is immutable.
|
7
|
+
class Number
|
8
|
+
|
9
|
+
# Access the E164 normalized value of the number.
|
10
|
+
attr_reader :number
|
11
|
+
alias :to_number :number
|
12
|
+
|
13
|
+
def initialize(value, options = {})
|
14
|
+
@options = options || {}
|
15
|
+
@raw_number = value.to_s
|
16
|
+
parse_raw_number
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: Returns true if this **could** represent a vanity number.
|
20
|
+
#
|
21
|
+
# Returns Boolean, true if this is a vanity number, else false.
|
22
|
+
def vanity?
|
23
|
+
!!@options[:vanity]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Prints formatted number, i.e. the human readable
|
27
|
+
# variant.
|
28
|
+
#
|
29
|
+
# Returns formatted number.
|
30
|
+
def to_s
|
31
|
+
@formatted_number ||= vanity? ? number : Phony.formatted(number)
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
# Internal: Parse raw number with the help of Phony. Automatically
|
37
|
+
# adds the country code if missing.
|
38
|
+
#
|
39
|
+
def parse_raw_number
|
40
|
+
unless vanity?
|
41
|
+
raise ArgumentError, "Unable to parse #{@raw_number} as number" unless Phony.plausible?(@raw_number)
|
42
|
+
normalized = Phony.normalize(@raw_number)
|
43
|
+
normalized = "#{@options[:cc]}#{normalized}" unless @options[:cc] && normalized.start_with?(@options[:cc])
|
44
|
+
raise ArgumentError, "Wrong national destination code #{@raw_number}" unless Phony.plausible?(normalized, @options)
|
45
|
+
|
46
|
+
@number = Phony.normalize normalized
|
47
|
+
else
|
48
|
+
@number = @raw_number.gsub(/[^A-Z0-9]/i, '').upcase.strip
|
49
|
+
raise ArgumentError, "Invalid vanity number #{@raw_number}" if @number.length == 0 || @number.length > 11
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'mote_sms/number'
|
2
|
+
|
3
|
+
module MoteSMS
|
4
|
+
|
5
|
+
# List of Number instances, which transparantly is able to add
|
6
|
+
# new Number instances from strings, or whatever.
|
7
|
+
#
|
8
|
+
# Implements Enumerable, thus can be used like any regular array.
|
9
|
+
#
|
10
|
+
# Examples:
|
11
|
+
#
|
12
|
+
# list << '+41 79 123 12 12'
|
13
|
+
# list.push '044 123 12 12', cc: '41'
|
14
|
+
# list.push Number.new('0800 123 12 12')
|
15
|
+
# list.normalized_numbers
|
16
|
+
# # => ['41791231212', '41441231212', '08001231212']
|
17
|
+
#
|
18
|
+
class NumberList
|
19
|
+
|
20
|
+
# Load ruby enumerable support.
|
21
|
+
include ::Enumerable
|
22
|
+
|
23
|
+
# Internal numbers array.
|
24
|
+
attr_reader :numbers
|
25
|
+
|
26
|
+
# Public: Create a new number list instance.
|
27
|
+
def initialize
|
28
|
+
@numbers = ::Array.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Count of numbers in the list.
|
32
|
+
def length
|
33
|
+
numbers.length
|
34
|
+
end
|
35
|
+
alias :size :length
|
36
|
+
|
37
|
+
# Public: Conform to arrayish behavior.
|
38
|
+
def empty?
|
39
|
+
numbers.empty?
|
40
|
+
end
|
41
|
+
alias :blank? :empty?
|
42
|
+
|
43
|
+
# Public: Add number to internal list, use duck typing to detect if
|
44
|
+
# it appears to be a number instance or not. So everything which does
|
45
|
+
# not respond to `to_number` is converted into a Number instance.
|
46
|
+
#
|
47
|
+
# number - The Number or String to add.
|
48
|
+
#
|
49
|
+
# Returns nothing.
|
50
|
+
def <<(number)
|
51
|
+
self.push(number)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Public: Add multiple numbers, with optional options hash which can
|
55
|
+
# be used to set country options etc.
|
56
|
+
#
|
57
|
+
# Returns self.
|
58
|
+
def push(*numbers)
|
59
|
+
options = numbers.last.is_a?(Hash) ? numbers.pop : {}
|
60
|
+
numbers.flatten.each do |number|
|
61
|
+
number = Number.new(number, options) unless number.respond_to?(:to_number)
|
62
|
+
self.numbers << number
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Public: Yields each Number instance from this number list
|
68
|
+
# to the provided block. This interface is also required to be
|
69
|
+
# implemeneted for Enumerable support.
|
70
|
+
#
|
71
|
+
# Returns self.
|
72
|
+
def each(&block)
|
73
|
+
numbers.each(&block)
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Public: Fetch numbers using to_number.
|
78
|
+
#
|
79
|
+
# Returns Array of E164 normalized numbers.
|
80
|
+
def normalized_numbers
|
81
|
+
numbers.map(&:to_number)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
File without changes
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'phony'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module MoteSMS
|
7
|
+
|
8
|
+
# MoteSMS::MobileTechnicsTransport provides the implementation to
|
9
|
+
# send messages using nth.ch bulk SMS HTTP/S API. Each customer has
|
10
|
+
# custom endpoint (with port) and username/password.
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# transport = MoteSMS::MobileTechnicsTransport.new 'https://mygateway.nth.ch', 'username', 'password'
|
15
|
+
# transport.deliver message
|
16
|
+
# # => ['000-791234', '001-7987324']
|
17
|
+
#
|
18
|
+
class MobileTechnicsTransport
|
19
|
+
|
20
|
+
# Maximum recipients allowed by API
|
21
|
+
MAX_RECIPIENT = 100
|
22
|
+
|
23
|
+
# Custom exception subclass.
|
24
|
+
ServiceError = Class.new(::Exception)
|
25
|
+
|
26
|
+
# Accessible attributes
|
27
|
+
attr_accessor :endpoint, :username, :password, :logger
|
28
|
+
|
29
|
+
# Options are readable as hash
|
30
|
+
attr_reader :options
|
31
|
+
|
32
|
+
# Public: Global default parameters for sending messages, Procs/lambdas
|
33
|
+
# are evaluated on #deliver. Ensure to use only symbols as keys. Contains
|
34
|
+
# `allow_adaption: true` as default.
|
35
|
+
#
|
36
|
+
# Examples:
|
37
|
+
#
|
38
|
+
# MoteSMS::MobileTechnicsTransports.defaults[:messageid] = ->(msg) { "#{msg.from}-#{SecureRandom.hex}" }
|
39
|
+
#
|
40
|
+
# Returns Hash with options.
|
41
|
+
def self.defaults
|
42
|
+
@@options ||= {
|
43
|
+
allow_adaption: true
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: Logger used to log HTTP requests to mobile
|
48
|
+
# technics API endpoint.
|
49
|
+
#
|
50
|
+
# Returns Logger instance.
|
51
|
+
def self.logger
|
52
|
+
@@logger ||= ::Logger.new($stdout)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Public: Change the logger used to log all HTTP requests to
|
56
|
+
# the endpoint.
|
57
|
+
#
|
58
|
+
# logger - The Logger instance, should at least respond to #debug, #error.
|
59
|
+
#
|
60
|
+
# Returns nothing.
|
61
|
+
def self.logger=(logger)
|
62
|
+
@@logger = logger
|
63
|
+
end
|
64
|
+
|
65
|
+
# Public: Create a new instance using specified endpoint, username
|
66
|
+
# and password.
|
67
|
+
#
|
68
|
+
# endpoint - The String with the URL (with protocol et all) to nth gateway.
|
69
|
+
# username - The String with username.
|
70
|
+
# password - The String with password.
|
71
|
+
# options - The Hash with additional options.
|
72
|
+
#
|
73
|
+
# Returns a new instance.
|
74
|
+
def initialize(endpoint, username, password, options = nil)
|
75
|
+
self.endpoint = endpoint
|
76
|
+
self.username = username
|
77
|
+
self.password = password
|
78
|
+
@options = options || {}
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Delivers message using mobile technics HTTP/S API.
|
82
|
+
#
|
83
|
+
# message - The MoteSMS::Message instance to send.
|
84
|
+
# options - The Hash with service specific options.
|
85
|
+
#
|
86
|
+
# Returns Array with sender ids.
|
87
|
+
def deliver(message, options = {})
|
88
|
+
raise ArgumentError, "Too many recipients, max. is #{MAX_RECIPIENT} (current: #{message.to.length})" if message.to.length > MAX_RECIPIENT
|
89
|
+
|
90
|
+
# Prepare request
|
91
|
+
uri = URI.parse endpoint
|
92
|
+
http = http_client uri
|
93
|
+
request = http_request uri, post_params(message, options)
|
94
|
+
|
95
|
+
# Log
|
96
|
+
self.class.logger.debug "curl -X#{request.method} #{http.use_ssl? ? '-k ' : ''}'#{endpoint}' -d '#{request.body}'"
|
97
|
+
|
98
|
+
# Perform request
|
99
|
+
resp = http.request request
|
100
|
+
|
101
|
+
# Handle errors
|
102
|
+
raise ServiceError, "Endpoint did respond with #{resp.code}" unless resp.code.to_i == 200
|
103
|
+
raise ServiceError, "Endpoint was unable to deliver message to all recipients" unless resp.body.split("\n").all? { |l| l =~ /Result_code: 00/ }
|
104
|
+
|
105
|
+
# extract Nth-SmsIds
|
106
|
+
resp['X-Nth-SmsId'].split(',')
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
# Internal: Prepare request including body, headers etc.
|
112
|
+
#
|
113
|
+
# uri - The URI from the endpoint.
|
114
|
+
# params - The Array with the attributes.
|
115
|
+
#
|
116
|
+
# Returns Net::HTTP::Post instance.
|
117
|
+
def http_request(uri, params)
|
118
|
+
Net::HTTP::Post.new(uri.request_uri).tap do |request|
|
119
|
+
request.body = URI.encode_www_form params
|
120
|
+
request.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Internal: Build new Net::HTTP instance, enables SSL if requested.
|
125
|
+
# FIXME: Add ability to change verify_mode, so e.g. certificates can be
|
126
|
+
# verified!
|
127
|
+
#
|
128
|
+
# uri - The URI from the endpoint.
|
129
|
+
#
|
130
|
+
# Returns Net::HTTP client instance.
|
131
|
+
def http_client(uri)
|
132
|
+
Net::HTTP.new(uri.host, uri.port).tap do |http|
|
133
|
+
# SSL support
|
134
|
+
if uri.instance_of?(URI::HTTPS)
|
135
|
+
http.use_ssl = true
|
136
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Internal: Merge defaults from class and instance with options
|
142
|
+
# supplied to #deliver.
|
143
|
+
#
|
144
|
+
# options - The Hash to merge with #defaults and #options.
|
145
|
+
#
|
146
|
+
# Returns Hash.
|
147
|
+
def prepare_options(options)
|
148
|
+
self.class.defaults.merge(self.options).merge(options)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Internal: Convert NumberList instance to ; separated string with international
|
152
|
+
# relative formatted numbers. Formatting is done using phony.
|
153
|
+
#
|
154
|
+
# number_list - The NumberList instance.
|
155
|
+
#
|
156
|
+
# Returns String with numbers separated by ;.
|
157
|
+
def prepare_numbers(number_list)
|
158
|
+
number_list.normalized_numbers.map { |n| Phony.formatted(n, format: :international_relative, spaces: '') }.join(';')
|
159
|
+
end
|
160
|
+
|
161
|
+
# Internal: Prepare parameters for sending POST to endpoint, merges defaults,
|
162
|
+
# local and per call options, adds message related informations etc etc.
|
163
|
+
#
|
164
|
+
# message - The MoteSMS::Message to create the POST body for.
|
165
|
+
# options - The Hash with additional, per delivery options.
|
166
|
+
#
|
167
|
+
# Returns Array with params.
|
168
|
+
def post_params(message, options)
|
169
|
+
params = prepare_options options
|
170
|
+
params.merge! username: self.username,
|
171
|
+
password: self.password,
|
172
|
+
origin: message.from ? message.from.to_number : params[:origin],
|
173
|
+
text: message.body,
|
174
|
+
:'call-number' => prepare_numbers(message.to)
|
175
|
+
|
176
|
+
# Post process params (Procs & allow_adaption)
|
177
|
+
params.map do |param, value|
|
178
|
+
value = value.call(message) if value.respond_to?(:call)
|
179
|
+
value = value ? 1 : 0 if param == :allow_adaption
|
180
|
+
|
181
|
+
[param.to_s, value.to_s]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module MoteSMS
|
2
|
+
|
3
|
+
# Public: Provide access to global array of delivered
|
4
|
+
# messages, this can be used in testing to assert sent
|
5
|
+
# SMS messages, test their contents, recipients etc.
|
6
|
+
#
|
7
|
+
# Must be cleared manually (!)
|
8
|
+
@@deliveries = []
|
9
|
+
def self.deliveries
|
10
|
+
@@deliveries
|
11
|
+
end
|
12
|
+
|
13
|
+
# MoteSMS::TestTransport provides a transport implementation which
|
14
|
+
# can be used in test cases. This allows to test sending SMSes
|
15
|
+
# et all without depending on an API or accidentally sending out
|
16
|
+
# messages to real people.
|
17
|
+
#
|
18
|
+
# It works similar to testing ActionMailers, all delivered messages
|
19
|
+
# will be appended to `MoteSMS.deliveries`. This array must be
|
20
|
+
# cleared manually, so it makes sense to add a before hook to
|
21
|
+
# your favorite testing framework:
|
22
|
+
#
|
23
|
+
# before do
|
24
|
+
# MoteSMS.transport = MoteSMS::TestTransport
|
25
|
+
# MoteSMS.deliveries.clear
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
module TestTransport
|
29
|
+
|
30
|
+
# Public: Appends supplied message to global deliveries array.
|
31
|
+
#
|
32
|
+
# message - The MoteSMS::Message instance to deliver.
|
33
|
+
# options - The Hash with additional, transport specific options.
|
34
|
+
#
|
35
|
+
# Returns nothing.
|
36
|
+
def self.deliver(message, options = {})
|
37
|
+
MoteSMS.deliveries << message
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module MoteSMS
|
2
|
+
|
3
|
+
# All transports live within mote_sms/transports, though should be
|
4
|
+
# available in ruby as `MoteSMS::<Some>Transport`.
|
5
|
+
autoload :TestTransport, 'mote_sms/transports/test_transport'
|
6
|
+
autoload :MobileTechnicsTransport, 'mote_sms/transports/mobile_technics_transport'
|
7
|
+
end
|
data/lib/mote_sms.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'mote_sms/transports'
|
2
|
+
|
3
|
+
module MoteSMS
|
4
|
+
autoload :Number, 'mote_sms/number'
|
5
|
+
autoload :NumberList, 'mote_sms/number_list'
|
6
|
+
autoload :Message, 'mote_sms/message'
|
7
|
+
|
8
|
+
autoload :VERSION, 'mote_sms/version'
|
9
|
+
|
10
|
+
# No default transport.
|
11
|
+
@@transport = nil
|
12
|
+
|
13
|
+
# Public: Get globally defined transport method, if any.
|
14
|
+
# Defaults to `nil`.
|
15
|
+
#
|
16
|
+
# Returns global SMS transport method.
|
17
|
+
def self.transport
|
18
|
+
@@transport
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Set global transport method to use.
|
22
|
+
#
|
23
|
+
# transport - Any object which implements `#deliver(message, options)`.
|
24
|
+
#
|
25
|
+
# Returns nothing.
|
26
|
+
def self.transport=(transport)
|
27
|
+
@@transport = transport
|
28
|
+
end
|
29
|
+
|
30
|
+
# Public: Directly deliver a message using global transport.
|
31
|
+
#
|
32
|
+
# Examples:
|
33
|
+
#
|
34
|
+
# MoteSMS.deliver do
|
35
|
+
# to '0041 79 123 12 12'
|
36
|
+
# from 'SENDER'
|
37
|
+
# body 'Hello world'
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Returns result of #deliver.
|
41
|
+
def self.deliver(&block)
|
42
|
+
raise ArgumentError, 'Block missing' unless block_given?
|
43
|
+
Message.new(&block).deliver
|
44
|
+
end
|
45
|
+
end
|
data/mote_sms.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/mote_sms/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'mote_sms'
|
6
|
+
gem.authors = ['Lukas Westermann']
|
7
|
+
gem.email = ['lukas.westermann@at-point.ch']
|
8
|
+
gem.summary = %q{Deliver SMS using MobileTechnics HTTP API.}
|
9
|
+
gem.description = %q{Unofficial ruby adapter for MobileTechnics HTTP Bulk SMS API.
|
10
|
+
Tries to mimick mail API, so users can switch e.g. ActionMailer
|
11
|
+
with this SMS provider.}
|
12
|
+
gem.homepage = 'https://at-point.ch/opensource'
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($\)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = %w{lib}
|
18
|
+
gem.version = MoteSMS::VERSION
|
19
|
+
|
20
|
+
gem.required_ruby_version = '>= 1.9'
|
21
|
+
|
22
|
+
gem.add_dependency 'phony', ['~> 1.7.0']
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rake'
|
25
|
+
gem.add_development_dependency 'rspec', ['~> 2.10']
|
26
|
+
gem.add_development_dependency 'webmock', ['~> 1.8.0']
|
27
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mote_sms/message'
|
3
|
+
|
4
|
+
describe MoteSMS::Message do
|
5
|
+
it 'can be constructed using a block' do
|
6
|
+
msg = described_class.new do
|
7
|
+
from 'SENDER'
|
8
|
+
to '+41 79 123 12 12'
|
9
|
+
body 'This is the SMS content'
|
10
|
+
end
|
11
|
+
msg.from.number.should == 'SENDER'
|
12
|
+
msg.to.normalized_numbers.should == %w{41791231212}
|
13
|
+
msg.body.should == 'This is the SMS content'
|
14
|
+
end
|
15
|
+
|
16
|
+
context '#to' do
|
17
|
+
it 'behaves as accessor' do
|
18
|
+
subject.to = '41791231212'
|
19
|
+
subject.to.normalized_numbers.should == %w{41791231212}
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'behaves as array' do
|
23
|
+
subject.to << '41791231212'
|
24
|
+
subject.to << '41797775544'
|
25
|
+
subject.to.normalized_numbers.should == %w{41791231212 41797775544}
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'normalizes numbers' do
|
29
|
+
subject.to = '+41 79 123 12 12'
|
30
|
+
subject.to.normalized_numbers.should == %w{41791231212}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "#deliver" do
|
35
|
+
let(:transport) { double("Some Transport") }
|
36
|
+
subject { described_class.new(transport) }
|
37
|
+
|
38
|
+
it "sends messages to transport" do
|
39
|
+
transport.should_receive(:deliver).with(subject, {})
|
40
|
+
subject.deliver
|
41
|
+
end
|
42
|
+
|
43
|
+
it "can pass additional attributes to transport" do
|
44
|
+
transport.should_receive(:deliver).with(subject, serviceid: "myapplication")
|
45
|
+
subject.deliver serviceid: "myapplication"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can override per message transport using :transport option" do
|
49
|
+
transport.should_not_receive(:deliver)
|
50
|
+
subject.deliver transport: double(deliver: true)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "uses global MoteSMS.transport if no per message transport defined" do
|
54
|
+
message = described_class.new
|
55
|
+
transport.should_receive(:deliver).with(message, {})
|
56
|
+
MoteSMS.should_receive(:transport) { transport }
|
57
|
+
message.deliver
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mote_sms/number_list'
|
3
|
+
|
4
|
+
describe MoteSMS::NumberList do
|
5
|
+
it 'has length' do
|
6
|
+
subject.length.should == 0
|
7
|
+
subject << '+41 79 111 22 33'
|
8
|
+
subject.length.should == 1
|
9
|
+
5.times { subject << '+41 79 222 33 44' }
|
10
|
+
subject.length.should == 6
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'has empty?' do
|
14
|
+
subject.empty?.should be_true
|
15
|
+
subject << '+41 79 111 22 33'
|
16
|
+
subject.empty?.should be_false
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'can add numbers by string' do
|
20
|
+
subject << '+41 79 111 22 33'
|
21
|
+
subject.normalized_numbers.should == %w{41791112233}
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'can multiple numbers using push' do
|
25
|
+
subject.push '+41 79 111 22 33', '+41 44 111 22 33'
|
26
|
+
subject.normalized_numbers.should == %w{41791112233 41441112233}
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'can push multiple numbers with adding country codes' do
|
30
|
+
subject.push '079 111 22 33', '0041 44 111 22 33', cc: '41', ndc: /(44|79)/
|
31
|
+
subject.normalized_numbers.should == %w{41791112233 41441112233}
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mote_sms/number'
|
3
|
+
|
4
|
+
describe MoteSMS::Number do
|
5
|
+
context 'normalized number' do
|
6
|
+
subject { described_class.new('41443643533') }
|
7
|
+
|
8
|
+
its(:to_s) { should == '+41 44 364 35 33' }
|
9
|
+
its(:number) { should == '41443643533' }
|
10
|
+
its(:to_number) { should == '41443643533' }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'E164 conforming number' do
|
14
|
+
subject { described_class.new('+41 44 3643533') }
|
15
|
+
|
16
|
+
its(:to_s) { should == '+41 44 364 35 33' }
|
17
|
+
its(:number) { should == '41443643533' }
|
18
|
+
its(:to_number) { should == '41443643533' }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'E164 conforming number with name' do
|
22
|
+
subject { described_class.new('Frank: +41 44 3643533', cc: '41') }
|
23
|
+
|
24
|
+
its(:to_s) { should == '+41 44 364 35 33' }
|
25
|
+
its(:number) { should == '41443643533' }
|
26
|
+
its(:to_number) { should == '41443643533' }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'handles local numbers' do
|
30
|
+
subject { described_class.new('079 700 50 90', cc: '41') }
|
31
|
+
|
32
|
+
its(:to_s) { should == '+41 79 700 50 90' }
|
33
|
+
its(:number) { should == '41797005090'}
|
34
|
+
its(:to_number) { should == '41797005090' }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'non conforming number' do
|
38
|
+
it 'raises error when creating' do
|
39
|
+
Proc.new { described_class.new('what ever?') }.should raise_error(ArgumentError, /unable to parse/i)
|
40
|
+
Proc.new { described_class.new('0000') }.should raise_error(ArgumentError, /unable to parse/i)
|
41
|
+
Proc.new { described_class.new('123456789012345678901') }.should raise_error(ArgumentError, /unable to parse/i)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'wrong cc/ndc' do
|
46
|
+
it 'raises error when creating instance with wrong ndc' do
|
47
|
+
Proc.new { described_class.new('+41 44 364 35 33', cc: '41', ndc: '43') }.should raise_error(ArgumentError, /national destination/i)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'vanity numbers' do
|
52
|
+
subject { described_class.new('0800-vanity', vanity: true) }
|
53
|
+
|
54
|
+
its(:to_s) { should == '0800VANITY' }
|
55
|
+
its(:number) { should == '0800VANITY' }
|
56
|
+
its(:to_number) { should == '0800VANITY' }
|
57
|
+
its(:vanity?) { should be_true }
|
58
|
+
|
59
|
+
it 'raises error if more than 11 alpha numeric chars' do
|
60
|
+
Proc.new { described_class.new('1234567890AB', vanity: true) }.should raise_error(ArgumentError, /invalid vanity/i)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'can also be normal phone number' do
|
64
|
+
num = described_class.new('0800 123 12 12', vanity: true)
|
65
|
+
num.to_s.should == '08001231212'
|
66
|
+
num.to_number.should == '08001231212'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'cgi'
|
4
|
+
require 'mote_sms/message'
|
5
|
+
require 'mote_sms/transports/mobile_technics_transport'
|
6
|
+
|
7
|
+
describe MoteSMS::MobileTechnicsTransport do
|
8
|
+
before do
|
9
|
+
@logger = described_class.logger
|
10
|
+
described_class.logger = stub(debug: true, info: true, error: true)
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
described_class.logger = @logger
|
15
|
+
end
|
16
|
+
|
17
|
+
subject { described_class.new(endpoint, "example", "123456") }
|
18
|
+
|
19
|
+
let(:endpoint) { "http://test.nth.ch" }
|
20
|
+
let(:message) {
|
21
|
+
MoteSMS::Message.new do
|
22
|
+
to '0041 079 123 12 12'
|
23
|
+
from 'SENDER'
|
24
|
+
body 'Hello World, with äöü.'
|
25
|
+
end
|
26
|
+
}
|
27
|
+
|
28
|
+
let(:success) {
|
29
|
+
{ body: "Result_code: 00, Message OK", status: 200, headers: { 'X-Nth-SmsId' => '43797917' } }
|
30
|
+
}
|
31
|
+
|
32
|
+
context "#deliver" do
|
33
|
+
it "sends POST to endpoint with URL encoded body" do
|
34
|
+
stub_request(:post, endpoint).with do |req|
|
35
|
+
params = CGI.parse(req.body)
|
36
|
+
params['username'].should == %w{example}
|
37
|
+
params['password'].should == %w{123456}
|
38
|
+
params['text'].should == ['Hello World, with äöü.']
|
39
|
+
params['call-number'].should == ['0041791231212']
|
40
|
+
end.to_return(success)
|
41
|
+
subject.deliver message
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'sends message in single request to multiple recipients' do
|
45
|
+
message.to << '+41 79 333 44 55'
|
46
|
+
message.to << '+41 78 111 22 33'
|
47
|
+
|
48
|
+
stub_request(:post, endpoint).with(body: hash_including('call-number' => '0041791231212;0041793334455;0041781112233')).to_return(success)
|
49
|
+
subject.deliver message
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'raises exception if required parameter is missing' do
|
53
|
+
stub_request(:post, endpoint).to_return(body: 'Result_code: 02, call-number')
|
54
|
+
Proc.new { subject.deliver message }.should raise_error(MoteSMS::MobileTechnicsTransport::ServiceError)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'raises exception if status code is not 200' do
|
58
|
+
stub_request(:post, endpoint).to_return(status: 500)
|
59
|
+
Proc.new { subject.deliver message }.should raise_error(MoteSMS::MobileTechnicsTransport::ServiceError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns message id' do
|
63
|
+
stub_request(:post, endpoint).to_return(success)
|
64
|
+
subject.deliver(message).should == %w{43797917}
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'logs curl compatible output' do
|
68
|
+
io = StringIO.new
|
69
|
+
described_class.logger = Logger.new(io)
|
70
|
+
stub_request(:post, endpoint).to_return(success)
|
71
|
+
subject.deliver message
|
72
|
+
io.rewind
|
73
|
+
io.read.should =~ %r{curl -XPOST 'http://test.nth.ch' -d 'allow_adaption=1&}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "#options" do
|
78
|
+
it 'can be passed in as last item in the constructor' do
|
79
|
+
transport = described_class.new endpoint, 'user', 'pass', allow_adaption: false, validity: 30
|
80
|
+
transport.options[:allow_adaption].should be_false
|
81
|
+
transport.options[:validity].should == 30
|
82
|
+
transport.options[:something].should be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should be exposed as hash' do
|
86
|
+
subject.options[:messageid] = "test"
|
87
|
+
subject.options[:messageid].should == "test"
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'overrides settings from #defaults' do
|
91
|
+
described_class.defaults[:something] = 'global'
|
92
|
+
subject.options[:something] = 'local'
|
93
|
+
|
94
|
+
stub_request(:post, endpoint).with(body: hash_including('something' => 'local')).to_return(success)
|
95
|
+
subject.deliver message
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'is overriden by options passed to #deliver' do
|
99
|
+
subject.options[:something] = 'local'
|
100
|
+
|
101
|
+
stub_request(:post, endpoint).with(body: hash_including('something' => 'deliver')).to_return(success)
|
102
|
+
subject.deliver message, something: 'deliver'
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'evaluates Procs & lambdas' do
|
106
|
+
subject.options[:messageid] = Proc.new { "test" }
|
107
|
+
|
108
|
+
stub_request(:post, endpoint).with(body: hash_including('messageid' => 'test')).to_return(success)
|
109
|
+
subject.deliver message
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'converts allow_adaption to 1 when true' do
|
113
|
+
subject.options[:allow_adaption] = true
|
114
|
+
stub_request(:post, endpoint).with(body: hash_including('allow_adaption' => '1')).to_return(success)
|
115
|
+
subject.deliver message
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'converts allow_adaption to 0 when false' do
|
119
|
+
subject.options[:allow_adaption] = nil
|
120
|
+
stub_request(:post, endpoint).with(body: hash_including('allow_adaption' => '0')).to_return(success)
|
121
|
+
subject.deliver message
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mote_sms/transports/test_transport'
|
3
|
+
|
4
|
+
describe MoteSMS::TestTransport do
|
5
|
+
subject { described_class }
|
6
|
+
before { MoteSMS.deliveries.clear }
|
7
|
+
|
8
|
+
it 'defines global #deliveries' do
|
9
|
+
MoteSMS.should respond_to(:deliveries)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'appends deliveries' do
|
13
|
+
subject.deliver "firstMessage"
|
14
|
+
subject.deliver "secondMessage"
|
15
|
+
MoteSMS.deliveries.should == %w{firstMessage secondMessage}
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mote_sms'
|
3
|
+
|
4
|
+
describe MoteSMS do
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
it 'has a version' do
|
8
|
+
subject::VERSION.should =~ /\d/
|
9
|
+
end
|
10
|
+
|
11
|
+
context "transport" do
|
12
|
+
before { @current_transport = subject.transport }
|
13
|
+
after { subject.transport = @current_transport }
|
14
|
+
let(:transport) { double("transport") }
|
15
|
+
|
16
|
+
it "has no default transport" do
|
17
|
+
subject.transport.should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can change global transport" do
|
21
|
+
subject.transport = transport
|
22
|
+
subject.transport.should == transport
|
23
|
+
end
|
24
|
+
|
25
|
+
context "#deliver" do
|
26
|
+
it 'delivers quick and dirty using global transport' do
|
27
|
+
transport.should_receive(:deliver).with(kind_of(MoteSMS::Message), {})
|
28
|
+
subject.transport = transport
|
29
|
+
subject.deliver { body 'Hello World' }
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'raises error if block is missing' do
|
33
|
+
Proc.new { subject.deliver }.should raise_error(ArgumentError, /block missing/i)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mote_sms
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Lukas Westermann
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: phony
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.7.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.7.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.10'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.10'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: webmock
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.8.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.8.0
|
78
|
+
description: ! "Unofficial ruby adapter for MobileTechnics HTTP Bulk SMS API.\n Tries
|
79
|
+
to mimick mail API, so users can switch e.g. ActionMailer\n with
|
80
|
+
this SMS provider."
|
81
|
+
email:
|
82
|
+
- lukas.westermann@at-point.ch
|
83
|
+
executables: []
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- .travis.yml
|
89
|
+
- Gemfile
|
90
|
+
- LICENSE
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- lib/mote_sms.rb
|
94
|
+
- lib/mote_sms/message.rb
|
95
|
+
- lib/mote_sms/number.rb
|
96
|
+
- lib/mote_sms/number_list.rb
|
97
|
+
- lib/mote_sms/transports.rb
|
98
|
+
- lib/mote_sms/transports/.gitkeep
|
99
|
+
- lib/mote_sms/transports/mobile_technics_transport.rb
|
100
|
+
- lib/mote_sms/transports/test_transport.rb
|
101
|
+
- lib/mote_sms/version.rb
|
102
|
+
- mote_sms.gemspec
|
103
|
+
- spec/mote_sms/message_spec.rb
|
104
|
+
- spec/mote_sms/number_list_spec.rb
|
105
|
+
- spec/mote_sms/number_spec.rb
|
106
|
+
- spec/mote_sms/transports/mobile_technics_transport_spec.rb
|
107
|
+
- spec/mote_sms/transports/test_transport_spec.rb
|
108
|
+
- spec/mote_sms_spec.rb
|
109
|
+
- spec/spec_helper.rb
|
110
|
+
homepage: https://at-point.ch/opensource
|
111
|
+
licenses: []
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '1.9'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
segments:
|
129
|
+
- 0
|
130
|
+
hash: 3261538551323079383
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 1.8.23
|
134
|
+
signing_key:
|
135
|
+
specification_version: 3
|
136
|
+
summary: Deliver SMS using MobileTechnics HTTP API.
|
137
|
+
test_files:
|
138
|
+
- spec/mote_sms/message_spec.rb
|
139
|
+
- spec/mote_sms/number_list_spec.rb
|
140
|
+
- spec/mote_sms/number_spec.rb
|
141
|
+
- spec/mote_sms/transports/mobile_technics_transport_spec.rb
|
142
|
+
- spec/mote_sms/transports/test_transport_spec.rb
|
143
|
+
- spec/mote_sms_spec.rb
|
144
|
+
- spec/spec_helper.rb
|