mobitex 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 +17 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +62 -0
- data/Rakefile +11 -0
- data/lib/mobitex.rb +59 -0
- data/lib/mobitex/configuration.rb +39 -0
- data/lib/mobitex/connection.rb +79 -0
- data/lib/mobitex/connection_errors.rb +50 -0
- data/lib/mobitex/delivery/file_delivery.rb +18 -0
- data/lib/mobitex/delivery/http_delivery.rb +45 -0
- data/lib/mobitex/delivery/test_delivery.rb +24 -0
- data/lib/mobitex/delivery_errors.rb +49 -0
- data/lib/mobitex/message.rb +206 -0
- data/lib/mobitex/message/configuration.rb +44 -0
- data/lib/mobitex/message/patterns.rb +29 -0
- data/lib/mobitex/response.rb +33 -0
- data/lib/mobitex/status.rb +49 -0
- data/lib/mobitex/version.rb +3 -0
- data/mobitex.gemspec +22 -0
- data/test/helper.rb +8 -0
- data/test/test_configuration.rb +103 -0
- data/test/test_mobitex.rb +11 -0
- metadata +118 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
Mobitex gem - wrapper to mobitex API
|
2
|
+
====================================
|
3
|
+
|
4
|
+
[][travis]
|
5
|
+
[][gemnasium]
|
6
|
+
|
7
|
+
[travis]: http://travis-ci.org/tjeden/smscenter
|
8
|
+
[gemnasium]: https://gemnasium.com/tjeden/smscenter
|
9
|
+
|
10
|
+
|
11
|
+
How to use it?
|
12
|
+
--------------
|
13
|
+
|
14
|
+
Gemfile:
|
15
|
+
|
16
|
+
``` ruby
|
17
|
+
gem 'mobitex'
|
18
|
+
```
|
19
|
+
|
20
|
+
Code:
|
21
|
+
|
22
|
+
``` ruby
|
23
|
+
Mobitex.configure { delivery_method :http, { :user => 'username', :pass => 'password' } }
|
24
|
+
Mobitex.deliver(:to => '48123456789', :body => 'Spam bacon sausage and spam')
|
25
|
+
```
|
26
|
+
|
27
|
+
Enjoy!
|
28
|
+
|
29
|
+
More usage examples
|
30
|
+
-------------------
|
31
|
+
|
32
|
+
First, you must setup delivery method:
|
33
|
+
|
34
|
+
``` ruby
|
35
|
+
Mobitex.configure do
|
36
|
+
delivery_method :http, {
|
37
|
+
:user => 'username',
|
38
|
+
:pass => 'password'
|
39
|
+
}
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
Then you can just:
|
44
|
+
|
45
|
+
``` ruby
|
46
|
+
Mobitex.deliver(:to => '48123456789', :body => 'Egg sausage and bacon')
|
47
|
+
```
|
48
|
+
|
49
|
+
...which really is a shortcut for:
|
50
|
+
|
51
|
+
``` ruby
|
52
|
+
Mobitex::Message.new(:to => '48123456789', :body => 'Egg sausage and bacon')
|
53
|
+
```
|
54
|
+
|
55
|
+
You can use DSL to create new message:
|
56
|
+
|
57
|
+
``` ruby
|
58
|
+
message = Mobitex::Message.new do
|
59
|
+
to '48123456789'
|
60
|
+
body 'Egg, Bacon, Spam, Baked Beans, Spam, Sausage and Spam'
|
61
|
+
end
|
62
|
+
```
|
data/Rakefile
ADDED
data/lib/mobitex.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'mobitex/configuration'
|
2
|
+
require 'mobitex/message'
|
3
|
+
require 'mobitex/response'
|
4
|
+
require 'mobitex/status'
|
5
|
+
|
6
|
+
module Mobitex
|
7
|
+
|
8
|
+
def self.new(*args, &block)
|
9
|
+
Mobitex::Message.new(*args, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Public: Configure global options.
|
13
|
+
#
|
14
|
+
# Examples
|
15
|
+
#
|
16
|
+
# Mobitex.configure do
|
17
|
+
# delivery_method :http, {
|
18
|
+
# :user => 'connectmed',
|
19
|
+
# :pass => '56asd4Fgi'
|
20
|
+
# }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
def self.configure(&block)
|
24
|
+
return unless block_given?
|
25
|
+
|
26
|
+
if block.arity == 1
|
27
|
+
yield Configuration.instance
|
28
|
+
else
|
29
|
+
Configuration.instance.instance_eval &block
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.delivery_method
|
34
|
+
Configuration.instance.delivery_method
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.deliver(*args, &block)
|
38
|
+
message = self.new(*args, &block)
|
39
|
+
message.deliver
|
40
|
+
message
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.register_observer(observer)
|
44
|
+
unless @@delivery_notification_observers.include?(observer)
|
45
|
+
@@delivery_notification_observers << observer
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
@@delivery_notification_observers = []
|
52
|
+
|
53
|
+
def self.inform_observers(message)
|
54
|
+
@@delivery_notification_observers.each do |observer|
|
55
|
+
observer.delivered_message(message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'mobitex/delivery/file_delivery'
|
2
|
+
require 'mobitex/delivery/http_delivery'
|
3
|
+
require 'mobitex/delivery/test_delivery'
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module Mobitex
|
7
|
+
|
8
|
+
class Configuration
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
reset!
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def delivery_method(method = nil, settings = {})
|
17
|
+
return @delivery_method if @delivery_method && method.nil?
|
18
|
+
|
19
|
+
@delivery_method = lookup_delivery_method(method).new(settings)
|
20
|
+
end
|
21
|
+
|
22
|
+
def lookup_delivery_method(method)
|
23
|
+
case method
|
24
|
+
when :http, nil then Mobitex::HTTPDelivery
|
25
|
+
when :file then Mobitex::FileDelivery
|
26
|
+
when :test then Mobitex::TestDelivery
|
27
|
+
else method
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def reset!
|
34
|
+
@delivery_method = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'mobitex/connection_errors'
|
3
|
+
|
4
|
+
module Mobitex
|
5
|
+
|
6
|
+
class Connection
|
7
|
+
attr_reader :endpoint
|
8
|
+
|
9
|
+
def initialize(endpoint)
|
10
|
+
raise ArgumentError, 'Missing endpoint URI' unless endpoint
|
11
|
+
|
12
|
+
self.endpoint = endpoint
|
13
|
+
end
|
14
|
+
|
15
|
+
# Executes a GET request.
|
16
|
+
# Used to get (find) resources.
|
17
|
+
def get(path, params = {})
|
18
|
+
request(:get, path, params)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Executes a POST request.
|
22
|
+
# Used to create new resources.
|
23
|
+
def post(path, params = {})
|
24
|
+
request(:post, path, params)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Set URI for remote service.
|
30
|
+
def endpoint=(endpoint)
|
31
|
+
@endpoint = endpoint.is_a?(URI) ? endpoint : URI.parse(endpoint)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Makes a request to the remote service.
|
35
|
+
def request(method, path, params = {})
|
36
|
+
response = http.send(method, path, query(params))
|
37
|
+
handle_response(response)
|
38
|
+
rescue Timeout::Error => e
|
39
|
+
raise TimeoutError.new(e.message)
|
40
|
+
end
|
41
|
+
|
42
|
+
def query(params)
|
43
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
44
|
+
params.map{ |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join('&')
|
45
|
+
end
|
46
|
+
|
47
|
+
# Handles response and error codes from the remote service.
|
48
|
+
def handle_response(response)
|
49
|
+
case response.code.to_i
|
50
|
+
when 301, 302 then raise(Redirection.new(response))
|
51
|
+
when 200...400 then response
|
52
|
+
when 400...500 then raise(ClientError.new(response))
|
53
|
+
when 500...600 then raise(ServerError.new(response))
|
54
|
+
else raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates new Net::HTTP instance for communication with the remote service.
|
59
|
+
def http
|
60
|
+
configure_http(new_http)
|
61
|
+
end
|
62
|
+
|
63
|
+
def new_http
|
64
|
+
Net::HTTP.new(@endpoint.host, @endpoint.port)
|
65
|
+
end
|
66
|
+
|
67
|
+
def configure_http(http)
|
68
|
+
# Net::HTTP timeouts default to 60 seconds.
|
69
|
+
if @timeout
|
70
|
+
http.open_timeout = @timeout
|
71
|
+
http.read_timeout = @timeout
|
72
|
+
end
|
73
|
+
|
74
|
+
http
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Mobitex
|
2
|
+
|
3
|
+
class ConnectionError < StandardError
|
4
|
+
attr_reader :response
|
5
|
+
|
6
|
+
def initialize(response, message = nil)
|
7
|
+
@response = response
|
8
|
+
@message = message
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
message = "Connection Failed."
|
13
|
+
message << " Response Code = #{response.code}." if response.respond_to?(:code)
|
14
|
+
message << " Response Message = #{response.message}." if response.respond_to?(:message)
|
15
|
+
message
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
# Raised when a Timeout::Error occurs.
|
21
|
+
class TimeoutError < ConnectionError
|
22
|
+
|
23
|
+
def initialize(message)
|
24
|
+
@message = message
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
@message
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
# 3xx Redirection
|
34
|
+
class Redirection < ConnectionError # :nodoc:
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
response['Location'] ? "#{super} => #{response['Location']}" : super
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
# 4xx Client Error
|
43
|
+
class ClientError < ConnectionError
|
44
|
+
end
|
45
|
+
|
46
|
+
# 5xx Server Error
|
47
|
+
class ServerError < ConnectionError
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'mobitex/connection'
|
2
|
+
|
3
|
+
module Mobitex
|
4
|
+
|
5
|
+
class HTTPDelivery
|
6
|
+
attr_accessor :settings
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
def initialize(values)
|
10
|
+
self.settings = {
|
11
|
+
:user => nil,
|
12
|
+
:pass => nil,
|
13
|
+
:return_response => false
|
14
|
+
}.merge!(values)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deliver!(message)
|
18
|
+
@response = nil
|
19
|
+
|
20
|
+
message.sanitize!
|
21
|
+
|
22
|
+
raw_response = connection.post('/send.php', {
|
23
|
+
:user => settings[:user],
|
24
|
+
:pass => settings[:pass],
|
25
|
+
:type => message.type,
|
26
|
+
:from => message.from,
|
27
|
+
:number => message.to,
|
28
|
+
:text => message.body,
|
29
|
+
:ext_id => message.message_id
|
30
|
+
})
|
31
|
+
|
32
|
+
@response = Response.parse(raw_response.body)
|
33
|
+
|
34
|
+
settings[:return_response] ? @response : self
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def connection
|
40
|
+
@connection ||= Connection.new('http://api.statsms.net')
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Mobitex
|
2
|
+
|
3
|
+
class TestDelivery
|
4
|
+
attr_accessor :settings
|
5
|
+
|
6
|
+
def self.deliveries
|
7
|
+
@@deliveries ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.deliveries=(deliveries)
|
11
|
+
@@deliveries = deliveries
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(values)
|
15
|
+
@settings = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def deliver!(message)
|
19
|
+
self.class.deliveries << message
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Mobitex
|
2
|
+
|
3
|
+
# Delivery errors
|
4
|
+
class DeliveryError < StandardError
|
5
|
+
attr_reader :response
|
6
|
+
|
7
|
+
def initialize(response, message = nil)
|
8
|
+
@response = response
|
9
|
+
@message = message
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
message = "Delivery Failed."
|
14
|
+
message << " Delivery Status = #{response['Status']}." if response.has_key?('Status')
|
15
|
+
message << " Delivery Id = #{response['Id']}." if response.has_key?('Id')
|
16
|
+
message << " Delivery Number = #{response['Number']}." if response.has_key?('Number')
|
17
|
+
message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class DeliveryCallbackError < DeliveryError; end
|
22
|
+
|
23
|
+
class UnauthorizedAccess < DeliveryError; end
|
24
|
+
|
25
|
+
class BlankText < DeliveryError; end
|
26
|
+
|
27
|
+
class NoSender < DeliveryError; end
|
28
|
+
|
29
|
+
class TextTooLong < DeliveryError; end
|
30
|
+
|
31
|
+
class BadNumber < DeliveryError; end
|
32
|
+
|
33
|
+
class BadType < DeliveryError; end
|
34
|
+
|
35
|
+
class TypeNotSupported < DeliveryError; end
|
36
|
+
|
37
|
+
class SenderUnauthorized < DeliveryError; end
|
38
|
+
|
39
|
+
class System < DeliveryError; end
|
40
|
+
|
41
|
+
class EmptyAccount < DeliveryError; end
|
42
|
+
|
43
|
+
class AccountInactive < DeliveryError; end
|
44
|
+
|
45
|
+
class DestinationNetworkUnavailable < DeliveryError; end
|
46
|
+
|
47
|
+
class BadMessageId < DeliveryError; end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'mobitex/message/configuration'
|
2
|
+
require 'mobitex/message/patterns'
|
3
|
+
|
4
|
+
module Mobitex
|
5
|
+
|
6
|
+
class Message
|
7
|
+
include Patterns
|
8
|
+
|
9
|
+
attr_accessor :delivery_handler
|
10
|
+
attr_writer *Configuration::VALID_OPTIONS
|
11
|
+
|
12
|
+
# Public: Set default values for Message attributes.
|
13
|
+
#
|
14
|
+
# Examples
|
15
|
+
#
|
16
|
+
# Mobitex::Message.configure do |config|
|
17
|
+
# config.body = 'My default body'
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# message = Mobitex::Message.new
|
21
|
+
# message.body
|
22
|
+
# # => 'My default body'
|
23
|
+
#
|
24
|
+
def self.configure(&block)
|
25
|
+
return unless block_given?
|
26
|
+
|
27
|
+
if block.arity == 1
|
28
|
+
yield Configuration.instance
|
29
|
+
else
|
30
|
+
Configuration.instance.instance_eval &block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: Initialize a new Message.
|
35
|
+
#
|
36
|
+
# args - Hash of attributes of the message.
|
37
|
+
# block - An optional block that can be used to build a new message using DSL-like syntax.
|
38
|
+
#
|
39
|
+
# Examples
|
40
|
+
#
|
41
|
+
# message = Mobitex::Message.new(:to => '48123456789', :body => 'Hello!')
|
42
|
+
#
|
43
|
+
# message = Mobitex::Message.new do
|
44
|
+
# to '48123456789'
|
45
|
+
# body 'Hello!'
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# message = Mobitex::Message.new do |m|
|
49
|
+
# m.to = '48123456789'
|
50
|
+
# m.body = 'Hello!'
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
def initialize(*args, &block)
|
54
|
+
@delivery_handler = nil
|
55
|
+
@delivery_method = Mobitex.delivery_method.dup
|
56
|
+
|
57
|
+
options = args.first.respond_to?(:each_pair) ? args.first : {}
|
58
|
+
Configuration::VALID_OPTIONS.each do |key|
|
59
|
+
send("#{key}=", options[key] || options[key.to_sym] || Configuration.instance.send("#{key}"))
|
60
|
+
end
|
61
|
+
|
62
|
+
if block_given?
|
63
|
+
if block.arity == 1
|
64
|
+
yield self
|
65
|
+
else
|
66
|
+
instance_eval &block
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
# Generate special accessors that act like readers when called without an argument or like writers when called
|
74
|
+
# with argument.
|
75
|
+
#
|
76
|
+
# Examples
|
77
|
+
#
|
78
|
+
# message = Mobitex::Message.new
|
79
|
+
# message.body 'Hello!' # This is equivalent to `message.body = 'Hello!'`
|
80
|
+
# message.body # => 'Hello!'
|
81
|
+
#
|
82
|
+
Configuration::VALID_OPTIONS.each do |key|
|
83
|
+
define_method key do |*value| # def from(*value)
|
84
|
+
if value.first # if value.first
|
85
|
+
self.send("#{key}=", value.first) # self.send("from=", value.first)
|
86
|
+
else # else
|
87
|
+
instance_variable_get("@#{key}") # instance_variable_get("@from")
|
88
|
+
end # end
|
89
|
+
end # end
|
90
|
+
end
|
91
|
+
|
92
|
+
def length
|
93
|
+
body ? body.length + body.count(DOUBLE_CHARACTERS) : 0
|
94
|
+
end
|
95
|
+
|
96
|
+
def type(*value)
|
97
|
+
if value.first
|
98
|
+
self.type = value.first
|
99
|
+
else
|
100
|
+
@type ||= length > MAX_LENGTH['sms'] ? 'concat' : 'sms'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Validation #######################################################################################################
|
105
|
+
|
106
|
+
def errors
|
107
|
+
@errors ||= Set.new
|
108
|
+
end
|
109
|
+
|
110
|
+
def invalid?
|
111
|
+
!valid?
|
112
|
+
end
|
113
|
+
|
114
|
+
def valid?
|
115
|
+
errors.clear
|
116
|
+
validate!
|
117
|
+
end
|
118
|
+
|
119
|
+
def type_valid?
|
120
|
+
TYPES.include?(type.to_s)
|
121
|
+
end
|
122
|
+
|
123
|
+
def to_valid?
|
124
|
+
!(to.to_s !~ BULK_NUMBERS_REGEXP)
|
125
|
+
end
|
126
|
+
|
127
|
+
def body_valid?
|
128
|
+
(!type_valid? && !(body !~ NON_WHITESPACE_REGEXP)) || # We can't determine validity of body if type is invalid
|
129
|
+
(length > 0 && length <= MAX_LENGTH[type] && !(body !~ NON_WHITESPACE_REGEXP) && (type != 'wap_push' || !(body !~ WAP_PUSH_REGEXP)))
|
130
|
+
end
|
131
|
+
|
132
|
+
def from_valid?
|
133
|
+
!(from.to_s !~ FROM_REGEXP)
|
134
|
+
end
|
135
|
+
|
136
|
+
def message_id_valid?
|
137
|
+
!(message_id.to_s !~ MESSAGE_ID_REGEXP)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Sanitization #####################################################################################################
|
141
|
+
|
142
|
+
def sanitize!
|
143
|
+
if from.is_a?(Numeric) || from =~ /^\d+$/
|
144
|
+
self.from = from.to_s[0...16]
|
145
|
+
else
|
146
|
+
self.from = from.to_s.gsub(/[^a-zA-Z0-9]/, '')[0...11]
|
147
|
+
end
|
148
|
+
|
149
|
+
self.to = [*to].flatten.map(&:to_s).join(BULK_DELIMITER)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Delivery #########################################################################################################
|
153
|
+
|
154
|
+
def deliver
|
155
|
+
if delivery_handler
|
156
|
+
delivery_handler.deliver_sms(self) { do_delivery }
|
157
|
+
else
|
158
|
+
do_delivery
|
159
|
+
end
|
160
|
+
|
161
|
+
inform_observers
|
162
|
+
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
def deliver!
|
167
|
+
response = delivery_method.deliver!(self)
|
168
|
+
|
169
|
+
inform_observers
|
170
|
+
|
171
|
+
delivery_method.settings[:return_response] ? response : self
|
172
|
+
end
|
173
|
+
|
174
|
+
def delivery_method(method = nil, settings = {})
|
175
|
+
if method
|
176
|
+
@delivery_method = Mobitex::Configuration.instance.lookup_delivery_method(method).new(settings)
|
177
|
+
else
|
178
|
+
@delivery_method
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def validate!
|
185
|
+
errors << :type unless type_valid?
|
186
|
+
errors << :to unless to_valid?
|
187
|
+
errors << :body unless body_valid?
|
188
|
+
errors << :from unless from_valid?
|
189
|
+
errors << :message_id unless message_id_valid?
|
190
|
+
|
191
|
+
errors.empty?
|
192
|
+
end
|
193
|
+
|
194
|
+
def inform_observers
|
195
|
+
Mobitex.inform_observers(self)
|
196
|
+
end
|
197
|
+
|
198
|
+
def do_delivery
|
199
|
+
delivery_method.deliver!(self) if perform_deliveries
|
200
|
+
rescue Exception => e # Net::HTTP errors
|
201
|
+
raise e if raise_delivery_errors
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Mobitex
|
4
|
+
|
5
|
+
class Message
|
6
|
+
|
7
|
+
class Configuration
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
VALID_OPTIONS = [
|
11
|
+
:type,
|
12
|
+
:from,
|
13
|
+
:to,
|
14
|
+
:body,
|
15
|
+
:message_id,
|
16
|
+
:perform_deliveries,
|
17
|
+
:raise_delivery_errors
|
18
|
+
]
|
19
|
+
|
20
|
+
attr_accessor *VALID_OPTIONS
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
reset!
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def reset!
|
30
|
+
@type = 'sms'
|
31
|
+
@from = 'SmsService'
|
32
|
+
@to = nil
|
33
|
+
@body = nil
|
34
|
+
@message_id = nil
|
35
|
+
|
36
|
+
@perform_deliveries = true
|
37
|
+
@raise_delivery_errors = true
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Mobitex
|
4
|
+
|
5
|
+
class Message
|
6
|
+
|
7
|
+
module Patterns
|
8
|
+
TYPES = %w(sms concat sms_flash wap_push binary)
|
9
|
+
NUMBER_REGEXP = /^\d{11}$/
|
10
|
+
BULK_DELIMITER = ','
|
11
|
+
BULK_NUMBERS_LIMIT = 500
|
12
|
+
BULK_NUMBERS_REGEXP = %r!^(\d{11}#{Regexp.escape(BULK_DELIMITER)}){0,#{BULK_NUMBERS_LIMIT - 1}}\d{11}$!o
|
13
|
+
NUMERIC_FROM_REGEXP = /^\d{1,16}$/
|
14
|
+
ALPHANUMERIC_FROM_REGEXP = /^[a-zA-Z0-9]{1,11}$/
|
15
|
+
FROM_REGEXP = Regexp.union(NUMERIC_FROM_REGEXP, ALPHANUMERIC_FROM_REGEXP)
|
16
|
+
MESSAGE_ID_CHARACTERS = '!@#$%^&*()_+-={}|[]:<>'
|
17
|
+
MESSAGE_ID_REGEXP = %r!^[a-zA-Z0-9#{Regexp.escape(MESSAGE_ID_CHARACTERS)}]{0,50}$!o
|
18
|
+
WAP_PUSH_REGEXP = /\S+\|https?\:\/\//
|
19
|
+
DOUBLE_CHARACTERS = "[]~^{}|\\\r\n"
|
20
|
+
STANDARD_CHARACTERS = %q{@£$¥èéùìòÇØøÅå_^{}\[~]|ÆæßÉ!"#¤%&'()*+,-./:;<=>?ÄÖÑܧ¿äöñüà }
|
21
|
+
SPECIAL_CHARACTER_REGEXP = %r![^a-zA-Z0-9#{Regexp.escape(STANDARD_CHARACTERS)}\r\n]!o
|
22
|
+
MAX_LENGTH = {'sms' => 160, 'sms_flash' => 160, 'concat' => 459, 'wap_push' => 225, 'binary' => 280}
|
23
|
+
MAX_LENGTH.default = -1
|
24
|
+
NON_WHITESPACE_REGEXP = %r![^\s#{[0x3000].pack("U")}]!
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Mobitex
|
2
|
+
|
3
|
+
class Response
|
4
|
+
attr_reader :statuses
|
5
|
+
|
6
|
+
# Public: Parses raw statuses from Mobitex response.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# response = Mobitex::Response.parse("Status: 001, Id: 3e2dc963309c6b574f6c7467a62ef25b, Number: 123456789\nStatus: 106, Id: 251eb8c426466a149bacf15f6c00eacf, Number: 987654321")
|
11
|
+
# response.statuses.length # => 2
|
12
|
+
# response.statuses.first.status # => '001'
|
13
|
+
# response.statuses.first.id # => '3e2dc963309c6b574f6c7467a62ef25b'
|
14
|
+
# response.statuses.first.number # => '123456789'
|
15
|
+
#
|
16
|
+
# Returns Response object.
|
17
|
+
def self.parse(raw_response)
|
18
|
+
statuses = []
|
19
|
+
|
20
|
+
raw_response.lines.map do |raw_status|
|
21
|
+
statuses << Status.parse(raw_status)
|
22
|
+
end
|
23
|
+
|
24
|
+
new(statuses)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(statuses = [])
|
28
|
+
@statuses = statuses
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Mobitex
|
2
|
+
|
3
|
+
class Status
|
4
|
+
attr_reader :attributes
|
5
|
+
|
6
|
+
# Public: Parses single raw status from Mobitex response.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# status = Mobitex::Status.parse('Status: 001, Id: 3e2dc963309c6b574f6c7467a62ef25b, Number: 123456789')
|
11
|
+
# status.status # => '001'
|
12
|
+
# status.id # => '3e2dc963309c6b574f6c7467a62ef25b'
|
13
|
+
# status.number # => '123456789'
|
14
|
+
#
|
15
|
+
# Returns Status object.
|
16
|
+
def self.parse(raw_status)
|
17
|
+
attributes = {}
|
18
|
+
|
19
|
+
raw_status.to_s.split(',').map do |raw_attribute|
|
20
|
+
part = raw_attribute.partition(':')
|
21
|
+
attributes[part.first.strip] = part.last.strip
|
22
|
+
end
|
23
|
+
|
24
|
+
new(attributes)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(attributes = {})
|
28
|
+
@attributes = attributes
|
29
|
+
end
|
30
|
+
|
31
|
+
def status
|
32
|
+
attributes['Status']
|
33
|
+
end
|
34
|
+
|
35
|
+
def id
|
36
|
+
attributes['Id']
|
37
|
+
end
|
38
|
+
|
39
|
+
def number
|
40
|
+
attributes['Number']
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid?
|
44
|
+
status == '002'
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/mobitex.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/mobitex/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ['Aleksadner Dąbrowski', 'Karol Sarnacki']
|
6
|
+
gem.email = ['tjeden@rubysfera.pl', 'sodercober@gmail.com']
|
7
|
+
gem.description = "Ruby interface to Mobitex's Smscenter API"
|
8
|
+
gem.summary = "Ruby interface to Mobitex's Smscenter API"
|
9
|
+
gem.homepage = 'https://github.com/tjeden/smscenter'
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = 'mobitex'
|
15
|
+
gem.require_paths = ['lib']
|
16
|
+
gem.version = Mobitex::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'minitest', ['>= 2.0.0']
|
19
|
+
gem.add_development_dependency 'rake'
|
20
|
+
gem.add_development_dependency 'simplecov'
|
21
|
+
gem.add_development_dependency 'webmock'
|
22
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class CustomDelivery
|
4
|
+
attr_accessor :settings
|
5
|
+
|
6
|
+
def initialize(values)
|
7
|
+
@settings = {}.merge!(values)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Mobitex do
|
12
|
+
|
13
|
+
before do
|
14
|
+
Mobitex::Configuration.instance.send(:reset!)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#defaults' do
|
18
|
+
|
19
|
+
it 'configures HTTP as default delivery method' do
|
20
|
+
Mobitex.delivery_method.class.must_equal Mobitex::HTTPDelivery
|
21
|
+
Mobitex.delivery_method.settings[:user].must_be_nil
|
22
|
+
Mobitex.delivery_method.settings[:pass].must_be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'with DSL configuration block' do
|
26
|
+
|
27
|
+
it 'configures HTTP delivery' do
|
28
|
+
Mobitex.configure do
|
29
|
+
delivery_method :http, {
|
30
|
+
:user => 'john',
|
31
|
+
:pass => 'doe'
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
Mobitex.delivery_method.class.must_equal Mobitex::HTTPDelivery
|
36
|
+
Mobitex.delivery_method.settings[:user].must_equal 'john'
|
37
|
+
Mobitex.delivery_method.settings[:pass].must_equal 'doe'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'configures File delivery' do
|
41
|
+
Mobitex.configure{ delivery_method :file, {:location => './my_tmp'} }
|
42
|
+
|
43
|
+
Mobitex.delivery_method.class.must_equal Mobitex::FileDelivery
|
44
|
+
Mobitex.delivery_method.settings[:location].must_equal './my_tmp'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'configures Test delivery' do
|
48
|
+
Mobitex.configure{ delivery_method :test }
|
49
|
+
|
50
|
+
Mobitex.delivery_method.class.must_equal Mobitex::TestDelivery
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'configures custom delivery' do
|
54
|
+
Mobitex.configure{ delivery_method CustomDelivery, {:option1 => 'value1', :option2 => 'value2'} }
|
55
|
+
|
56
|
+
Mobitex.delivery_method.class.must_equal CustomDelivery
|
57
|
+
Mobitex.delivery_method.settings[:option1].must_equal 'value1'
|
58
|
+
Mobitex.delivery_method.settings[:option2].must_equal 'value2'
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'with configuration block' do
|
64
|
+
|
65
|
+
it 'configures HTTP delivery' do
|
66
|
+
Mobitex.configure do |config|
|
67
|
+
config.delivery_method :http, {
|
68
|
+
:user => 'jack',
|
69
|
+
:pass => 'daniels'
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
Mobitex.delivery_method.class.must_equal Mobitex::HTTPDelivery
|
74
|
+
Mobitex.delivery_method.settings[:user].must_equal 'jack'
|
75
|
+
Mobitex.delivery_method.settings[:pass].must_equal 'daniels'
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'configures File delivery' do
|
79
|
+
Mobitex.configure{ |config| config.delivery_method :file, {:location => './my_new_tmp'} }
|
80
|
+
|
81
|
+
Mobitex.delivery_method.class.must_equal Mobitex::FileDelivery
|
82
|
+
Mobitex.delivery_method.settings[:location].must_equal './my_new_tmp'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'configures Test delivery' do
|
86
|
+
Mobitex.configure{ |config| config.delivery_method :test }
|
87
|
+
|
88
|
+
Mobitex.delivery_method.class.must_equal Mobitex::TestDelivery
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'configures custom delivery' do
|
92
|
+
Mobitex.configure{ |config| config.delivery_method CustomDelivery, {:new_option1 => 'new_value1', :new_option2 => 'new_value2'} }
|
93
|
+
|
94
|
+
Mobitex.delivery_method.class.must_equal CustomDelivery
|
95
|
+
Mobitex.delivery_method.settings[:new_option1].must_equal 'new_value1'
|
96
|
+
Mobitex.delivery_method.settings[:new_option2].must_equal 'new_value2'
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mobitex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Aleksadner Dąbrowski
|
9
|
+
- Karol Sarnacki
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-03-28 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: minitest
|
17
|
+
requirement: &70166249740780 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.0.0
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70166249740780
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake
|
28
|
+
requirement: &70166249740360 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *70166249740360
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: simplecov
|
39
|
+
requirement: &70166249739900 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70166249739900
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: webmock
|
50
|
+
requirement: &70166249739480 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *70166249739480
|
59
|
+
description: Ruby interface to Mobitex's Smscenter API
|
60
|
+
email:
|
61
|
+
- tjeden@rubysfera.pl
|
62
|
+
- sodercober@gmail.com
|
63
|
+
executables: []
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files: []
|
66
|
+
files:
|
67
|
+
- .gitignore
|
68
|
+
- .travis.yml
|
69
|
+
- Gemfile
|
70
|
+
- README.md
|
71
|
+
- Rakefile
|
72
|
+
- lib/mobitex.rb
|
73
|
+
- lib/mobitex/configuration.rb
|
74
|
+
- lib/mobitex/connection.rb
|
75
|
+
- lib/mobitex/connection_errors.rb
|
76
|
+
- lib/mobitex/delivery/file_delivery.rb
|
77
|
+
- lib/mobitex/delivery/http_delivery.rb
|
78
|
+
- lib/mobitex/delivery/test_delivery.rb
|
79
|
+
- lib/mobitex/delivery_errors.rb
|
80
|
+
- lib/mobitex/message.rb
|
81
|
+
- lib/mobitex/message/configuration.rb
|
82
|
+
- lib/mobitex/message/patterns.rb
|
83
|
+
- lib/mobitex/response.rb
|
84
|
+
- lib/mobitex/status.rb
|
85
|
+
- lib/mobitex/version.rb
|
86
|
+
- mobitex.gemspec
|
87
|
+
- test/helper.rb
|
88
|
+
- test/test_configuration.rb
|
89
|
+
- test/test_mobitex.rb
|
90
|
+
homepage: https://github.com/tjeden/smscenter
|
91
|
+
licenses: []
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 1.8.11
|
111
|
+
signing_key:
|
112
|
+
specification_version: 3
|
113
|
+
summary: Ruby interface to Mobitex's Smscenter API
|
114
|
+
test_files:
|
115
|
+
- test/helper.rb
|
116
|
+
- test/test_configuration.rb
|
117
|
+
- test/test_mobitex.rb
|
118
|
+
has_rdoc:
|