mblox 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/README.md +2 -2
- data/lib/mblox.rb +0 -1
- data/lib/mblox/sms.rb +38 -25
- data/lib/mblox/sms_response.rb +69 -0
- data/lib/mblox/version.rb +1 -1
- data/mblox.gemspec +1 -0
- data/spec/configuration_spec.rb +43 -11
- data/spec/sms_response_result_spec.rb +62 -0
- data/spec/sms_spec.rb +116 -70
- data/spec/spec_helper.rb +5 -14
- metadata +22 -28
- data/lib/mblox/sms_error.rb +0 -4
- data/spec/log_spec.rb +0 -35
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MmIxY2E0ZmM4NmU1ZWMwZmYwNGFmMDMyZGJkNDY4ZmQ2ZjhmNTM4Mw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTExZGY3ODg3Mzg2M2RkNmQwMTUxNzI5YzRmMGMzYzc3ZjA1NzhlZg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MGQ2MjM0YjY0MDk0MWU1MmIzNTRlZDQwZGIyZjhiNGQ2ZDQ3YjI1ODJmMjk1
|
10
|
+
YmVlZWViMGIxMjNhY2U1OGI4OGY3YzNlNWM4MGRkNTNjNDdjMzI0NjcxMDZi
|
11
|
+
YjMwY2MxMjk2ZjU1OThkMjM5MWQ2ZTBhNjQ1NTllZjE2MjJmNDg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTI0ZGM4YTlhNzZmMGU4ZTVkNjg0MjQ5NmUzZTBiZjNlNzBkNDY2Mjg4N2Y1
|
14
|
+
ZDQyNDUzMjMwMzk0MjI3YTIxM2UzN2FiYWM4YzkxMTg0ZGM2ZTQ0NzE1ZWI3
|
15
|
+
NmIwMjRjMzk4MmY4NTRiZmIyZjMxYTY3MzE1Y2FjODA1YTY4YmQ=
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ Configuration
|
|
29
29
|
config.partner_name = ...
|
30
30
|
config.tariff = ...
|
31
31
|
config.service_id = ...
|
32
|
-
|
32
|
+
|
33
33
|
# You can also configure some logging options
|
34
34
|
# In a Rails environment, config.logger will default to Rails.logger and config.log_at will default to :debug
|
35
35
|
# config.log_at means the level at which Mblox will log.
|
@@ -38,7 +38,7 @@ Configuration
|
|
38
38
|
# logging will be suppressed because it is below the log level of the logger.
|
39
39
|
config.logger = Logger.new(STDOUT)
|
40
40
|
config.log_at :info
|
41
|
-
|
41
|
+
|
42
42
|
# What to do if messages are longer than 160 characters. Default is :raise_error
|
43
43
|
# Other options are :truncate and :split
|
44
44
|
config.on_message_too_long = :truncate
|
data/lib/mblox.rb
CHANGED
data/lib/mblox/sms.rb
CHANGED
@@ -1,26 +1,33 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'active_support/core_ext/hash'
|
2
3
|
require 'addressable/uri'
|
3
4
|
require 'builder'
|
4
5
|
require "net/https"
|
6
|
+
require 'mblox/sms_response'
|
5
7
|
|
6
8
|
module Mblox
|
7
9
|
class Sms
|
10
|
+
class InvalidPhoneNumberError < ::ArgumentError; end
|
11
|
+
class InvalidMessageError < ::ArgumentError; end
|
8
12
|
MAX_LENGTH = 160
|
9
13
|
MAX_SECTION_LENGTH = MAX_LENGTH - "(MSG XXX/XXX): ".size
|
14
|
+
ILLEGAL_CHARACTERS = /([^a-zA-Z0-9!"#$\%&'\(\)*+,-.\/:;<=>?@_£¤¥§¿i¡ÄÅÆÇÉÑÖØÜßáäåæèéìñòöøùü\n\r\tí ])/
|
10
15
|
|
11
16
|
attr_reader :phone, :message
|
12
17
|
|
13
18
|
ON_MESSAGE_TOO_LONG_HANDLER = {
|
14
|
-
:raise_error => Proc.new { raise
|
19
|
+
:raise_error => Proc.new { raise InvalidMessageError, "Message cannot be longer than #{MAX_LENGTH} characters" },
|
15
20
|
:truncate => Proc.new { |message| Mblox.log "Truncating message due to length. Message was: \"#{message}\" but will now be \"#{message = message[0,MAX_LENGTH]}\""; [message] },
|
16
21
|
:split => Proc.new { |message| split_message(message) }
|
17
22
|
}
|
18
23
|
|
19
24
|
def initialize(phone,message)
|
20
25
|
phone = phone.to_s
|
21
|
-
raise
|
22
|
-
raise
|
23
|
-
raise
|
26
|
+
raise InvalidPhoneNumberError, "Phone number must be ten digits" unless /\A[0-9]{10}\z/.match(phone)
|
27
|
+
raise InvalidPhoneNumberError, "Phone number cannot begin with 0 or 1" if ['0','1'].include?(phone[0].to_s)
|
28
|
+
raise InvalidMessageError, "Message cannot be blank" if message.empty?
|
29
|
+
illegal_characters = ILLEGAL_CHARACTERS.match(message).to_a
|
30
|
+
raise InvalidMessageError, "Message cannot contain the following special characters: #{illegal_characters.uniq.join(', ')}" unless illegal_characters.size.zero?
|
24
31
|
Mblox.log "WARNING: Some characters may be lost because the message must be broken into at least 1000 sections" if message.size > (999 * MAX_SECTION_LENGTH)
|
25
32
|
@message = (message.size > MAX_LENGTH) ? ON_MESSAGE_TOO_LONG_HANDLER[Mblox.config.on_message_too_long].call(message) : [message.dup]
|
26
33
|
@phone = "1#{phone}"
|
@@ -31,23 +38,12 @@ module Mblox
|
|
31
38
|
end
|
32
39
|
private
|
33
40
|
def commit(request_body)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
response = http.start {|http| http.request(request) }
|
41
|
-
response = response.body
|
42
|
-
Mblox.log "Mblox responds with:\n#{response}"
|
43
|
-
build_response(Hash.from_xml(response))
|
44
|
-
end
|
45
|
-
|
46
|
-
def build_response(result)
|
47
|
-
result = result['NotificationRequestResult']
|
48
|
-
result_header = result['NotificationResultHeader']
|
49
|
-
subscriber_result = result['NotificationResultList']['NotificationResult']['SubscriberResult']
|
50
|
-
"RequestResult: \"#{result_header['RequestResultCode']}:#{result_header['RequestResultText']}\" / SubscriberResult: \"#{subscriber_result['SubscriberResultCode']}:#{subscriber_result['SubscriberResultText']}\""
|
41
|
+
Mblox.log "Sending SMS to Mblox:\n#{request_body}"
|
42
|
+
request = self.class.request
|
43
|
+
request.body = request_body
|
44
|
+
response = self.class.http.start{ |http| http.request(request) }.body
|
45
|
+
Mblox.log "Mblox responds with:\n#{response}"
|
46
|
+
SmsResponse.new(response)
|
51
47
|
end
|
52
48
|
|
53
49
|
def build(message)
|
@@ -58,11 +54,13 @@ module Mblox
|
|
58
54
|
nh.PartnerName(Mblox.config.partner_name)
|
59
55
|
nh.PartnerPassword(Mblox.config.password)
|
60
56
|
end
|
61
|
-
nr.NotificationList(:BatchID =>
|
62
|
-
nl.Notification(:SequenceNumber =>
|
63
|
-
n.Message
|
57
|
+
nr.NotificationList(:BatchID => 1) do |nl|
|
58
|
+
nl.Notification(:SequenceNumber => 1, :MessageType => :SMS, :Format => :UTF8) do |n|
|
59
|
+
n.Message do |m|
|
60
|
+
m.cdata!(message)
|
61
|
+
end
|
64
62
|
n.Profile(Mblox.config.profile_id)
|
65
|
-
n.SenderID(Mblox.config.sender_id, :Type =>
|
63
|
+
n.SenderID(Mblox.config.sender_id, :Type => :Shortcode)
|
66
64
|
n.Tariff(Mblox.config.tariff)
|
67
65
|
n.Subscriber do |s|
|
68
66
|
s.SubscriberNumber(@phone)
|
@@ -90,5 +88,20 @@ module Mblox
|
|
90
88
|
Mblox.log "Section ##{sections} of ##{sections} contains characters #{first_char + 1} thru #{message.size} of #{message.size}"
|
91
89
|
split_message << "(MSG #{sections}/#{sections}): #{message[first_char..-1]}"
|
92
90
|
end
|
91
|
+
|
92
|
+
class << self
|
93
|
+
def url
|
94
|
+
@url ||= URI.parse(URI.escape(Mblox.config.outbound_url))
|
95
|
+
end
|
96
|
+
def http
|
97
|
+
@http ||= Net::HTTP.new(url.host, url.port)
|
98
|
+
end
|
99
|
+
def request
|
100
|
+
return @request if @request
|
101
|
+
@request = Net::HTTP::Post.new(url.request_uri)
|
102
|
+
@request.content_type = 'text/xml'
|
103
|
+
@request
|
104
|
+
end
|
105
|
+
end
|
93
106
|
end
|
94
107
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'active_model/serialization'
|
2
|
+
require 'active_model/serializers/xml.rb'
|
3
|
+
|
4
|
+
require 'active_model/callbacks'
|
5
|
+
require 'active_model/validator'
|
6
|
+
require 'active_model/naming'
|
7
|
+
require 'active_model/translation'
|
8
|
+
require 'active_model/validations'
|
9
|
+
require 'active_model/errors'
|
10
|
+
|
11
|
+
module Mblox
|
12
|
+
class SmsResponse
|
13
|
+
class MissingExpectedXmlContentError < StandardError; end
|
14
|
+
class Result
|
15
|
+
include ActiveModel::Validations
|
16
|
+
validates_presence_of :text, :code, :message => "%{attribute} cannot be blank"
|
17
|
+
validates_numericality_of :code, :only_integer => true, :allow_nil => true, :message => "%{attribute} must be an integer"
|
18
|
+
|
19
|
+
attr_reader :code, :text
|
20
|
+
def initialize(code, text)
|
21
|
+
@code, @text = (code.to_i.to_s == code ? code.to_i : code), text
|
22
|
+
end
|
23
|
+
|
24
|
+
def is_ok?
|
25
|
+
0 == @code
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(rhs)
|
29
|
+
code == rhs.code && text == rhs.text
|
30
|
+
end
|
31
|
+
|
32
|
+
UNROUTABLE_TEXT = "MsipRejectCode=29 Number unroutable:2e Do not retry:2e"
|
33
|
+
UNROUTABLE = new(10, UNROUTABLE_TEXT)
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :request, :result, :subscriber_result
|
37
|
+
def initialize(xml)
|
38
|
+
data = Hash.from_xml(xml)
|
39
|
+
data = data['NotificationRequestResult']
|
40
|
+
raise MissingExpectedXmlContentError, "Xml should have contained a 'NotificationRequestResult' node, but was #{xml}" if data.blank?
|
41
|
+
header = data['NotificationResultHeader']
|
42
|
+
raise MissingExpectedXmlContentError, "Xml should have contained a 'NotificationRequestResult' -> 'NotificationResultHeader' node, but was #{xml}" if header.blank?
|
43
|
+
@request = Result.new(header['RequestResultCode'], header['RequestResultText'])
|
44
|
+
@request = nil unless @request.valid?
|
45
|
+
|
46
|
+
result_list = data['NotificationResultList']
|
47
|
+
raise MissingExpectedXmlContentError, "Xml should have contained a 'NotificationRequestResult' -> 'NotificationResultList' node, but was #{xml}" if result_list.blank?
|
48
|
+
result_list = result_list['NotificationResult']
|
49
|
+
raise MissingExpectedXmlContentError, "Xml should have contained a 'NotificationRequestResult' -> 'NotificationResultList' => 'NotificationResult' node, but was #{xml}" if result_list.blank?
|
50
|
+
@result = Result.new(result_list['NotificationResultCode'], result_list['NotificationResultText'])
|
51
|
+
@result = nil unless @result.valid?
|
52
|
+
|
53
|
+
if @result.is_ok?
|
54
|
+
result_list = result_list['SubscriberResult']
|
55
|
+
raise MissingExpectedXmlContentError, "Xml should have contained a 'NotificationRequestResult' -> 'NotificationResultList' => 'NotificationResult' -> 'SubscriberResult' node, but was #{xml}" if result_list.blank?
|
56
|
+
@subscriber_result = Result.new(result_list['SubscriberResultCode'], result_list['SubscriberResultText'])
|
57
|
+
@subscriber_result = nil unless @subscriber_result.valid?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def is_ok?
|
62
|
+
@request.is_ok? && @result.is_ok? && @subscriber_result.is_ok?
|
63
|
+
end
|
64
|
+
|
65
|
+
def is_unroutable?
|
66
|
+
@request.is_ok? && @result.is_ok? && Result::UNROUTABLE == @subscriber_result
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/mblox/version.rb
CHANGED
data/mblox.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "rspec"
|
24
24
|
|
25
|
+
spec.add_runtime_dependency 'activemodel'
|
25
26
|
spec.add_runtime_dependency 'activesupport'
|
26
27
|
spec.add_runtime_dependency 'addressable'
|
27
28
|
spec.add_runtime_dependency 'builder'
|
data/spec/configuration_spec.rb
CHANGED
@@ -1,27 +1,59 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe
|
4
|
-
describe "
|
5
|
-
|
3
|
+
describe Mblox::Configuration do
|
4
|
+
describe "logger" do
|
5
|
+
before(:each) do
|
6
6
|
Mblox.reset_configuration
|
7
|
-
expect(Mblox.config.on_message_too_long).to eq(:raise_error)
|
8
7
|
end
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
after(:all) do
|
10
|
+
set_configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
[:fatal, :error, :warn, :info, :debug].each do |val|
|
14
|
+
it "should allow log level ':#{val}'" do
|
15
|
+
Mblox.config.log_at val
|
16
|
+
Mblox.config.logger = ::Logger.new('/dev/null')
|
17
|
+
expect { Mblox.log "Some info" }.to_not raise_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should default to log level debug" do
|
22
|
+
Mblox.config.log_level.should eq(:debug)
|
23
|
+
expect { Mblox.log "Some debug info" }.to_not raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not allow log level news when the logger is created after log level is set" do
|
27
|
+
Mblox.config.log_at :news
|
28
|
+
expect { Mblox.config.logger = ::Logger.new(STDOUT)}.to raise_error(ArgumentError, "Mblox log level must be set to :fatal, :error, :warn, :info or :debug")
|
29
|
+
expect { Mblox.log "Some news" }.to_not raise_error
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should not allow log level news when the logger is created before log level is set and should remain in a valid state" do
|
33
|
+
Mblox.config.logger = ::Logger.new("/dev/null")
|
34
|
+
expect { Mblox.config.log_at :news }.to raise_error(ArgumentError, "Mblox log level must be set to :fatal, :error, :warn, :info or :debug")
|
35
|
+
Mblox.config.log_level.should eq(:debug)
|
36
|
+
expect { Mblox.log "Some news" }.to_not raise_error
|
12
37
|
end
|
38
|
+
end
|
13
39
|
|
14
|
-
|
15
|
-
|
40
|
+
describe "on_message_too_long" do
|
41
|
+
it "should default to :raise_error" do
|
42
|
+
Mblox.reset_configuration
|
43
|
+
Mblox.config.on_message_too_long.should eq(:raise_error)
|
16
44
|
end
|
17
45
|
|
18
|
-
|
19
|
-
|
46
|
+
[:raise_error, :split, :truncate].each do |val|
|
47
|
+
it "should allow the value ':#{val}'" do
|
48
|
+
expect { Mblox.config.on_message_too_long = val }.to_not raise_error
|
49
|
+
Mblox.config.on_message_too_long.should eq(val)
|
50
|
+
end
|
20
51
|
end
|
21
52
|
|
22
53
|
it "should not allow other values and should remain in a valid state" do
|
54
|
+
original = Mblox.config.on_message_too_long
|
23
55
|
expect { Mblox.config.on_message_too_long = :do_nothing }.to raise_error(ArgumentError, "Mblox.config.on_message_too_long must be either :truncate, :split or :raise_error")
|
24
|
-
|
56
|
+
Mblox.config.on_message_too_long.should eq(original)
|
25
57
|
end
|
26
58
|
end
|
27
59
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mblox::SmsResponse::Result do
|
4
|
+
describe "code" do
|
5
|
+
it "cannot be blank" do
|
6
|
+
result = described_class.new(nil, "123")
|
7
|
+
result.valid?
|
8
|
+
result.errors[:code].should include("Code cannot be blank")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "must be a number" do
|
12
|
+
result = described_class.new('abc', "123")
|
13
|
+
result.valid?
|
14
|
+
result.errors[:code].should include("Code must be an integer")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "must be an integer" do
|
18
|
+
result = described_class.new(3.14159, "123")
|
19
|
+
result.valid?
|
20
|
+
result.errors[:code].should include("Code must be an integer")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "text" do
|
25
|
+
it "cannot be blank" do
|
26
|
+
result = described_class.new(0, '')
|
27
|
+
result.valid?
|
28
|
+
result.errors[:text].should include("Text cannot be blank")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "is_ok?" do
|
33
|
+
it "is true for code 0" do
|
34
|
+
described_class.new(0, "123").is_ok?.should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
10.times do |i|
|
38
|
+
it "is false for code #{i+1}" do
|
39
|
+
described_class.new(i+1, "123").is_ok?.should be_false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "==" do
|
45
|
+
it "should be true if code and text are the same" do
|
46
|
+
lhs = described_class.new(0, 'OK')
|
47
|
+
rhs = described_class.new(0, 'OK')
|
48
|
+
(lhs == rhs).should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should be false if code does not match" do
|
52
|
+
lhs = described_class.new(0, 'OK')
|
53
|
+
rhs = described_class.new(4, 'OK')
|
54
|
+
(lhs == rhs).should be_false
|
55
|
+
end
|
56
|
+
it "should be false if text does not match" do
|
57
|
+
lhs = described_class.new(0, 'OK')
|
58
|
+
rhs = described_class.new(0, '__OK__')
|
59
|
+
(lhs == rhs).should be_false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/sms_spec.rb
CHANGED
@@ -1,91 +1,137 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require "spec_helper"
|
2
3
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
6
|
-
expect { Mblox::Sms.new("2"*10, the_message) }.to_not raise_error
|
7
|
-
expect { Mblox::Sms.new("2"*11, the_message) }.to raise_error(Mblox::SmsError, "Phone number must be ten digits")
|
4
|
+
describe Mblox::Sms do
|
5
|
+
def the_message
|
6
|
+
"Mblox gem test sent at #{Time.now}"
|
8
7
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
expect { Mblox::Sms.new("0"+"2"*9, the_message) }.to raise_error(Mblox::SmsError, "Phone number cannot begin with 0 or 1")
|
8
|
+
before(:all) do
|
9
|
+
Mblox.reset_configuration
|
10
|
+
set_configuration
|
13
11
|
end
|
12
|
+
describe "phone number" do
|
13
|
+
it "should be 10 digits" do
|
14
|
+
expect { Mblox::Sms.new("2"*9, the_message) }.to raise_error(Mblox::Sms::InvalidPhoneNumberError, "Phone number must be ten digits")
|
15
|
+
expect { Mblox::Sms.new("2"*10, the_message) }.to_not raise_error
|
16
|
+
expect { Mblox::Sms.new("2"*11, the_message) }.to raise_error(Mblox::Sms::InvalidPhoneNumberError, "Phone number must be ten digits")
|
17
|
+
end
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
expect(mblox.phone).to eq("1#{TEST_NUMBER}")
|
20
|
-
end
|
21
|
-
end
|
19
|
+
it "should not start with 0 or 1" do
|
20
|
+
expect { Mblox::Sms.new("1"+"2"*9, the_message) }.to raise_error(Mblox::Sms::InvalidPhoneNumberError, "Phone number cannot begin with 0 or 1")
|
21
|
+
expect { Mblox::Sms.new("0"+"2"*9, the_message) }.to raise_error(Mblox::Sms::InvalidPhoneNumberError, "Phone number cannot begin with 0 or 1")
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
it "should be safe from changing" do
|
25
|
+
number = TEST_NUMBER.to_s
|
26
|
+
mblox = Mblox::Sms.new(number,the_message)
|
27
|
+
number[1..3] = ''
|
28
|
+
mblox.phone.should eq("1#{TEST_NUMBER}")
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
describe "message" do
|
33
|
+
it "cannot be blank" do
|
34
|
+
expect { Mblox::Sms.new(LANDLINE, "") }.to raise_error(Mblox::Sms::InvalidMessageError, "Message cannot be blank")
|
35
|
+
end
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
expect { @mblox = Mblox::Sms.new("2"*10, message) }.to_not raise_error
|
36
|
-
expect(@mblox.message).to eq([message[0,160]])
|
37
|
-
end
|
37
|
+
it "can be 160 characters long" do
|
38
|
+
expect { Mblox::Sms.new(LANDLINE, "A"*160) }.to_not raise_error
|
39
|
+
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
it "will be truncated when the message is longer than 160 characters if configured to do so" do
|
42
|
+
message = "A"+"ABCDEFGHIJ"*16
|
43
|
+
Mblox.config.on_message_too_long = :truncate
|
44
|
+
expect { @mblox = Mblox::Sms.new(LANDLINE, message) }.to_not raise_error
|
45
|
+
@mblox.message.should eq([message[0,160]])
|
46
|
+
end
|
43
47
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
expect(@mblox.message).to eq(["(MSG 1/4): #{message[0,145]}", "(MSG 2/4): #{message[145,145]}", "(MSG 3/4): #{message[290,145]}", "(MSG 4/4): #{message[435,145]}"])
|
49
|
-
expect(@mblox.send).to eq(Array.new(4, result_unroutable))
|
50
|
-
end
|
48
|
+
it "cannot be longer than 160 characters if configured to raise error" do
|
49
|
+
Mblox.config.on_message_too_long = :raise_error
|
50
|
+
expect { Mblox::Sms.new(LANDLINE, "A"*161) }.to raise_error(Mblox::Sms::InvalidMessageError, "Message cannot be longer than 160 characters")
|
51
|
+
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
53
|
+
it "should be split into multiple messages when longer than 160 characters if configured to split and even split" do
|
54
|
+
message = "ABCDEFGHIJ"*58
|
55
|
+
Mblox.config.on_message_too_long = :split
|
56
|
+
expect { @mblox = Mblox::Sms.new(LANDLINE, message) }.to_not raise_error
|
57
|
+
@mblox.message.should eq(["(MSG 1/4): #{message[0,145]}", "(MSG 2/4): #{message[145,145]}", "(MSG 3/4): #{message[290,145]}", "(MSG 4/4): #{message[435,145]}"])
|
58
|
+
response = @mblox.send
|
59
|
+
response.count.should eq(4)
|
60
|
+
response.each { |r| r.is_unroutable?.should be_true }
|
61
|
+
end
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
it "should be split into multiple messages when longer than 160 characters if configured to split and not even split" do
|
64
|
+
message = "ABCDEFGHIJ"*32
|
65
|
+
Mblox.config.on_message_too_long = :split
|
66
|
+
expect { @mblox = Mblox::Sms.new(LANDLINE, message) }.to_not raise_error
|
67
|
+
@mblox.message.should eq(["(MSG 1/3): #{message[0,145]}", "(MSG 2/3): #{message[145,145]}", "(MSG 3/3): #{message[290..-1]}"])
|
68
|
+
response = @mblox.send
|
69
|
+
response.count.should eq(3)
|
70
|
+
response.each { |r| r.is_unroutable?.should be_true }
|
71
|
+
end
|
66
72
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
73
|
+
it "should be safe from changing when short" do
|
74
|
+
msg = the_message
|
75
|
+
mblox = Mblox::Sms.new(TEST_NUMBER,msg)
|
76
|
+
msg[1..3] = ''
|
77
|
+
mblox.message.should eq([the_message])
|
78
|
+
end
|
74
79
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
80
|
+
it "should be safe from changing when long when configured to split" do
|
81
|
+
Mblox.config.on_message_too_long = :split
|
82
|
+
msg = the_message * 10
|
83
|
+
mblox = Mblox::Sms.new(TEST_NUMBER,msg)
|
84
|
+
msg[1..3] = ''
|
85
|
+
mblox.message[0][11, 20].should eq(the_message[0,20])
|
86
|
+
end
|
79
87
|
|
80
|
-
|
81
|
-
|
88
|
+
it "should be safe from changing when long when configured to truncate" do
|
89
|
+
Mblox.config.on_message_too_long = :truncate
|
90
|
+
msg = the_message * 10
|
91
|
+
mblox = Mblox::Sms.new(TEST_NUMBER,msg)
|
92
|
+
msg[1..3] = ''
|
93
|
+
mblox.message[0][0, 20].should eq(the_message[0,20])
|
94
|
+
end
|
82
95
|
end
|
83
96
|
|
84
|
-
|
85
|
-
|
86
|
-
|
97
|
+
describe "SMS messages" do
|
98
|
+
def expect_ok_response(response)
|
99
|
+
response.is_ok?.should be_true
|
100
|
+
response.is_unroutable?.should be_false
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should be sent when the phone number is a Fixnum" do
|
104
|
+
response = Mblox::Sms.new(TEST_NUMBER.to_i,the_message).send
|
105
|
+
response.size.should eq(1)
|
106
|
+
expect_ok_response(response.first)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should be sent when the phone number is a String" do
|
110
|
+
response = Mblox::Sms.new(TEST_NUMBER.to_s,the_message).send
|
111
|
+
response.size.should eq(1)
|
112
|
+
expect_ok_response(response.first)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should allow 160-character messages" do
|
116
|
+
response = Mblox::Sms.new(TEST_NUMBER,"A"*160).send
|
117
|
+
response.size.should eq(1)
|
118
|
+
expect_ok_response(response.first)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should be unroutable when sent to a landline" do
|
122
|
+
response = Mblox::Sms.new(LANDLINE,the_message).send
|
123
|
+
response.size.should eq(1)
|
124
|
+
response.first.is_unroutable?.should be_true, "#{response.first.inspect} should have been unroutable"
|
125
|
+
response.first.is_ok?.should be_false
|
126
|
+
end
|
87
127
|
|
88
|
-
|
89
|
-
|
128
|
+
"\r\n!\"#$\%&'\(\)*+,-.\/:;<=>?@_£¤¥§¿iÄÅÆÇÉÑÖØÜßáäåæèéìñòöøùü\tí¡ ".each_char do |i|
|
129
|
+
it "allows the special char #{i}, correctly escaping illegal XML characters where necessary" do
|
130
|
+
response = Mblox::Sms.new(LANDLINE,"#{the_message}#{i}#{the_message}").send
|
131
|
+
response.size.should eq(1)
|
132
|
+
response.first.is_ok?.should be_false
|
133
|
+
response.first.is_unroutable?.should be_true
|
134
|
+
end
|
135
|
+
end
|
90
136
|
end
|
91
137
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -15,21 +15,8 @@ def set_configuration
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
set_configuration
|
19
|
-
|
20
18
|
TEST_NUMBER = CONFIG['test_number']
|
21
|
-
|
22
|
-
def the_message
|
23
|
-
"Mblox gem test sent at #{Time.now}"
|
24
|
-
end
|
25
|
-
|
26
|
-
def result_ok
|
27
|
-
"RequestResult: \"0:OK\" / SubscriberResult: \"0:OK\""
|
28
|
-
end
|
29
|
-
|
30
|
-
def result_unroutable
|
31
|
-
"RequestResult: \"0:OK\" / SubscriberResult: \"10:MsipRejectCode=29 Number unroutable:2e Do not retry:2e\""
|
32
|
-
end
|
19
|
+
LANDLINE = 6176354500
|
33
20
|
|
34
21
|
module Mblox
|
35
22
|
class << self
|
@@ -38,3 +25,7 @@ module Mblox
|
|
38
25
|
end
|
39
26
|
end
|
40
27
|
end
|
28
|
+
|
29
|
+
RSpec.configure do |config|
|
30
|
+
config.order = "random"
|
31
|
+
end
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mblox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Isaac Betesh
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-10-09 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,7 +27,6 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ! '>='
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,7 +34,6 @@ dependencies:
|
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ! '>='
|
44
39
|
- !ruby/object:Gem::Version
|
@@ -46,7 +41,6 @@ dependencies:
|
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rspec
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ! '>='
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,20 @@ dependencies:
|
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activemodel
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
65
|
requirements:
|
59
66
|
- - ! '>='
|
60
67
|
- !ruby/object:Gem::Version
|
@@ -62,7 +69,6 @@ dependencies:
|
|
62
69
|
- !ruby/object:Gem::Dependency
|
63
70
|
name: activesupport
|
64
71
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
72
|
requirements:
|
67
73
|
- - ! '>='
|
68
74
|
- !ruby/object:Gem::Version
|
@@ -70,7 +76,6 @@ dependencies:
|
|
70
76
|
type: :runtime
|
71
77
|
prerelease: false
|
72
78
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
79
|
requirements:
|
75
80
|
- - ! '>='
|
76
81
|
- !ruby/object:Gem::Version
|
@@ -78,7 +83,6 @@ dependencies:
|
|
78
83
|
- !ruby/object:Gem::Dependency
|
79
84
|
name: addressable
|
80
85
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
86
|
requirements:
|
83
87
|
- - ! '>='
|
84
88
|
- !ruby/object:Gem::Version
|
@@ -86,7 +90,6 @@ dependencies:
|
|
86
90
|
type: :runtime
|
87
91
|
prerelease: false
|
88
92
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
93
|
requirements:
|
91
94
|
- - ! '>='
|
92
95
|
- !ruby/object:Gem::Version
|
@@ -94,7 +97,6 @@ dependencies:
|
|
94
97
|
- !ruby/object:Gem::Dependency
|
95
98
|
name: builder
|
96
99
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
100
|
requirements:
|
99
101
|
- - ! '>='
|
100
102
|
- !ruby/object:Gem::Version
|
@@ -102,7 +104,6 @@ dependencies:
|
|
102
104
|
type: :runtime
|
103
105
|
prerelease: false
|
104
106
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
107
|
requirements:
|
107
108
|
- - ! '>='
|
108
109
|
- !ruby/object:Gem::Version
|
@@ -124,43 +125,36 @@ files:
|
|
124
125
|
- lib/mblox.rb
|
125
126
|
- lib/mblox/configuration.rb
|
126
127
|
- lib/mblox/sms.rb
|
127
|
-
- lib/mblox/
|
128
|
+
- lib/mblox/sms_response.rb
|
128
129
|
- lib/mblox/version.rb
|
129
130
|
- mblox.gemspec
|
130
131
|
- spec/configuration_spec.rb
|
131
|
-
- spec/
|
132
|
+
- spec/sms_response_result_spec.rb
|
132
133
|
- spec/sms_spec.rb
|
133
134
|
- spec/spec_helper.rb
|
134
135
|
homepage: ''
|
135
136
|
licenses:
|
136
137
|
- MIT
|
138
|
+
metadata: {}
|
137
139
|
post_install_message:
|
138
140
|
rdoc_options: []
|
139
141
|
require_paths:
|
140
142
|
- lib
|
141
143
|
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
-
none: false
|
143
144
|
requirements:
|
144
145
|
- - ! '>='
|
145
146
|
- !ruby/object:Gem::Version
|
146
147
|
version: '0'
|
147
|
-
segments:
|
148
|
-
- 0
|
149
|
-
hash: 965711471
|
150
148
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
-
none: false
|
152
149
|
requirements:
|
153
150
|
- - ! '>='
|
154
151
|
- !ruby/object:Gem::Version
|
155
152
|
version: '0'
|
156
|
-
segments:
|
157
|
-
- 0
|
158
|
-
hash: 965711471
|
159
153
|
requirements: []
|
160
154
|
rubyforge_project:
|
161
|
-
rubygems_version: 1.
|
155
|
+
rubygems_version: 2.1.5
|
162
156
|
signing_key:
|
163
|
-
specification_version:
|
157
|
+
specification_version: 4
|
164
158
|
summary: ! '# Mblox This gem is for subscribers to Mblox to send SMS messages. ##
|
165
159
|
Installation Add this line to your application''s Gemfile: gem ''mblox'' And
|
166
160
|
then execute: $ bundle Or install it yourself as: $ gem install mblox ## Usage Configuration Mblox.configure
|
@@ -186,6 +180,6 @@ summary: ! '# Mblox This gem is for subscribers to Mblox to send SMS messages.
|
|
186
180
|
5. Create new Pull Request'
|
187
181
|
test_files:
|
188
182
|
- spec/configuration_spec.rb
|
189
|
-
- spec/
|
183
|
+
- spec/sms_response_result_spec.rb
|
190
184
|
- spec/sms_spec.rb
|
191
185
|
- spec/spec_helper.rb
|
data/lib/mblox/sms_error.rb
DELETED
data/spec/log_spec.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "logger"
|
3
|
-
|
4
|
-
describe "logger" do
|
5
|
-
before(:each) do
|
6
|
-
Mblox.reset_configuration
|
7
|
-
end
|
8
|
-
|
9
|
-
after(:all) do
|
10
|
-
set_configuration
|
11
|
-
end
|
12
|
-
|
13
|
-
it "should allow log level info" do
|
14
|
-
Mblox.config.log_at :info
|
15
|
-
Mblox.config.logger = ::Logger.new('/dev/null')
|
16
|
-
expect { Mblox.log "Some info" }.to_not raise_error
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should default to log level debug" do
|
20
|
-
expect(Mblox.config.log_level).to eq(:debug)
|
21
|
-
expect { Mblox.log "Some debug info" }.to_not raise_error
|
22
|
-
end
|
23
|
-
|
24
|
-
it "should not allow log level news when the logger is created after log level is set" do
|
25
|
-
Mblox.config.log_at :news
|
26
|
-
expect { Mblox.config.logger = ::Logger.new(STDOUT)}.to raise_error(ArgumentError, "Mblox log level must be set to :fatal, :error, :warn, :info or :debug")
|
27
|
-
expect { Mblox.log "Some news" }.to_not raise_error
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should not allow log level news when the logger is created before log level is set and should remain in a valid state" do
|
31
|
-
Mblox.config.logger = ::Logger.new("/dev/null")
|
32
|
-
expect { Mblox.config.log_at :news }.to raise_error(ArgumentError, "Mblox log level must be set to :fatal, :error, :warn, :info or :debug")
|
33
|
-
expect { Mblox.log "Some news" }.to_not raise_error
|
34
|
-
end
|
35
|
-
end
|